master ohmu/fastfetch-openbsd / src / common / impl / binary_windows.c
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}