#ifndef _WINPE_H #define _WINPE_H #define WINPE_VERSION 370 #ifdef USECOMPAT #include "commdef_v100.h" #else #include "commdef.h" #endif // USECOMPAT // define specific macro #ifdef WINPE_API #undef WINPE_API #endif #ifdef WINPE_API_DEF #undef WINPE_API_DEF #endif #ifdef WINPE_API_EXPORT #undef WINPE_API_EXPORT #endif #ifdef WINPE_API_INLINE #undef WINPE_API_INLINE #endif #ifdef WINPE_STATIC #define WINPE_API_DEF static #else #define WINPE_API_DEF extern #endif // WINPE_STATIC #ifdef WINPE_SHARED #define WINPE_API_EXPORT EXPORT #else #define WINPE_API_EXPORT #endif // WINPE_SHARED #ifdef WINPE_NOINLINE #define WINPE_API_INLINE #else #define WINPE_API_INLINE INLINE #endif // WINPE_NOINLINE #define WINPE_API WINPE_API_DEF WINPE_API_EXPORT WINPE_API_INLINE // declear #ifdef __cplusplus extern "C" { #endif // __cplusplus #include #include #include typedef struct _RELOCOFFSET { WORD offset : 12; WORD type : 4; }RELOCOFFSET,*PRELOCOFFSET; typedef int bool_t; typedef HMODULE (WINAPI *PFN_LoadLibraryA)( LPCSTR lpLibFileName); typedef FARPROC (WINAPI *PFN_GetProcAddress)( HMODULE hModule, LPCSTR lpProcName); typedef PFN_GetProcAddress PFN_GetProcRVA; typedef LPVOID (WINAPI *PFN_VirtualAlloc)( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); typedef BOOL (WINAPI *PFN_VirtualFree)( LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); typedef BOOL (WINAPI *PFN_VirtualProtect)( LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); typedef SIZE_T (WINAPI *PFN_VirtualQuery)( LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength); typedef BOOL (WINAPI *PFN_DllMain)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved ); #define WINPE_LDFLAG_MEMALLOC 0x1 #define WINPE_LDFLAG_MEMFIND 0x2 /** * load the origin rawpe file in memory buffer by mem align * mempe means the pe in memory alignment * @param pmemsize mempe buffer size * @return mempe buf */ WINPE_API void* STDCALL winpe_memload_file(const char *path, size_t *pmemsize, bool_t same_align); /** * load the overlay data in a pe file * @param pmemsize overlay size * @return overlay buf */ WINPE_API void* STDCALL winpe_overlayload_file(const char *path, size_t *poverlaysize); /** * similar to LoadlibrayA, * will load the mempe in a valid imagebase * @return hmodule base */ WINPE_API void* STDCALL winpe_memLoadLibrary(void *mempe); /** * load the mempe in a valid imagebase, will call dll entry * @param imagebase if 0, will load on mempe, else in imagebase * @param flag WINPE_LDFLAG_MEMALLOC 0x1, will alloc memory to imagebase * WINPE_LDFLAG_MEMFIND 0x2, will find a valid space, * @return hmodule base */ WINPE_API void* STDCALL winpe_memLoadLibraryEx(void *mempe, size_t imagebase, DWORD flag, PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress); /** * similar to FreeLibrary, will call dll entry * @return True on successful */ WINPE_API BOOL STDCALL winpe_memFreeLibrary(void *mempe); WINPE_API BOOL STDCALL winpe_memFreeLibraryEx(void *mempe, PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress); /** * similar to GetProcAddress * @return function va */ WINPE_API PROC STDCALL winpe_memGetProcAddress(void *mempe, const char *funcname); /** * use peb and ldr list, to obtain to find kernel32.dll address * @return kernel32.dll va */ WINPE_API void* winpe_findkernel32(); /** * use peb and ldr list, similar as GetModuleHandleA * @return ldr module address */ WINPE_API void* STDCALL winpe_findmoduleaex(PPEB peb, const char *modulename); WINPE_API void* STDCALL winpe_findmodulea(const char *modulename) { return winpe_findmoduleaex(NULL, modulename); } /** * @return LoadLibraryA func addr */ WINPE_API PROC winpe_findloadlibrarya(); /** * @return GetProcAddress func addr */ WINPE_API PROC winpe_findgetprocaddress(); /** * find a valid space address start from imagebase with imagesize * @return va */ WINPE_API void* STDCALL winpe_findspace( size_t imagebase, size_t imagesize, size_t alignsize, PFN_VirtualQuery pfnVirtualQuery); /** * @return the overlay offset */ WINPE_API size_t STDCALL winpe_overlayoffset(const void *rawpe); /** * load the origin rawpe in memory buffer by mem align * @return memsize */ WINPE_API size_t STDCALL winpe_memload(const void *rawpe, size_t rawsize, void *mempe, size_t memsize, bool_t same_align); /** * realoc the addrs for the mempe addr as image base * origin image base usually at 0x00400000, 0x0000000180000000 * new image base mush be divided by 0x10000, if use loadlibrary * @return reloc count */ WINPE_API size_t STDCALL winpe_memreloc(void *mempe, size_t newimagebase); /** * load the iat for the mempe, use rvafunc for winpe_memfindexp * @return iat count */ WINPE_API size_t STDCALL winpe_membindiat(void *mempe, PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress); /** * exec the tls callbacks for the mempe, before dll oep load * @param reason for function PIMAGE_TLS_CALLBACK * @return tls count */ WINPE_API size_t STDCALL winpe_membindtls(void *mempe, DWORD reason); /** * find the iat addres, for call [iat] * @return target iat va */ WINPE_API void* STDCALL winpe_memfindiat(void *mempe, LPCSTR dllname, LPCSTR funcname); /** * find the exp addres, the same as GetProcAddress, * without forward to other dll, such as NTDLL.RtlInitializeSListHead * @return target exp va */ WINPE_API void* STDCALL winpe_memfindexp(void *mempe, LPCSTR funcname); WINPE_API void* STDCALL winpe_memfindexpcrc32(void* mempe, uint32_t crc32); /** * forward the exp to the final expva * @return the final exp va */ WINPE_API void* STDCALL winpe_memforwardexp(void *mempe, size_t exprva, PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress); /** * change the oep of the pe if newoeprva!=0 * @return the old oep rva */ WINPE_API DWORD STDCALL winpe_oepval(void *mempe, DWORD newoeprva); /** * change the imagebase of the pe if newimagebase!=0 * @return the old imagebase va */ WINPE_API size_t STDCALL winpe_imagebaseval(void *mempe, size_t newimagebase); /** * change the imagesize of the pe if newimagesize!=0 * @return the old imagesize */ WINPE_API size_t STDCALL winpe_imagesizeval(void *pe, size_t newimagesize); /** * set off the aslr feature of an pe */ WINPE_API void STDCALL winpe_noaslr(void *pe); /** * append a section header in a pe, sect rva will be ignored * the mempe size must be enough for extend a section * @return image size */ WINPE_API size_t STDCALL winpe_appendsecth(void *mempe, PIMAGE_SECTION_HEADER psecth); #ifdef __cplusplus } #endif // __cplusplus // implement #ifdef WINPE_IMPLEMENTATION #if defined(__TINYC__) #ifdef _WIN64 #pragma pack(8) #else #pragma pack(4) #endif #endif #include #include #include #include void* STDCALL winpe_memload_file(const char* path, size_t* pmemsize, bool_t same_align) { if (!path) { if (pmemsize) *pmemsize = 0; return NULL; // Invalid path } // Open file with error checking FILE* fp = fopen(path, "rb"); if (!fp) { perror("fopen failed"); if (pmemsize) *pmemsize = 0; return NULL; // File not found or inaccessible } // Get file size safely if (fseek(fp, 0, SEEK_END) != 0) { fclose(fp); if (pmemsize) *pmemsize = 0; return NULL; // Seek failed } long rawsize_long = ftell(fp); if (rawsize_long <= 0 || rawsize_long > SIZE_MAX) { fclose(fp); if (pmemsize) *pmemsize = 0; return NULL; // Invalid file size } size_t rawsize = (size_t)rawsize_long; rewind(fp); // Debug: Log the size before allocation printf("Allocating %zu bytes for rawpe (path: %s)\n", rawsize, path); // Alternative allocation with VirtualAlloc (Windows-specific workaround) void* rawpe = NULL; HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); if (hKernel32) { FARPROC pVirtualAlloc = GetProcAddress(hKernel32, "VirtualAlloc"); if (pVirtualAlloc) { rawpe = ((LPVOID(WINAPI*)(LPVOID, SIZE_T, DWORD, DWORD))pVirtualAlloc)( NULL, rawsize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); } } // Fallback to malloc if VirtualAlloc isn't available or fails if (!rawpe) { printf("GOING FOR MALLOC! \n"); rawpe = malloc(rawsize); } if (!rawpe) { perror("Allocation failed for rawpe"); printf("Failed to allocate %zu bytes\n", rawsize); fclose(fp); if (pmemsize) *pmemsize = 0; return NULL; // Memory allocation failed } // Debug: Confirm allocation succeeded printf("Successfully allocated %zu bytes at %p\n", rawsize, rawpe); // Read file into buffer size_t bytes_read = fread(rawpe, 1, rawsize, fp); fclose(fp); if (bytes_read != rawsize) { printf("Read failed: expected %zu, got %zu\n", rawsize, bytes_read); if (hKernel32 && rawpe) { FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree"); if (pVirtualFree) { ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE); } } else { free(rawpe); } if (pmemsize) *pmemsize = 0; return NULL; // Incomplete read } // Calculate required memory size for aligned PE void* mempe = NULL; if (pmemsize) { *pmemsize = winpe_memload(rawpe, rawsize, NULL, 0, FALSE); if (*pmemsize == 0) { printf("winpe_memload failed to calculate size\n"); if (hKernel32 && rawpe) { FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree"); if (pVirtualFree) { ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE); } } else { free(rawpe); } return NULL; } mempe = malloc(*pmemsize); if (!mempe) { perror("malloc failed for mempe"); printf("Failed to allocate %zu bytes\n", *pmemsize); if (hKernel32 && rawpe) { FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree"); if (pVirtualFree) { ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE); } } else { free(rawpe); } *pmemsize = 0; return NULL; } size_t loaded_size = winpe_memload(rawpe, rawsize, mempe, *pmemsize, same_align); if (loaded_size == 0) { printf("winpe_memload failed to load PE\n"); free(mempe); if (hKernel32 && rawpe) { FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree"); if (pVirtualFree) { ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE); } } else { free(rawpe); } *pmemsize = 0; return NULL; } } if (hKernel32 && rawpe) { FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree"); if (pVirtualFree) { ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE); } } else { free(rawpe); } return mempe; } void* STDCALL winpe_memload_buffer(const void* rawpe, size_t rawsize, size_t* pmemsize, bool_t same_align) { void* mempe = NULL; if (pmemsize) { *pmemsize = winpe_memload(rawpe, rawsize, NULL, 0, FALSE); mempe = malloc(*pmemsize); winpe_memload(rawpe, rawsize, mempe, *pmemsize, same_align); } return mempe; } void* STDCALL winpe_overlayload_file(const char *path, size_t *poverlaysize) { FILE *fp = fopen(path, "rb"); fseek(fp, 0, SEEK_END); size_t rawsize = ftell(fp); fseek(fp, 0, SEEK_SET); void *rawpe = malloc(rawsize); fread(rawpe, 1, rawsize, fp); fclose(fp); void *overlay = NULL; size_t overlayoffset = winpe_overlayoffset(rawpe); if(poverlaysize) { *poverlaysize = rawsize - overlayoffset; if(*poverlaysize>0) { overlay = malloc(*poverlaysize); memcpy(overlay, (uint8_t*)rawpe+overlayoffset, *poverlaysize); } } free(rawpe); return overlay; } void* STDCALL winpe_memLoadLibrary(void *mempe) { PFN_LoadLibraryA pfnLoadLibraryA = (PFN_LoadLibraryA)winpe_findloadlibrarya(); PFN_GetProcAddress pfnGetProcAddress = (PFN_GetProcAddress)winpe_findgetprocaddress(); return winpe_memLoadLibraryEx(mempe, 0, WINPE_LDFLAG_MEMFIND | WINPE_LDFLAG_MEMALLOC, pfnLoadLibraryA, pfnGetProcAddress); } void* STDCALL winpe_memLoadLibraryEx(void *mempe, size_t imagebase, DWORD flag, PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress) { // bind windows api char name_kernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l' , '\0'}; char name_VirtualQuery[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'Q', 'u', 'e', 'r', 'y', '\0'}; char name_VirtualAlloc[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'A', 'l', 'l', 'o', 'c', '\0'}; char name_VirtualProtect[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'P', 'r', 'o', 't', 'e', 'c', 't', '\0'}; HMODULE hmod_kernel32 = pfnLoadLibraryA(name_kernel32); PFN_VirtualQuery pfnVirtualQuery = (PFN_VirtualQuery)pfnGetProcAddress(hmod_kernel32, name_VirtualQuery); PFN_VirtualAlloc pfnVirtualAlloc = (PFN_VirtualAlloc)pfnGetProcAddress(hmod_kernel32, name_VirtualAlloc); PFN_VirtualProtect pfnVirtualProtect =(PFN_VirtualProtect)pfnGetProcAddress(hmod_kernel32, name_VirtualProtect); #ifdef _DEBUG assert(pfnVirtualQuery!=0 && pfnVirtualAlloc!=0 && pfnVirtualProtect!=0); #endif // find proper imagebase size_t imagesize = winpe_imagesizeval(mempe, 0); if(flag & WINPE_LDFLAG_MEMFIND) { imagebase = winpe_imagebaseval(mempe, 0); imagebase = (size_t)winpe_findspace(imagebase, imagesize, 0x10000, pfnVirtualQuery); } if(flag & WINPE_LDFLAG_MEMALLOC) // find proper memory to reloc { imagebase = (size_t)pfnVirtualAlloc((void*)imagebase, imagesize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(!imagebase) // try alloc in arbitary place { imagebase = (size_t)pfnVirtualAlloc(NULL, imagesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!imagebase) return NULL; } else { imagebase = (size_t)pfnVirtualAlloc((void*)imagebase, imagesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!imagebase) return NULL; } } // copy to imagebase if(!imagebase) { imagebase = (size_t)mempe; } else { DWORD oldprotect; pfnVirtualProtect((void*)imagebase, imagesize, PAGE_EXECUTE_READWRITE, &oldprotect); inl_memcpy((void*)imagebase, mempe, imagesize); pfnVirtualProtect((void*)imagebase, imagesize, oldprotect, &oldprotect); } // initial memory module if(!winpe_memreloc((void*)imagebase, imagebase)) return NULL; if(!winpe_membindiat((void*)imagebase, pfnLoadLibraryA, pfnGetProcAddress)) return NULL; winpe_membindtls(mempe, DLL_PROCESS_ATTACH); PFN_DllMain pfnDllMain = (PFN_DllMain)(imagebase + winpe_oepval((void*)imagebase, 0)); pfnDllMain((HINSTANCE)imagebase, DLL_PROCESS_ATTACH, NULL); return (void*)imagebase; } BOOL STDCALL winpe_memFreeLibrary(void *mempe) { PFN_LoadLibraryA pfnLoadLibraryA = (PFN_LoadLibraryA)winpe_findloadlibrarya(); PFN_GetProcAddress pfnGetProcAddress = (PFN_GetProcAddress)winpe_findgetprocaddress(); return winpe_memFreeLibraryEx(mempe, pfnLoadLibraryA, pfnGetProcAddress); } BOOL STDCALL winpe_memFreeLibraryEx(void *mempe, PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress) { char name_kernel32[] = {'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '\0'}; char name_VirtualFree[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'F', 'r', 'e', 'e', '\0'}; HMODULE hmod_kernel32 = pfnLoadLibraryA(name_kernel32); PFN_VirtualFree pfnVirtualFree = (PFN_VirtualFree)pfnGetProcAddress(hmod_kernel32, name_VirtualFree); PFN_DllMain pfnDllMain = (PFN_DllMain)((uint8_t*)mempe + winpe_oepval(mempe, 0)); winpe_membindtls(mempe, DLL_PROCESS_DETACH); pfnDllMain((HINSTANCE)mempe, DLL_PROCESS_DETACH, NULL); return pfnVirtualFree(mempe, 0, MEM_FREE); } PROC STDCALL winpe_memGetProcAddress(void *mempe, const char *funcname) { // this function might failed if inline and -Os, -O3, if in dll function, use printf might help void* expva = winpe_memfindexp(mempe, funcname); size_t exprva = (size_t)expva - (size_t)mempe; // printf("[winpe_memGetProcAddress] expva=%p\n", expva); // use pfnLoadLibraryA, pfnGetProcAddress to avoid infinity loop void *hmod_kernel32 = winpe_findkernel32(); char name_GetProcAddress[] = {'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', '\0'}; char name_LoadLibraryA[] = {'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', '\0'}; char *name[2] = {name_LoadLibraryA, name_GetProcAddress}; void *func[2] = {NULL, NULL}; for(int i=0; i<2;i++) { func[i] = winpe_memfindexp(hmod_kernel32, name[i]); } PFN_LoadLibraryA pfnLoadLibraryA = (PFN_LoadLibraryA)func[0]; PFN_GetProcAddress pfnGetProcAddress = (PFN_GetProcAddress)func[1]; return (PROC)winpe_memforwardexp(mempe, exprva, pfnLoadLibraryA, pfnGetProcAddress); } // PE query functions void* winpe_findkernel32() { // return (void*)LoadLibrary("kernel32.dll"); // TEB->PEB->Ldr->InMemoryOrderLoadList->curProgram->ntdll->kernel32 void *kerenl32 = NULL; #ifdef WINPE_NOASM char name_kernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l' , '\0' }; kerenl32 = winpe_findmodulea(name_kernel32); #else #if defined(__GNUC__) // can not use -masm=intel, as there are some ATT in intrin-impl.h #ifdef _WIN64 asm("mov %%gs:0x60, %%rax\n" // peb "mov 0x18(%%rax), %%rax\n" // ldr "mov 0x20(%%rax), %%rax\n" // InMemoryOrderLoadList, currentProgramEntry "mov (%%rax), %%rax\n" // ntdllEntry, currentProgramEntry->->Flink "mov (%%rax), %%rax\n" // kernel32Entry, ntdllEntry->Flink "mov 0x30-0x10(%%rax), %%rax\n" // kernel32.DllBase "mov %%rax, %0\n":"=m"(kerenl32)); #else asm("mov %%fs:0x30, %%eax\n" // peb "mov 0xc(%%eax), %%eax\n" // ldr "mov 0x14(%%eax), %%eax\n" // InMemoryOrderLoadList, currentProgramEntry "mov (%%eax), %%eax\n" // ntdllEntry, currentProgramEntry->->Flink "mov (%%eax), %%eax\n" // kernel32Entry, ntdllEntry->Flink "mov -0x8+0x18(%%eax), %%eax\n" // kernel32.DllBase "mov %%eax, %0\n":"=m"(kerenl32)); #endif // _WIN64 #else #ifdef _WIN64 __asm{ mov rax, gs:[60h]; peb mov rax, [rax+18h]; ldr mov rax, [rax+20h]; InMemoryOrderLoadList, currentProgramEntry mov rax, [rax]; ntdllEntry, currentProgramEntry->->Flink mov rax, [rax]; kernel32Entry, ntdllEntry->Flink mov rax, [rax-10h+30h]; kernel32.DllBase mov kerenl32, rax; } #else __asm{ mov eax, fs:[30h]; peb mov eax, [eax+0ch]; ldr mov eax, [eax+14h]; InMemoryOrderLoadList, currentProgramEntry mov eax, [eax]; ntdllEntry, currentProgramEntry->->Flink mov eax, [eax]; kernel32Entry, ntdllEntry->Flink mov eax, [eax - 8h +18h]; kernel32.DllBase mov kerenl32, eax; } #endif // _WIN64 #endif // __GNUC__ #endif // WINPE_NOASM return kerenl32; } void* STDCALL winpe_findmoduleaex(PPEB peb, const char *modulename) { typedef struct _LDR_ENTRY // has 3 kinds of pointer link list { LIST_ENTRY InLoadOrderLinks; // this has link pointer LIST_ENTRY InMemoryOrderLinks; // order is program, ntdll, kernel32.dll LIST_ENTRY InInitializationOrderLinks;//to next entry in same place PVOID DllBase; // 0x18, 0x30 PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; ULONG TimeDateStamp; } LDR_ENTRY, *PLDR_ENTRY; // use this because of mingw bug PLDR_ENTRY ldrentry = NULL; PPEB_LDR_DATA ldr = NULL; if(!peb) { PTEB teb = NtCurrentTeb(); #ifdef _WIN64 peb = *(PPEB*)((uint8_t*)teb + 0x60); #else peb = *(PPEB*)((uint8_t*)teb + 0x30); #endif } #ifdef _WIN64 ldr = *(PPEB_LDR_DATA*)((uint8_t*)peb + 0x18); #else ldr = *(PPEB_LDR_DATA*)((uint8_t*)peb + 0xC); #endif // InMemoryOrderModuleList is the second entry ldrentry = (PLDR_ENTRY)((size_t)ldr->InMemoryOrderModuleList.Flink - 2*sizeof(size_t)); if(!modulename) { return ldrentry->DllBase; } while(ldrentry->InMemoryOrderLinks.Flink != ldr->InMemoryOrderModuleList.Flink) { PUNICODE_STRING ustr = &ldrentry->FullDllName; int i; for(i=ustr->Length/2-1; i>0 && ustr->Buffer[i]!='\\';i--); if(ustr->Buffer[i]=='\\') i++; if(inl_stricmp2(modulename, ustr->Buffer + i)==0) { return ldrentry->DllBase; } ldrentry = (PLDR_ENTRY)((size_t)ldrentry->InMemoryOrderLinks.Flink - 2*sizeof(size_t)); } return NULL; } PROC winpe_findloadlibrarya() { // -Os might make this function not current, such as strcpy(Flink, "LoadLibraryA"); void* hmod_kernel32 = winpe_findkernel32(); char name_LoadLibraryA[] = {'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', '\0'}; // suppose exp no forward, to avoid recursive return (PROC)winpe_memfindexp(hmod_kernel32, name_LoadLibraryA); } PROC winpe_findgetprocaddress() { void* hmod_kernel32 = winpe_findkernel32(); char name_GetProcAddress[] = {'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', '\0'}; return (PROC)winpe_memfindexp(hmod_kernel32, name_GetProcAddress); } void* STDCALL winpe_findspace(size_t imagebase, size_t imagesize, size_t alignsize, PFN_VirtualQuery pfnVirtualQuery) { #define MAX_QUERY 0x1000 size_t addr = imagebase; MEMORY_BASIC_INFORMATION minfo; for (int i=0;i= imagesize) return (void*)addr; addr += minfo.RegionSize; } return NULL; } // PE load, adjust functions size_t STDCALL winpe_overlayoffset(const void *rawpe) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)rawpe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)rawpe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_SECTION_HEADER pSectHeader = (PIMAGE_SECTION_HEADER)((uint8_t*)pOptHeader + pFileHeader->SizeOfOptionalHeader); WORD sectNum = pFileHeader->NumberOfSections; return pSectHeader[sectNum-1].PointerToRawData + pSectHeader[sectNum-1].SizeOfRawData; } size_t STDCALL winpe_memload(const void *rawpe, size_t rawsize, void *mempe, size_t memsize, bool_t same_align) { // load rawpe to memalign PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)rawpe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)rawpe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_SECTION_HEADER pSectHeader = (PIMAGE_SECTION_HEADER)((uint8_t*)pOptHeader + pFileHeader->SizeOfOptionalHeader); WORD sectNum = pFileHeader->NumberOfSections; size_t imagesize = pOptHeader->SizeOfImage; if(!mempe) return imagesize; else if(memsize!=0 && memsizeSizeOfHeaders); for(WORD i=0;ie_lfanew); pFileHeader = &pNtHeader->FileHeader; pOptHeader = &pNtHeader->OptionalHeader; pSectHeader = (PIMAGE_SECTION_HEADER)((uint8_t*)pOptHeader + pFileHeader->SizeOfOptionalHeader); sectNum = pFileHeader->NumberOfSections; pOptHeader->FileAlignment = pOptHeader->SectionAlignment; for(WORD i=0;ie_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_DATA_DIRECTORY pRelocEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; DWORD reloc_count = 0; DWORD reloc_offset = 0; int64_t shift = (int64_t)newimagebase - (int64_t)pOptHeader->ImageBase; while (reloc_offset < pRelocEntry->Size) { PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION) ((uint8_t*)mempe + pRelocEntry->VirtualAddress + reloc_offset); PRELOCOFFSET pRelocOffset = (PRELOCOFFSET)((uint8_t*)pBaseReloc + sizeof(IMAGE_BASE_RELOCATION)); DWORD item_num = (pBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(RELOCOFFSET); for (size_t i = 0; i < item_num; i++) { if (!pRelocOffset[i].type && !pRelocOffset[i].offset) continue; DWORD targetoffset = pBaseReloc->VirtualAddress + pRelocOffset[i].offset; size_t *paddr = (size_t *)((uint8_t*)mempe + targetoffset); size_t relocaddr = (size_t)((int64_t)*paddr + shift); //printf("reloc 0x%08x->0x%08x\n", *paddr, relocaddr); *paddr = relocaddr; } reloc_offset += sizeof(IMAGE_BASE_RELOCATION) + sizeof(RELOCOFFSET) * item_num; reloc_count += item_num; } pOptHeader->ImageBase = newimagebase; return reloc_count; } size_t STDCALL winpe_membindiat(void *mempe, PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_DATA_DIRECTORY pImpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; PIMAGE_IMPORT_DESCRIPTOR pImpDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((uint8_t*)mempe + pImpEntry->VirtualAddress); PIMAGE_THUNK_DATA pFtThunk = NULL; PIMAGE_THUNK_DATA pOftThunk = NULL; LPCSTR pDllName = NULL; PIMAGE_IMPORT_BY_NAME pImpByName = NULL; size_t funcva = 0; char *funcname = NULL; // origin GetProcAddress will crash at InitializeSListHead if(!pfnLoadLibraryA) pfnLoadLibraryA = (PFN_LoadLibraryA)winpe_findloadlibrarya(); if(!pfnGetProcAddress) pfnGetProcAddress = (PFN_GetProcAddress)winpe_findgetprocaddress(); DWORD iat_count = 0; for (; pImpDescriptor->Name; pImpDescriptor++) { pDllName = (LPCSTR)((uint8_t*)mempe + pImpDescriptor->Name); pFtThunk = (PIMAGE_THUNK_DATA)((uint8_t*)mempe + pImpDescriptor->FirstThunk); pOftThunk = (PIMAGE_THUNK_DATA)((uint8_t*)mempe + pImpDescriptor->OriginalFirstThunk); size_t dllbase = (size_t)pfnLoadLibraryA(pDllName); if(!dllbase) return 0; for (int j=0; pFtThunk[j].u1.Function && pOftThunk[j].u1.Function; j++) { size_t _addr = (size_t)((uint8_t*)mempe + pOftThunk[j].u1.AddressOfData); if(sizeof(size_t)>4) // x64 { if(((uint64_t)_addr>>63) == 1) { funcname = (char *)(_addr & 0x000000000000ffff); } else { pImpByName=(PIMAGE_IMPORT_BY_NAME)_addr; funcname = pImpByName->Name; } } else { if(((size_t)pImpByName>>31) == 1) { funcname = (char *)(_addr & 0x0000ffff); } else { pImpByName=(PIMAGE_IMPORT_BY_NAME)_addr; funcname = pImpByName->Name; } } funcva = (size_t)pfnGetProcAddress((HMODULE)dllbase, funcname); if(!funcva) continue; pFtThunk[j].u1.Function = funcva; #ifdef _DEBUG assert(funcva == (size_t)GetProcAddress((HMODULE)dllbase, funcname)); #endif iat_count++; } } return iat_count; } size_t STDCALL winpe_membindtls(void *mempe, DWORD reason) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_DATA_DIRECTORY pTlsDirectory = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; if(!pTlsDirectory->VirtualAddress) return 0; size_t tls_count = 0; PIMAGE_TLS_DIRECTORY pTlsEntry = (PIMAGE_TLS_DIRECTORY)((uint8_t*)mempe + pTlsDirectory->VirtualAddress); PIMAGE_TLS_CALLBACK *tlscb= (PIMAGE_TLS_CALLBACK*)pTlsEntry->AddressOfCallBacks; if(tlscb) { while(*tlscb) { (*tlscb)(mempe, reason, NULL); tlscb++; tls_count++; } } return tls_count; } void* STDCALL winpe_memfindiat(void *mempe, LPCSTR dllname, LPCSTR funcname) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_DATA_DIRECTORY pImpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; PIMAGE_IMPORT_DESCRIPTOR pImpDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((uint8_t*)mempe + pImpEntry->VirtualAddress); PIMAGE_THUNK_DATA pFtThunk = NULL; PIMAGE_THUNK_DATA pOftThunk = NULL; LPCSTR pDllName = NULL; PIMAGE_IMPORT_BY_NAME pImpByName = NULL; for (; pImpDescriptor->Name; pImpDescriptor++) { pDllName = (LPCSTR)((uint8_t*)mempe + pImpDescriptor->Name); if(dllname && inl_stricmp(pDllName, dllname)!=0) continue; pFtThunk = (PIMAGE_THUNK_DATA)((uint8_t*)mempe + pImpDescriptor->FirstThunk); pOftThunk = (PIMAGE_THUNK_DATA)((uint8_t*)mempe + pImpDescriptor->OriginalFirstThunk); for (int j=0; pFtThunk[j].u1.Function && pOftThunk[j].u1.Function; j++) { pImpByName=(PIMAGE_IMPORT_BY_NAME)((uint8_t*)mempe + pOftThunk[j].u1.AddressOfData); if((size_t)funcname < MAXWORD) // ordinary { WORD funcord = LOWORD(funcname); if(pImpByName->Hint == funcord) return &pFtThunk[j]; } else { if(inl_stricmp(pImpByName->Name, funcname)==0) return &pFtThunk[j]; } } } return 0; } void* STDCALL winpe_memfindexp(void *mempe, LPCSTR funcname) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_DATA_DIRECTORY pExpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; PIMAGE_EXPORT_DIRECTORY pExpDescriptor = (PIMAGE_EXPORT_DIRECTORY)((uint8_t*)mempe + pExpEntry->VirtualAddress); WORD *ordrva = (WORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfNameOrdinals); DWORD *namerva = (DWORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfNames); DWORD *funcrva = (DWORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfFunctions); if((size_t)funcname <= MAXWORD) // find by ordnial { WORD ordbase = LOWORD(pExpDescriptor->Base) - 1; WORD funcord = LOWORD(funcname); return (void*)((uint8_t*)mempe + funcrva[ordrva[funcord-ordbase]]); } else { for(DWORD i=0;iNumberOfNames;i++) { LPCSTR curname = (LPCSTR)((uint8_t*)mempe+namerva[i]); if(inl_stricmp(curname, funcname)==0) { return (void*)((uint8_t*)mempe + funcrva[ordrva[i]]); } } } return NULL; } void* STDCALL winpe_memfindexpcrc32(void* mempe, uint32_t crc32) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_DATA_DIRECTORY pExpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; PIMAGE_EXPORT_DIRECTORY pExpDescriptor =(PIMAGE_EXPORT_DIRECTORY)((uint8_t*)mempe + pExpEntry->VirtualAddress); WORD* ordrva = (WORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfNameOrdinals); DWORD* namerva = (DWORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfNames); DWORD* funcrva = (DWORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfFunctions); for (DWORD i = 0; i < pExpDescriptor->NumberOfNames; i++) { LPCSTR curname = (LPCSTR)((uint8_t*)mempe + namerva[i]); if (crc32==inl_crc32(curname, inl_strlen(curname))) { return (void*)((uint8_t*)mempe + funcrva[ordrva[i]]); } } return NULL; } void* STDCALL winpe_memforwardexp(void *mempe, size_t exprva, PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress) { // this function might have infinite loop // kerenl32.dll, GetProcessMitigationPolicy -> api-ms-win-core-processthreads-l1-1-1.dll -> kerenl32.dll, GetProcessMitigationPolicys size_t dllbase = (size_t)mempe; while (1) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dllbase; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)dllbase + pDosHeader->e_lfanew); PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_DATA_DIRECTORY pExpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; if(exprva >= pExpEntry->VirtualAddress && exprva < pExpEntry->VirtualAddress + pExpEntry->Size) { char namebuf[MAX_PATH]; char *dllname = (char*)(dllbase + exprva); char *funcname = dllname; //printf("[winpe_memforwardexp] dllname=%s funcname=%s\n", dllname, funcname); int i=0, j=0; while(dllname[i]!=0) { if(dllname[i]=='.') { namebuf[j] = dllname[i]; namebuf[++j] = 'd'; namebuf[++j] = 'l'; namebuf[++j] = 'l'; namebuf[++j] = '\0'; funcname = namebuf + j + 1; } else { namebuf[j] = dllname[i]; } i++; j++; } namebuf[j] = '\0'; dllname = namebuf; dllbase = (size_t)pfnLoadLibraryA(dllname); size_t expva = (size_t)pfnGetProcAddress((HMODULE)dllbase, funcname); exprva = expva - dllbase; } else { return (void*)(dllbase + exprva); } } return NULL; } // PE setting function void STDCALL winpe_noaslr(void *pe) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; #ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE #define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 #endif pOptHeader->DllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; } DWORD STDCALL winpe_oepval(void *pe, DWORD newoeprva) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; DWORD orgoep = pOptHeader->AddressOfEntryPoint; if(newoeprva) pOptHeader->AddressOfEntryPoint = newoeprva; return orgoep; } size_t STDCALL winpe_imagebaseval(void *pe, size_t newimagebase) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; size_t imagebase = pOptHeader->ImageBase; if(newimagebase) pOptHeader->ImageBase = newimagebase; return imagebase; } size_t STDCALL winpe_imagesizeval(void *pe, size_t newimagesize) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; size_t imagesize = pOptHeader->SizeOfImage; if(newimagesize) pOptHeader->SizeOfImage = (DWORD)newimagesize; return imagesize; } size_t STDCALL winpe_appendsecth(void *pe, PIMAGE_SECTION_HEADER psecth) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_SECTION_HEADER pSectHeader = (PIMAGE_SECTION_HEADER)((uint8_t*)pOptHeader + pFileHeader->SizeOfOptionalHeader); WORD sectNum = pFileHeader->NumberOfSections; PIMAGE_SECTION_HEADER pLastSectHeader = &pSectHeader[sectNum-1]; DWORD addr, align; // check the space to append section if(pFileHeader->SizeOfOptionalHeader + sizeof(IMAGE_SECTION_HEADER)> pSectHeader[0].PointerToRawData) return 0; // fill rva addr align = pOptHeader->SectionAlignment; addr = pLastSectHeader->VirtualAddress + pLastSectHeader->Misc.VirtualSize; if(addr % align) addr += align - addr%align; psecth->VirtualAddress = addr; // fill file offset align = pOptHeader->FileAlignment; addr = pLastSectHeader->PointerToRawData+ pLastSectHeader->SizeOfRawData; if(addr % align) addr += align - addr%align; psecth->PointerToRawData = addr; // adjust the section and imagesize pFileHeader->NumberOfSections++; inl_memcpy(&pSectHeader[sectNum], psecth, sizeof(IMAGE_SECTION_HEADER)); align = pOptHeader->SectionAlignment; addr = psecth->VirtualAddress + psecth->Misc.VirtualSize; if(addr % align) addr += align - addr%align; pOptHeader->SizeOfImage = addr; return pOptHeader->SizeOfImage; } #endif // WINPE_IMPLEMENTATION #endif // _WINPE_H