├── .gitignore ├── PebLdr.cpp ├── PebLdr.h ├── PebLdr.vcxproj ├── PebLdr.vcxproj.filters ├── PebLdr.vcxproj.user ├── README.md ├── Source.cpp └── crc32.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.vcx* 2 | x64/ 3 | Debug/ 4 | .gitignore -------------------------------------------------------------------------------- /PebLdr.cpp: -------------------------------------------------------------------------------- 1 | #include "PebLdr.h" 2 | 3 | HMODULE getK32() { 4 | HMODULE r; 5 | #ifdef _WIN64 6 | PPEB _ppeb = (PPEB)__readgsqword(0x60); 7 | r = *(HMODULE*)((unsigned char*)_ppeb->Ldr->InMemoryOrderModuleList.Flink->Flink->Flink + 0x20); 8 | #else 9 | PPEB _ppeb = (PPEB)__readfsdword(0x30); 10 | r = *(HMODULE*)((unsigned char*)_ppeb->Ldr->InMemoryOrderModuleList.Flink->Flink->Flink + 0x10); 11 | #endif 12 | return r; 13 | } 14 | -------------------------------------------------------------------------------- /PebLdr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef __PEB_LDR 3 | #define __PEB_LDR 4 | 5 | #include 6 | #include 7 | #include 8 | #include "crc32.h" 9 | #include 10 | 11 | #define HASH(s) WSID(s) 12 | 13 | HMODULE getK32(); 14 | 15 | typedef struct _peb_ldr { 16 | // Get the current module's base address 17 | HMODULE current_proc; 18 | HMODULE base; 19 | void* p_eat_strtbl; 20 | PDWORD p_eat_ptrtbl; 21 | PWORD p_eat_ordtbl; 22 | size_t num_exp; 23 | BOOL init; 24 | BOOL _eat_from_base() { 25 | IMAGE_DOS_HEADER* _dos = (IMAGE_DOS_HEADER*)this->base; 26 | if (_dos->e_magic != IMAGE_DOS_SIGNATURE) 27 | return FALSE; 28 | IMAGE_NT_HEADERS* _nt = (IMAGE_NT_HEADERS*)((size_t)this->base + _dos->e_lfanew); 29 | if (_nt->Signature != IMAGE_NT_SIGNATURE) 30 | return FALSE; 31 | 32 | IMAGE_EXPORT_DIRECTORY* _export = (IMAGE_EXPORT_DIRECTORY*)(_nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (size_t)this->base); 33 | PDWORD funcTbl = (PDWORD)(_export->AddressOfFunctions + (size_t)this->base); 34 | void* nameTbl = (void*)(_export->AddressOfNames + (size_t)this->base); 35 | PWORD ordTbl = (PWORD)(_export->AddressOfNameOrdinals + (size_t)this->base); 36 | this->p_eat_ptrtbl = funcTbl; 37 | this->p_eat_strtbl = nameTbl; 38 | this->p_eat_ordtbl = ordTbl; 39 | this->num_exp = _export->NumberOfFunctions; 40 | return TRUE; 41 | } 42 | 43 | /* 44 | Passing NULL as the dll name signifies you're walking the export table 45 | of Kernel32.dll 46 | */ 47 | _peb_ldr(const char* dll) : init(FALSE), base(NULL), p_eat_ptrtbl(NULL), p_eat_strtbl(NULL) { 48 | 49 | #ifdef _WIN64 50 | PPEB _ppeb = (PPEB)__readgsqword(0x60); 51 | current_proc = *(HMODULE*)((unsigned char*)_ppeb->Ldr->InMemoryOrderModuleList.Flink + 0x20); 52 | #else 53 | PPEB _ppeb = (PPEB)__readfsdword(0x30); 54 | current_proc = *(HMODULE*)((unsigned char*)_ppeb->Ldr->InMemoryOrderModuleList.Flink + 0x10); 55 | #endif 56 | 57 | if (dll != NULL) { 58 | this->base = GetModuleHandleA(dll); 59 | if (this->base == NULL) { 60 | this->base = LoadLibraryA(dll); 61 | if (this->base == NULL) 62 | return; 63 | } 64 | } 65 | else { 66 | this->base = getK32(); 67 | } 68 | if (this->_eat_from_base()) { 69 | this->init = TRUE; 70 | } 71 | else { 72 | return; 73 | } 74 | } 75 | void *operator new(size_t block_size) { 76 | return HeapAlloc(GetProcessHeap(), 0, block_size); 77 | } 78 | void operator delete(void* p) { 79 | HeapFree(GetProcessHeap(), 0, p); 80 | } 81 | ~_peb_ldr() { 82 | HeapFree(GetProcessHeap(), 0, this); 83 | } 84 | 85 | void* get(DWORD hash) { 86 | void* string_tbl_iter = this->p_eat_strtbl; 87 | for (int i = 0; i < this->num_exp; i++) { 88 | DWORD name_offset = *(DWORD*)string_tbl_iter; 89 | char* namePtr = ((char*)this->base + name_offset); 90 | auto x = HASH(namePtr); 91 | if (HASH(namePtr) == hash) { 92 | DWORD fn_va = this->p_eat_ptrtbl[this->p_eat_ordtbl[i]]; 93 | void* fn = (void*)((size_t)this->base + (DWORD)fn_va); 94 | return fn; 95 | } 96 | string_tbl_iter = (void*)((unsigned char*)string_tbl_iter + sizeof(DWORD)); 97 | } 98 | return NULL; 99 | } 100 | 101 | void* currentmodule_iat_hook(DWORD lowercase_dllname_hash, DWORD function_hash, size_t absolute_dest_address) { 102 | IMAGE_DOS_HEADER* ImgBase = (IMAGE_DOS_HEADER*)this->current_proc; 103 | if (ImgBase->e_magic != IMAGE_DOS_SIGNATURE) 104 | return FALSE; 105 | 106 | IMAGE_NT_HEADERS* inth = (IMAGE_NT_HEADERS*)(ImgBase->e_lfanew + (size_t)this->current_proc); 107 | 108 | PIMAGE_OPTIONAL_HEADER pImageOptHdr = &inth->OptionalHeader; 109 | if ((pImageOptHdr->Magic ^ IMAGE_NT_OPTIONAL_HDR_MAGIC)) { 110 | return FALSE; 111 | } 112 | IMAGE_IMPORT_DESCRIPTOR* importIter = (IMAGE_IMPORT_DESCRIPTOR*)(pImageOptHdr->DataDirectory[1].VirtualAddress + (size_t)this->current_proc); 113 | while (importIter->Name != NULL) { 114 | const char* pModString = (const char*)((size_t)this->current_proc + importIter->Name); 115 | char* pModCpy = new(char[strlen(pModString) + 1]); 116 | //pModCpy, pModString, strlen(pModString) 117 | strncpy_s(pModCpy, strlen(pModString) + 1, pModString, strlen(pModString)); 118 | for (size_t i = 0; i < strlen(pModString); i++) 119 | { 120 | pModCpy[i] = tolower(pModCpy[i]); 121 | } 122 | 123 | if (HASH(pModCpy) != lowercase_dllname_hash) { 124 | importIter++; 125 | continue; 126 | } 127 | //printf("Found dll target : %s\n", pModCpy); 128 | 129 | IMAGE_THUNK_DATA* imgThunk = (PIMAGE_THUNK_DATA)(importIter->FirstThunk + (size_t)this->current_proc); 130 | IMAGE_THUNK_DATA* OriginalThunk = (PIMAGE_THUNK_DATA)(importIter->OriginalFirstThunk + (size_t)this->current_proc); 131 | 132 | while (imgThunk->u1.Function != NULL) { 133 | LPVOID func = (LPVOID)(imgThunk->u1.Function); 134 | PIMAGE_IMPORT_BY_NAME pFname = (PIMAGE_IMPORT_BY_NAME)((size_t)this->current_proc + OriginalThunk->u1.AddressOfData); 135 | if (HASH(pFname->Name) == function_hash) { 136 | 137 | DWORD oldProtect, junk = 0; 138 | VirtualProtect(&imgThunk->u1.Function, sizeof(size_t), PAGE_READWRITE, &oldProtect); 139 | imgThunk->u1.Function = absolute_dest_address; 140 | VirtualProtect(&imgThunk->u1.Function, sizeof(size_t), oldProtect, &junk); 141 | return func; 142 | } 143 | OriginalThunk++; 144 | imgThunk++; 145 | } 146 | } 147 | return NULL; 148 | } 149 | 150 | 151 | } _peb_ldr, *_ppeb_ldr; 152 | 153 | 154 | 155 | #endif -------------------------------------------------------------------------------- /PebLdr.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {0203B09F-6DF8-4B2A-A081-B9DE97F65F4B} 24 | PebLdr 25 | 10.0.19041.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v141 45 | MultiByte 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | MinSpace 77 | true 78 | true 79 | Default 80 | 81 | 82 | 83 | 84 | Level3 85 | MinSpace 86 | true 87 | true 88 | Default 89 | 90 | 91 | 92 | 93 | Level3 94 | MinSpace 95 | true 96 | true 97 | true 98 | true 99 | 100 | 101 | true 102 | true 103 | 104 | 105 | 106 | 107 | Level3 108 | MinSpace 109 | true 110 | true 111 | true 112 | true 113 | 114 | 115 | true 116 | true 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /PebLdr.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | 34 | 35 | Source Files 36 | 37 | 38 | -------------------------------------------------------------------------------- /PebLdr.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PebLdr 2 | 3 | Yet another PEB Loader 4 | 5 | Refer to Source.cpp for usage. 6 | The basic gist is this: 7 | 8 | ```C++ 9 | #include 10 | #include "PebLdr.h" 11 | #include 12 | 13 | typedef int(*pMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT); 14 | pMessageBoxA OriginalMessageBoxA = MessageBoxA; 15 | 16 | int HookedMessageBoxA(HWND hWnd, LPCSTR content, LPCSTR title, UINT status) { 17 | return OriginalMessageBoxA(NULL, "MUAHAHAHAHA Hooked!", "So Long, And Thanks For All The Fish!", MB_OK); 18 | } 19 | 20 | int main() { 21 | // Must be a constexpr int to be evaluated at compile-time 22 | // Store the crc32 of "MessageBoxA" 23 | constexpr int y = HASH("MessageBoxA"); 24 | 25 | // create a new loader for "User32.dll" 26 | _ppeb_ldr pk32 = new _peb_ldr("User32.dll"); 27 | // use the get function to retrieve the export who's name equals the crc32 of "MessageBoxA" 28 | pMessageBoxA _pMessageBoxA = (pMessageBoxA)pk32->get(y); 29 | // call it normally 30 | _pMessageBoxA(NULL, "Hello", "Hello World!", MB_OK); 31 | 32 | // Hook the function 33 | void* lpMsgBoxA = pk32->currentmodule_iat_hook(HASH("user32.dll"), HASH("MessageBoxA"), (size_t)HookedMessageBoxA); 34 | 35 | // Call the hooked MessageBoxA 36 | MessageBoxA(NULL, "Hello", "Hello World!", MB_OK); 37 | 38 | // Unhook the function 39 | pk32->currentmodule_iat_hook(HASH("user32.dll"), HASH("MessageBoxA"), (size_t)lpMsgBoxA); 40 | 41 | // Call the unhooked MessageBoxA 42 | MessageBoxA(NULL, "Hello", "Hello World!", MB_OK); 43 | 44 | return 0; 45 | } 46 | ``` -------------------------------------------------------------------------------- /Source.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "PebLdr.h" 3 | #include 4 | 5 | typedef int(*pMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT); 6 | pMessageBoxA OriginalMessageBoxA = MessageBoxA; 7 | 8 | int HookedMessageBoxA(HWND hWnd, LPCSTR content, LPCSTR title, UINT status) { 9 | return OriginalMessageBoxA(NULL, "MUAHAHAHAHA Hooked!", "So Long, And Thanks For All The Fish!", MB_OK); 10 | } 11 | 12 | int main() { 13 | // Must be a constexpr int to be evaluated at compile-time 14 | // Store the crc32 of "MessageBoxA" 15 | constexpr int y = HASH("MessageBoxA"); 16 | 17 | // create a new loader for "User32.dll" 18 | _ppeb_ldr pk32 = new _peb_ldr("User32.dll"); 19 | // use the get function to retrieve the export who's name equals the crc32 of "MessageBoxA" 20 | pMessageBoxA _pMessageBoxA = (pMessageBoxA)pk32->get(y); 21 | // call it normally 22 | _pMessageBoxA(NULL, "Hello", "Hello World!", MB_OK); 23 | 24 | // Hook the function 25 | void* lpMsgBoxA = pk32->currentmodule_iat_hook(HASH("user32.dll"), HASH("MessageBoxA"), (size_t)HookedMessageBoxA); 26 | 27 | // Call the hooked MessageBoxA 28 | MessageBoxA(NULL, "Hello", "Hello World!", MB_OK); 29 | 30 | // Unhook the function 31 | pk32->currentmodule_iat_hook(HASH("user32.dll"), HASH("MessageBoxA"), (size_t)lpMsgBoxA); 32 | 33 | // Call the unhooked MessageBoxA 34 | MessageBoxA(NULL, "Hello", "Hello World!", MB_OK); 35 | 36 | return 0; 37 | } -------------------------------------------------------------------------------- /crc32.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //https://create.stephan-brumme.com/crc32/#crc32Lookup 5 | //https://stackoverflow.com/questions/28675727/using-crc32-algorithm-to-hash-string-at-compile-time 6 | 7 | // Generate CRC lookup table 8 | template 9 | struct f : f<((c & 1) ? 0xedb88320 : 0) ^ (c >> 1), k - 1> {}; 10 | template struct f { enum { value = c }; }; 11 | 12 | #define A(x) B(x) B(x + 128) 13 | #define B(x) C(x) C(x + 64) 14 | #define C(x) D(x) D(x + 32) 15 | #define D(x) E(x) E(x + 16) 16 | #define E(x) F(x) F(x + 8) 17 | #define F(x) G(x) G(x + 4) 18 | #define G(x) H(x) H(x + 2) 19 | #define H(x) I(x) I(x + 1) 20 | #define I(x) f::value , 21 | 22 | constexpr unsigned crc_table[] = { A(0) }; 23 | 24 | // Constexpr implementation and helpers 25 | constexpr uint32_t crc32_impl(const char* p, size_t len, uint32_t crc) { 26 | return len ? 27 | crc32_impl(p + 1, len - 1, (crc >> 8) ^ crc_table[(crc & 0xFF) ^ *p]) 28 | : crc; 29 | } 30 | 31 | constexpr uint32_t crc32(const char* data, size_t length) { 32 | return ~crc32_impl(data, length, ~0); 33 | } 34 | 35 | constexpr size_t strlen_c(const char* str) { 36 | return *str ? 1 + strlen_c(str + 1) : 0; 37 | } 38 | 39 | constexpr int WSID(const char* str) { 40 | return crc32(str, strlen_c(str)); 41 | } 42 | --------------------------------------------------------------------------------