diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml index a44b3ff..0c897ca 100644 --- a/.github/workflows/make.yml +++ b/.github/workflows/make.yml @@ -14,7 +14,7 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" - build: + linux-build: # The type of runner that the job will run on runs-on: ubuntu-latest @@ -29,7 +29,7 @@ jobs: id: version with: scheme: semver - increment: patch + increment: minor - name: Build run: echo "char *version = \"${{ steps.version.outputs.version }}\";" > headers/version.h && make && ls @@ -38,6 +38,42 @@ jobs: uses: ncipollo/release-action@v1 with: artifacts: "SHA1check" + replacesArtifacts: false + allowUpdates: true + generateReleaseNotes: true + name: "Release v${{ steps.version.outputs.version }}" + tag: "v${{ steps.version.outputs.version }}" + + windows-build: + # The type of runner that the job will run on + runs-on: windows-2022 + + defaults: + run: + shell: bash + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - name: Checkout + uses: actions/checkout@v3 + + - name: Increment version + uses: reecetech/version-increment@2023.4.1 + id: version + with: + scheme: semver + increment: minor + + - name: Build + run: echo "char *version = \"${{ steps.version.outputs.version }}\";" > headers/version.h && make && ls + + - name: Create Release + uses: ncipollo/release-action@v1 + with: + artifacts: "SHA1check.exe" + replacesArtifacts: false + allowUpdates: true generateReleaseNotes: true name: "Release v${{ steps.version.outputs.version }}" tag: "v${{ steps.version.outputs.version }}" diff --git a/args.c b/args.c index 16bffcc..304438b 100644 --- a/args.c +++ b/args.c @@ -6,6 +6,12 @@ #include "paths.h" #include "version.h" +#ifdef _WIN32 +#define win 1 +#else +#define win 0 +#endif + char *help_string = "usage: %s [-h] [-s SOURCE] [-d DESTINATION] path\n\ \n\ @@ -100,8 +106,13 @@ void parse_args(int argc, char **argv, char *src, else directory = argv[optind]; } - // realpath(directory, dir); // uncomment for testing - strcpy(dir, directory); // uncomment for production + // for windows, removes " at the end of the path and changes all backslashes + if (win) { + int len = strlen(directory); + if (directory[len - 1] == '\"') directory[len-1] = 0; + } + + strcpy(dir, directory); if (source[0] == ':') strcpy(src, source + 1); else { diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..db95a92 --- /dev/null +++ b/build.bat @@ -0,0 +1,9 @@ +@echo off +for %%i in (*.c) do ( + echo Compiling %%i + gcc -c -I headers %%i -o %%~ni.o + echo. +) +gcc *.o -o SHA1check.exe +del *.o +pause diff --git a/compare.c b/compare.c index 9d726b1..990558e 100644 --- a/compare.c +++ b/compare.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -8,27 +7,66 @@ #include "files.h" #include "hashing.h" #include "paths.h" -#define LINELEN 128 - -regex_t reghex1; -regex_t reghex2; - -// init regex sequences -void regex_init() { - int regh1 = regcomp(®hex1, "[0-9a-fA-F]{40}", REG_EXTENDED); - int regh2 = regcomp(®hex2, "[0-9a-fA-F]{10}\\*\\*\\*[0-9a-fA-F]{10}", REG_EXTENDED); - if (regh1 | regh2) { - printf("Something went wrong while initialising pattern matcher\n"); - exit(-2); + +int is_hex(char c) { + return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f'); +} + +// homemade regex +int matcher(char *str, int ver, int *range) { + int len = strlen(str); + if (ver == 1) { + for (int i = 0; i < len; i++) { + int j = i; + for (; j < len; j++) { + if (!is_hex(str[j])) break; + } + + if (j - i == 40) { + if (range) { + range[0] = i; + range[1] = j; + } + return 0; + } + } + } else { + for (int i = 0; i < len; i++) { + int j = 0; + for (; i + j < len && j < 10; j++) { + if (!is_hex(str[i + j])) break; + } + + if (j != 10) continue; + + for (; i + j < len && j < 13; j++) { + if (str[i + j] != '*') break; + } + + if (j != 13) continue; + + for (; i + j < len && j < 23; j++) { + if (!is_hex(str[i + j])) break; + } + + if (j != 23) continue; + if (range) { + range[0] = i; + range[1] = i + 23; + } + + return 0; + } } + return 1; } // if full hash given, return 1 // if partial hash given in the form of reghex2, returns 2 // if unknown, return 0 int get_hash_ver(char *line) { - if (!regexec(®hex1, line, 0, NULL, 0)) return 1; - if (!regexec(®hex2, line, 0, NULL, 0)) return 2; + if (!matcher(line, 1, NULL)) return 1; + if (!matcher(line, 2, NULL)) return 2; return 0; } @@ -51,26 +89,23 @@ void obf_hash(char *line, char *result) { // 0 if hashsum equal and positive if strcmp fails int compare(char *dir, char *line, int ver) { int limit; - regex_t *reg; - regmatch_t pmatch[1]; if (ver == 1) { limit = 40; - reg = ®hex1; } else { limit = 23; - reg = ®hex2; } + int range[2] = {0}; // find hexstring and copy to hex, return -1 if no matches - int match = regexec(reg, line, 1, pmatch, 0); + int match = matcher(line, ver, range); if (match) return -1; - char hex[41] = {0}; - strncpy(hex, line + pmatch[0].rm_so, limit); + char hex[limit + 1]; + strncpy(hex, line + range[0], limit); // trim line to find file - trim(line, pmatch[0].rm_so); + trim(line, range[0]); // copy to path and concat char path[PATH_MAX]; @@ -91,7 +126,7 @@ int compare(char *dir, char *line, int ver) { result = (long) strcmp(hash_value, hex); } else { long size; - sscanf(line + pmatch[0].rm_eo, "%ld", &size); + sscanf(line + range[1], "%ld", &size); char temp[24] = {0}; obf_hash(hash_value, temp); @@ -105,9 +140,3 @@ int compare(char *dir, char *line, int ver) { free(hash_value); return !!result; } - -// frees regex allocs -void free_regex() { - regfree(®hex1); - regfree(®hex2); -} diff --git a/getdent.c b/getdent.c index a7692e9..d2951b9 100644 --- a/getdent.c +++ b/getdent.c @@ -1,14 +1,14 @@ -#include -#include -#include #include #include #include + +#if defined __unix__ +#include +#include #include #include #include -#ifdef __unix__ #define BUF_SIZE 1024 #define define_func void getdent(char *name) { \ int fd, nread; \ @@ -55,11 +55,36 @@ error: exit(-4); \ } -#endif +#elif defined _WIN32 +#include -#ifdef __APPLE__ +#define define_func void getdent(const char *folder) { \ + char path[MAX_PATH]; \ + sprintf(path, "%s/*", folder); \ + WIN32_FIND_DATA fd; \ + HANDLE handle = FindFirstFile(path, &fd); \ + if(handle == INVALID_HANDLE_VALUE) return; \ + do { \ + if(!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) continue; \ + sprintf(path, "%s/%s", folder, fd.cFileName); \ + \ + if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) getdent(path); \ + else { \ + if (!strcmp(path, src) || !strcmp(path, dst)) continue; \ + if (path_exists(pass, get_relative_path(dir, path)) \ + || path_exists(fail, get_relative_path(dir, path))) \ + continue; \ + \ + add_path_to_dir(get_relative_path(dir, path), extras); \ + (*count)++; \ + } \ + } while(FindNextFile(handle, &fd)); \ + FindClose(handle); \ +} + +#else +#include -#define getdent looper #define define_func int loop_files(const char *path, const struct stat *sb, \ int type, struct FTW* buf) { \ if (type != FTW_F) return 0; \ @@ -72,7 +97,7 @@ error: return 0; \ } \ \ -void looper(char *name) { \ +void getdent(char *name) { \ nftw(name, &loop_files, 1, FTW_PHYS); \ } diff --git a/hashing.c b/hashing.c index 09c1817..d98161c 100644 --- a/hashing.c +++ b/hashing.c @@ -1,40 +1,40 @@ #include #include + +#ifndef _WIN32 #include +#define hash_init(ctx, stat) \ + *(ctx) = EVP_MD_CTX_new(); \ + stat = !EVP_DigestInit(*(ctx), EVP_sha1()) +#define hash_append(ctx, buffer, read_size) \ + EVP_DigestUpdate((ctx), (buffer), (read_size)); +#define end_hash(ctx, digest) EVP_DigestFinal(ctx, digest, NULL); free(ctx); + +#else +#include +#include +#include +#define hash_init(ctx, stat) \ + HCRYPTPROV context; \ + CryptAcquireContext(&context, NULL, NULL, PROV_RSA_FULL, \ + CRYPT_VERIFYCONTEXT); \ + stat = !CryptCreateHash(context, CALG_SHA, 0, 0, (HCRYPTHASH *) (ctx)) +#define hash_append(ctx, buffer, read_size) \ + CryptHashData((HCRYPTHASH) (ctx), (buffer), (read_size), 0) +#define end_hash(ctx, digest) \ + unsigned long d_size = 21; \ + CryptGetHashParam((HCRYPTHASH) (ctx), HP_HASHVAL, (digest), &d_size, 0); \ + CryptDestroyHash((HCRYPTHASH) (ctx)); \ + CryptReleaseContext((HCRYPTHASH) (ctx), 0) +#endif #include "datatypes.h" #define CHUNK 131072 -char byte_to_hex(char b) { - switch (b) { - case 10: - return 'a'; - case 11: - return 'b'; - case 12: - return 'c'; - case 13: - return 'd'; - case 14: - return 'e'; - case 15: - return 'f'; - default: - return b + '0'; - } -} - void hexdigest(unsigned char *digest, char *result) { // loop through all chars of digest for (int i = 0; i < 20; i++) { - unsigned char c = digest[i]; - - // mask the top 4 and bottom 4 bits and - // convert to hex - char top = (c & 0xf0) >> 4; - char bot = c & 0x0f; - result[2 * i] = byte_to_hex(top); - result[2 * i + 1] = byte_to_hex(bot); + snprintf(result + 2 * i, 3, "%02x", digest[i]); } result[40] = '\0'; @@ -45,28 +45,29 @@ void hexdigest(unsigned char *digest, char *result) { char *hash(char *path, size_t size) { // SHA struct init unsigned char digest[20]; - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - if (!EVP_DigestInit(ctx, EVP_sha1())) { + void *ctx; + int stat; + hash_init(&ctx, stat); + if (stat) { printf("There was a problem initialising the hasher"); return NULL; } // try to open file - FILE *f = fopen(path, "r"); + FILE *f = fopen(path, "rb"); if (!f) return NULL; // loops through the whole file unsigned char buffer[CHUNK]; for (int i = 0; i < size; i += CHUNK) { - size_t size = fread(buffer, 1, CHUNK, f); - EVP_DigestUpdate(ctx, buffer, size); + size_t read_size = fread(buffer, 1, CHUNK, f); + hash_append(ctx, buffer, read_size); } fclose(f); // retrieve digest and free - EVP_DigestFinal(ctx, digest, NULL); - free(ctx); + end_hash(ctx, digest); // parse to hexadecimal char *result = malloc(41 * sizeof(char)); diff --git a/headers/compare.h b/headers/compare.h index 41b1e4e..2762188 100644 --- a/headers/compare.h +++ b/headers/compare.h @@ -1,4 +1,2 @@ int compare(char *dir, char *line, int version); -void regex_init(); -void free_regex(); int get_hash_ver(char *line); diff --git a/main.c b/main.c index 12501ca..3013c19 100644 --- a/main.c +++ b/main.c @@ -20,7 +20,6 @@ void free_all() { if (missing) free_dir(missing); if (failed) free_dir(failed); if (extra) free_dir(extra); - free_regex(); } int main(int argc, char **argv) { @@ -42,8 +41,6 @@ int main(int argc, char **argv) { return -1; } - regex_init(); - FILE *hashfile = fopen(src, "r"); int version = 0; diff --git a/readme.md b/readme.md index eb12d09..8cc6d4d 100644 --- a/readme.md +++ b/readme.md @@ -7,15 +7,16 @@ Use `make` to build without debug symbols Use `make debug` to build with address sanitizer and debug symbols ## Usage: -`main FOLDERPATH [-s HASHFILENAME] [-d RESULTFILENAME]` +`SHA1check FOLDERPATH [-s HASHFILENAME] [-d RESULTFILENAME]` Supply the executable with the following: ### Positional (Required) 1. `FOLDERPATH` : The directory in which the files exist ### Options -1. `-h` : display this help message +1. `-h` : display this help message 1. `-s` : source file i.e. file containing hashes in "FILE HASHSUM" format 1. `-d` : destination file +1. `-v` : display current version >HASHPATH default: FOLDERPATH/hashes.sha1 > @@ -26,4 +27,4 @@ Supply the executable with the following: >FOLDERPATH always uses absolute path, there is no need for ':' ## Supported OS -The current source supports Linux and macOS, plans are in place to make it cross-platform (Mainly for Windows) \ No newline at end of file +The current source supports Linux and macOS, plans are in place to make it cross-platform (Mainly for Windows)