ohmu
·
2026-10-15
1#include "common/binary.h"
2#include "common/debug.h"
3#include "common/io.h"
4#include "common/strutil.h"
5#include "common/windows/nt.h"
6
7#include <windows.h>
8#include <stdlib.h>
9#include <string.h>
10
11/**
12 * Extracts string literals from a PE (Windows) executable
13 *
14 * This function maps the PE file into memory, locates the .rdata section
15 * (which typically contains string literals), and scans it for valid strings.
16 * Each string found is passed to the callback function for processing.
17 */
18const char* ffBinaryExtractStrings(const char* peFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
19 FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileA(peFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
20 if (hFile == INVALID_HANDLE_VALUE) {
21 FF_DEBUG("CreateFileA() failed: %s", ffDebugWin32Error(GetLastError()));
22 return "CreateFileA() failed";
23 }
24
25 FF_AUTO_CLOSE_FD HANDLE hSection = nullptr;
26 NTSTATUS status = NtCreateSection(&hSection, SECTION_MAP_READ, nullptr, nullptr, PAGE_READONLY, SEC_COMMIT, hFile);
27 if (!NT_SUCCESS(status)) {
28 FF_DEBUG("NtCreateSection() failed: %s", ffDebugNtStatus(status));
29 return "NtCreateSection() failed";
30 }
31
32 PVOID base = nullptr;
33 SIZE_T viewSize = 0;
34 status = NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, nullptr, &viewSize, ViewUnmap, 0, PAGE_READONLY);
35 if (!NT_SUCCESS(status)) {
36 FF_DEBUG("NtMapViewOfSection() failed: %s", ffDebugNtStatus(status));
37 return "NtMapViewOfSection() failed";
38 }
39
40 PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(base);
41 if (!ntHeaders) {
42 FF_DEBUG("RtlImageNtHeader() failed");
43 NtUnmapViewOfSection(NtCurrentProcess(), base);
44 return "RtlImageNtHeader() failed";
45 }
46
47 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
48 for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i, ++section) {
49 // Look for initialized data sections with the name ".rdata" which typically contains string literals
50 if ((section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) && ffStrEquals((const char*) section->Name, ".rdata")) {
51 uint8_t* data = (uint8_t*) base + section->PointerToRawData;
52
53 // Scan the section for string literals
54 for (size_t off = 0; off < section->SizeOfRawData; ++off) {
55 const char* p = (const char*) data + off;
56 if (*p == '\0') {
57 continue;
58 }
59 uint32_t len = (uint32_t) strnlen(p, section->SizeOfRawData - off);
60 if (len < minLength) {
61 off += len;
62 continue;
63 }
64 // Only process printable ASCII characters
65 if (*p >= ' ' && *p <= '~') // Ignore control characters
66 {
67 if (!cb(p, len, userdata)) {
68 break;
69 }
70 }
71 off += len;
72 }
73 }
74 }
75
76 NtUnmapViewOfSection(NtCurrentProcess(), base);
77 return nullptr;
78}