含crypt函数的暴力破解算法随机出现段错误问题求助
Let's walk through the issues causing random crashes when targeting passwords longer than 4 characters, and fix them one by one:
Key Issues Causing Crashes
1. Gigantic, Leaked Memory Allocations
Your line f_password=(char*) malloc(1000000000*sizeof(char)); allocates 1 gigabyte of memory every single time you test a password combination. You never free this memory, so after just a few thousand attempts, your system runs out of RAM and crashes. This is completely unnecessary—password entries from shadow files are never that long.
2. File Handle Leaks & Misused File Pointers
- You open three files (
shadow_2016.txt,Users_found2.txt,encr_pass) every timebruteImplis called—and since this is a recursive function, that means hundreds or thousands of simultaneous open files. Most systems limit the number of open file handles per process (usually around 1024), so hitting this limit will trigger a crash. - Once you read through
shadow_2016.txtonce, the file pointer stays at the end of the file. All subsequentfscanfcalls returnEOFwithout processing any data, and you never reset the pointer or re-open the file correctly.
3. Unsafe strtok Usage
If your password file has any malformed lines (missing the : separator), strtok(NULL, ":") will return NULL. Calling strcpy(password, NULL) leads to a null pointer dereference, which is a guaranteed segmentation fault.
Fixed Code
Here's the revised version with all these issues addressed, plus some efficiency improvements:
#define _GNU_SOURCE #include <stdio.h> #include <time.h> #include <unistd.h> #include <crypt.h> #include <string.h> #include <stdlib.h> #include <sys/stat.h> static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; static const int alphabetSize = sizeof(alphabet) - 1; // Helper struct to store shadow file entries typedef struct { char username[100]; char password[100]; } ShadowEntry; // Load all shadow entries into memory once (far more efficient than re-reading) int load_shadow_entries(const char* filename, ShadowEntry** entries, int* count) { FILE* f = fopen(filename, "r"); if (!f) { perror("Failed to open shadow file"); return -1; } *count = 0; char line[1024]; // First count valid lines to allocate memory while (fgets(line, sizeof(line), f)) { if (strtok(line, ":") && strtok(NULL, ":")) { (*count)++; } } rewind(f); *entries = malloc(*count * sizeof(ShadowEntry)); if (!*entries) { perror("Failed to allocate memory for shadow entries"); fclose(f); return -1; } int idx = 0; while (fgets(line, sizeof(line), f)) { char* uname = strtok(line, ":"); char* pass = strtok(NULL, ":"); if (uname && pass) { strncpy((*entries)[idx].username, uname, sizeof((*entries)[idx].username)-1); strncpy((*entries)[idx].password, pass, sizeof((*entries)[idx].password)-1); idx++; } } fclose(f); return 0; } void bruteImpl(char* str, int index, int maxDepth, ShadowEntry* entries, int entry_count, FILE* out_log) { static int attempt_count = 0; static int found_count = 0; for (int i = 0; i < alphabetSize; ++i) { str[index] = alphabet[i]; str[index + 1] = '\0'; // Ensure string is null-terminated if (index == maxDepth - 1) { attempt_count++; printf("Attempt %d, testing: %s\n", attempt_count, str); // Check against all pre-loaded shadow entries for (int j = 0; j < entry_count; ++j) { char* encrypted_guess = crypt(str, entries[j].password); if (encrypted_guess && strcmp(encrypted_guess, entries[j].password) == 0) { fprintf(out_log, "Access Granted: User '%s' with password '%s' (Bruteforce)\n", entries[j].username, str); found_count++; printf("Found password for user %s!\n", entries[j].username); } } } else { bruteImpl(str, index + 1, maxDepth, entries, entry_count, out_log); } } // Print summary when finishing top-level recursion if (index == 0) { printf("Done! Found %d password(s). Check the log file for details.\n", found_count); } } void bruteSequential(int maxLen, ShadowEntry* entries, int entry_count, FILE* out_log) { char* buf = malloc(maxLen + 1); if (!buf) { perror("Failed to allocate buffer for brute-force"); return; } for (int i = 1; i <= maxLen; ++i) { memset(buf, 0, maxLen + 1); bruteImpl(buf, 0, i, entries, entry_count, out_log); } free(buf); } int main(void) { clock_t begin = clock(); int ans, length; ShadowEntry* shadow_entries = NULL; int entry_count = 0; printf("Press 1 for Bruteforce.\n"); printf("Press 2 for Exit.\n"); scanf("%d", &ans); switch(ans) { case 1: // Load shadow entries once at startup if (load_shadow_entries("shadow_2016.txt", &shadow_entries, &entry_count) != 0) { exit(EXIT_FAILURE); } if (entry_count == 0) { printf("No valid entries found in shadow file.\n"); free(shadow_entries); exit(EXIT_FAILURE); } // Open log file once (not every recursive call) FILE* out_log = fopen("Users_found2.txt", "a+"); if (!out_log) { perror("Failed to open log file"); free(shadow_entries); exit(EXIT_FAILURE); } printf("Enter the maximum length of the passwords to test:\n"); scanf("%d", &length); bruteSequential(length, shadow_entries, entry_count, out_log); fclose(out_log); free(shadow_entries); break; case 2: printf("Bye!\n"); exit(EXIT_SUCCESS); default: printf("Invalid choice!\n"); exit(EXIT_FAILURE); } clock_t end = clock(); double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; printf("Total process time: %.2f Seconds\n", time_spent); return 0; }
What Changed & Why
Memory Optimization:
- Removed the 1GB malloc; instead, we use a small fixed-size buffer for reading lines, and load all shadow entries into memory once (way more efficient than re-reading the file every time).
File Management:
- Open the shadow file once at the start, load all entries into memory, then close it.
- Open the log file once in
main()and pass the pointer tobruteImpl, avoiding hundreds of redundant open/close calls.
Safer String Handling:
- Use
strncpyinstead ofstrcpyto prevent buffer overflows. - Check that
strtokreturns valid pointers before using them. - Ensure the brute-force string is always null-terminated to avoid undefined behavior.
- Use
Recursion Cleanup:
- Removed unnecessary global variables; use static variables in
bruteImplfor counters to keep state without polluting the global namespace. - Added error checking for all memory allocations and file operations (critical for debugging unexpected issues).
- Removed unnecessary global variables; use static variables in
内容的提问来源于stack exchange,提问作者laland

