├── README.md ├── bruh.asm └── main.c /README.md: -------------------------------------------------------------------------------- 1 | # effective-waffle 2 | yet another sleep encryption thing. also used the default github repo name for this one. 3 | 4 | TpAllocWork + TpPostWork + TpWaitForWork + TpReleaseWork to create + execute + clean the callback. 5 | 6 | The callback is some scuffed assembly that queues up a ton of rop gadgets that will pop arguments (either hard coded values, or based on an offset within a struct passed as the TpAllocWork parameter) into the correct registers, then returning to the next gadget/function. Padding is creating between some function calls via pushing 5 garbage values + a gadget to move the stack pointer by 40 bytes as a hacky solution to fix some weird stack stuff I ran into. This is also probably why the call stack looks disgusting but idk how else to address that. 7 | 8 | If you're trying to extend this and make it actually useful, there's gonna be a lot of weird stack fuckery going on (at least on my end). Also, can't guarantee the rop gadgets that I found will be present in all versions of windows (I was devving on Server 2016). 9 | 10 | ## Credits 11 | Kudaes, for creating [RustChain](https://github.com/Kudaes/RustChain) which the ROP idea came from for me 12 | C5pider, for creating [Ekko](https://github.com/Cracked5pider/Ekko/) and helping me understand sleep encryption 13 | x86matthew, for helping me troubleshoot weird stack stuff 14 | NinjaParanoid, for providing a [blog post](https://0xdarkvortex.dev/hiding-in-plainsight/) on the Tp Apis 15 | -------------------------------------------------------------------------------- /bruh.asm: -------------------------------------------------------------------------------- 1 | WorkCallback proto 2 | 3 | .code 4 | 5 | WorkCallback proc 6 | 7 | ; Queuing up a shitty rop chain that triggers from the return 8 | 9 | ; seems to work fine without padding between this virtualprotect and returning to code 10 | 11 | ; 2nd Virtual Protect 12 | push qword ptr [rdx + 40h] ; VirtualProtect address 13 | push qword ptr [rdx + 70h] ; arg4 (out lpflOldProtect) 14 | push 20h ; arg3 (flNewProtect), RX 15 | push qword ptr [rdx + 80h] ; arg1 (lpAddress) 16 | push qword ptr [rdx + 78h] ; arg2 (dwSize) 17 | push qword ptr [rdx + 38h] ; pop rdx; pop rcx; pop r8; pop r9; ret; 18 | 19 | ; padding 20 | push 22 21 | push 22 22 | push 22 23 | push 22 24 | push 22 25 | push qword ptr [rdx + 88h] ; add rsp 28h; ret 26 | 27 | ; SystemFunction032 28 | push qword ptr [rdx + 48h] ; VirtualProtect address 29 | push 22 ; arg4 (nothing) 30 | push 22 ; arg3 (nothing) 31 | push qword ptr [rdx + 58h] ; arg1 (ustring* data) 32 | push qword ptr [rdx + 60h] ; arg2 (ustring* key) 33 | push qword ptr [rdx + 38h] ; pop rdx; pop rcx; pop r8; pop r9; ret; 34 | 35 | ; padding 36 | push 22 37 | push 22 38 | push 22 39 | push 22 40 | push 22 41 | push qword ptr [rdx + 88h] ; add rsp 28h; ret 42 | 43 | ; 2nd Sleep, "the real sleep" 44 | push qword ptr [rdx + 50h] ; Sleep address 45 | push 22 ; arg4 (nothing) 46 | push 22 ; arg3 (nothing) 47 | push -1 ; arg1 (Handle) 48 | push qword ptr [rdx + 68h] ; arg2 (SleepTime) 49 | push qword ptr [rdx + 38h] ; pop rdx; pop rcx; pop r8; pop r9; ret; 50 | 51 | ; padding 52 | push 22 53 | push 22 54 | push 22 55 | push 22 56 | push 22 57 | push qword ptr [rdx + 88h] ; add rsp 28h; ret 58 | 59 | ; SystemFunction032 60 | push qword ptr [rdx + 48h] ; VirtualProtect address 61 | push 22 ; arg4 (nothing) 62 | push 22 ; arg3 (nothing) 63 | push qword ptr [rdx + 58h] ; arg1 (ustring* data) 64 | push qword ptr [rdx + 60h] ; arg2 (ustring* key) 65 | push qword ptr [rdx + 38h] ; pop rdx; pop rcx; pop r8; pop r9; ret; 66 | 67 | ; padding 68 | push 22 69 | push 22 70 | push 22 71 | push 22 72 | push 22 73 | push qword ptr [rdx + 88h] ; add rsp 28h; ret 74 | 75 | ; 1st Virtual Protect 76 | push qword ptr [rdx + 40h] ; VirtualProtect address 77 | push qword ptr [rdx + 70h] ; arg4 (out lpflOldProtect) 78 | push 4h ; arg3 (flNewProtect), RW 79 | push qword ptr [rdx + 80h] ; arg1 (lpAddress) 80 | push qword ptr [rdx + 78h] ; arg2 (dwSize) 81 | push qword ptr [rdx + 38h] ; pop rdx; pop rcx; pop r8; pop r9; ret; 82 | 83 | ; padding 84 | push 22 85 | push 22 86 | push 22 87 | push 22 88 | push 22 89 | push qword ptr [rdx + 88h] ; add rsp 28h; ret 90 | 91 | ; 1st Sleep, so then protect doesn't run before the TPWAITFORWORK call. 92 | push qword ptr [rdx + 50h] ; Sleep address 93 | push 22 ; arg4 (nothing) 94 | push 22 ; arg3 (nothing) 95 | push -1 ; arg1 (Handle) 96 | push 32h ; arg2 (SleepTime), 50 milliseconds 97 | push qword ptr [rdx + 38h] ; pop rdx; pop rcx; pop r8; pop r9; ret; 98 | ; Begin the ROP 99 | ret ; return into the rop chain 100 | 101 | 102 | WorkCallback endp 103 | 104 | end 105 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | typedef NTSTATUS(NTAPI* TPALLOCWORK)(PTP_WORK* ptpWrk, PTP_WORK_CALLBACK pfnwkCallback, PVOID OptionalArg, PTP_CALLBACK_ENVIRON CallbackEnvironment); 5 | typedef VOID(NTAPI* TPPOSTWORK)(PTP_WORK); 6 | typedef VOID(NTAPI* TPRELEASEWORK)(PTP_WORK); 7 | typedef VOID(NTAPI* TPWAITFORWORK)(PTP_WORK, BOOL); 8 | 9 | typedef struct 10 | { 11 | DWORD Length; 12 | DWORD MaximumLength; 13 | PVOID Buffer; 14 | } USTRING; 15 | 16 | typedef struct _CALLBACK_ARGS { 17 | PVOID MessageBoxA; // 0x0 18 | PVOID text; // 0x8 19 | PVOID caption; // 0x10 20 | PVOID gadget_pop_rcx_ret; // 0x18 address of a pop rcx; 21 | PVOID gadget_pop_rdx_ret; // 0x20 address of a pop rdx; 22 | PVOID gadget_pop_r8_ret; // 0x28 address of a pop r8; 23 | PVOID gadget_pop_r9_ret; // 0x30 address of a pop r9; 24 | PVOID gadget_pop_rdx_rcx_r8_r9_ret; // 0x38 address of a pop rdx; pop rcx; pop r8; pop r9; ret; 25 | PVOID pVirtualProtect; // 0x40 address of VirtualProtect 26 | PVOID pSystemFunction032; // 0x48 address of SystemFunction032 27 | PVOID pSleep; // 0x50 address of Sleep 28 | PVOID pImage; // 0x58 address of the USTRING containing encryption information for Sys032 29 | PVOID pKey; // 0x60 address of the key buffer 30 | LONG SleepTime; // 0x68 sleep duration 31 | PVOID OldProtect; // 0x70 address of the oldprotect value for virtualprotect 32 | DWORD BytesToProtect; // 0x78 amount of bytes to protect for virtualprotect 33 | PVOID pMemory; // 0x80 address of the bytes to protect for virtual protect 34 | PVOID gadget_add_rsp_28_ret; // 0x88 address of a add rsp 28h; ret; 35 | } CALLBACK_ARGS, *PCALLBACK_ARGS; 36 | 37 | extern VOID CALLBACK WorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work); 38 | typedef NTSTATUS(WINAPI* NTCONTINUE)(PCONTEXT, BOOLEAN); 39 | void* GetGadget(char* gadget) { 40 | // This function is written like shit (i am chatgpt abuser), please fix 41 | int counter = 0; 42 | char* moduleName = NULL; 43 | HMODULE ntdll = NULL; 44 | do { 45 | switch (counter) 46 | { 47 | case 0: 48 | moduleName = "ntdll.dll"; 49 | break; 50 | case 1: 51 | moduleName = "kernel32.dll"; 52 | break; 53 | case 2: // Server 2016 finds pop r8; ret here 54 | moduleName = "shell32.dll"; 55 | LoadLibraryA(moduleName); 56 | break; 57 | case 3: 58 | moduleName = "kernelbase.dll"; 59 | break; 60 | 61 | } 62 | counter++; 63 | if (counter > 3) { 64 | break; 65 | } 66 | ntdll = GetModuleHandleA(moduleName); 67 | if (ntdll == NULL) { 68 | printf("Could not load module\n"); 69 | return NULL; // handle error 70 | } 71 | 72 | // get information about the ntdll module 73 | MODULEINFO mi; 74 | if (!GetModuleInformation(GetCurrentProcess(), ntdll, &mi, sizeof(mi))) { 75 | printf("getmoduleinformation error\n"); 76 | return NULL; // handle error 77 | } 78 | 79 | // find the address of the "\x90" bytes in the .text section of the ntdll module 80 | IMAGE_NT_HEADERS* nt_headers = (IMAGE_NT_HEADERS*)((char*)ntdll + ((IMAGE_DOS_HEADER*)ntdll)->e_lfanew); // address of the PE header 81 | IMAGE_SECTION_HEADER* section_header = IMAGE_FIRST_SECTION(nt_headers); // address of the first section header 82 | 83 | for (int i = 0; i < nt_headers->FileHeader.NumberOfSections; i++) { 84 | if (strcmp((char*)section_header->Name, ".text") == 0) { // if the section name is ".text" 85 | char* start_of_section = (char*)mi.lpBaseOfDll + section_header->VirtualAddress; // address of the start of the section 86 | char* end_of_section = start_of_section + section_header->Misc.VirtualSize; // address of the end of the section 87 | for (char* p = start_of_section; p < end_of_section; p++) { 88 | if (memcmp(p, gadget, strlen(gadget)) == 0) { 89 | printf("Found gadget at 0x%llx in %s\n", p, moduleName); 90 | return p; 91 | } 92 | } 93 | } 94 | section_header++; // move to the next section header 95 | } 96 | } while (1); 97 | printf("Could not find gadget\n"); 98 | return NULL; 99 | } 100 | 101 | void tpsleep(DWORD SleepTime) { 102 | char* text = "Hello"; 103 | DWORD OldProtect = 0; 104 | 105 | USTRING Img = { 0 }; 106 | PVOID ImageBase = GetModuleHandleA(NULL); 107 | Img.Buffer = ImageBase; 108 | DWORD ImageSize = ((PIMAGE_NT_HEADERS)((PBYTE)ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew))->OptionalHeader.SizeOfImage; 109 | Img.Length = Img.MaximumLength = ImageSize; 110 | 111 | USTRING Key = { 0 }; 112 | CHAR KeyBuf[16] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }; 113 | Key.Buffer = KeyBuf; 114 | Key.Length = Key.MaximumLength = 16; 115 | 116 | CALLBACK_ARGS c_args = { 0 }; 117 | c_args.MessageBoxA = GetProcAddress(LoadLibraryA("user32.dll"), "MessageBoxA"); 118 | c_args.text = text; 119 | c_args.caption = text; 120 | c_args.gadget_pop_rcx_ret = GetGadget("\x59\xc3"); 121 | c_args.gadget_pop_rdx_ret = GetGadget("\x5a\xc3"); 122 | c_args.gadget_pop_r8_ret = GetGadget("\x41\x58\xc3"); 123 | c_args.gadget_pop_r9_ret = GetGadget("\x41\x59\xc3"); 124 | c_args.gadget_pop_rdx_rcx_r8_r9_ret = GetGadget("\x5A\x59\x41\x58\x41\x59\xC3"); 125 | c_args.pVirtualProtect = VirtualProtect; 126 | c_args.pSystemFunction032 = GetProcAddress(LoadLibraryA("Advapi32"), "SystemFunction032"); 127 | c_args.pSleep = WaitForSingleObject; 128 | c_args.pImage = &Img; 129 | c_args.pKey = &Key; 130 | c_args.SleepTime = SleepTime; 131 | c_args.OldProtect = &OldProtect; 132 | c_args.BytesToProtect = Img.Length; 133 | c_args.pMemory = Img.Buffer; 134 | c_args.gadget_add_rsp_28_ret = GetGadget("\x48\x83\xc4\x28\xC3"); 135 | 136 | //GetGadget("\x48\x83\xc4\x18\xC3"); // sub rsp 18h 137 | //GetGadget("\x59\x41\x58\x41\x59\xc3"); //pop rcx, r8, r9 138 | 139 | 140 | FARPROC pTpAllocWork = GetProcAddress(GetModuleHandleA("ntdll"), "TpAllocWork"); 141 | FARPROC pTpPostWork = GetProcAddress(GetModuleHandleA("ntdll"), "TpPostWork"); 142 | FARPROC pTpWaitForWork = GetProcAddress(GetModuleHandleA("ntdll"), "TpWaitForWork"); 143 | FARPROC pTpReleaseWork = GetProcAddress(GetModuleHandleA("ntdll"), "TpReleaseWork"); 144 | 145 | PTP_WORK WorkReturn = NULL; 146 | ((TPALLOCWORK)pTpAllocWork)(&WorkReturn, (PTP_WORK_CALLBACK)WorkCallback, &c_args, NULL); 147 | ((TPPOSTWORK)pTpPostWork)(WorkReturn); 148 | ((TPWAITFORWORK)pTpWaitForWork)(WorkReturn, FALSE); 149 | ((TPRELEASEWORK)pTpReleaseWork)(WorkReturn); 150 | } 151 | int main() { 152 | DWORD i = 0; 153 | while (1) { 154 | printf("On iteration %d\n", i); 155 | tpsleep(1000); 156 | i++; 157 | } 158 | 159 | return 0; 160 | } 161 | --------------------------------------------------------------------------------