Hello
I'm trying to build a loader stub for my crypter. I have seen how to create a new file, set it to temp, delete on exit, etc.
However I want to go down the stealthier path, and not write anything on disk or create a new file.
I have found a source in rohitab about executing a PE file from memory.
It is I believe, based, on Tan Chew Keong's POC posted in 2004 on win32 forking.
I have been experimenting with this, and although it looks promising (and working!), is a bit more complicated that I would want it to be.
So I started toying with the idea of setting my executable's base address to a lower memory address, and then just loading the actual crypted file's code, data and import
section's in the most commonly used memory address: 0x00400000.
In VC++, under link, there is an advanced option: Base address. I've managed to get a simple app to load at 0x00300000, and set its sections using this base address, leaving
0x00400000 free for the crypted application. Now I know that this is a tricky approach, and have managed to get it working up to a point.
The stub is a simple Hello world written in masm:Code:int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdParam, int iCmdShow) { DWORD error = 0; LPVOID BaseAddr = NULL; DWORD oldVal = NULL; BaseAddr = VirtualAlloc((LPVOID)0x00400000,0x4000,MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE); bool ok = VirtualProtect(BaseAddr,0x4000,PAGE_EXECUTE_READWRITE,&oldVal); if (BaseAddr != NULL && ok) { memcpy(LPVOID(0x00401000),(void*)CodeStub,39); memcpy(LPVOID(0x00402000),(void*)ImportStub,144); memcpy(LPVOID(0x00403000),(void*)DataStub,16); } else { error = GetLastError(); } _asm{ mov eax,00401000h jmp eax } }
The stub imported are the actual bytes:Code:.data HelloWorld db "Hello World!", 0 .code start: invoke MessageBoxA, NULL, addr HelloWorld, addr HelloWorld, MB_OK invoke ExitProcess, 0 end start
Now, When I run this under OllyDbg, it works up to the point where it has to call MessageBoxA.Code:unsigned char CodeStub[39] = { 0x6A, 0x00, 0x68, 0x00, 0x30, 0x40, 0x00, 0x68, 0x00, 0x30, 0x40, 0x00, 0x6A, 0x00, 0xE8, 0x0D, 0x00, 0x00, 0x00, 0x6A, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x25, 0x00, 0x20, 0x40, 0x00, 0xFF, 0x25, 0x08, 0x20, 0x40, 0x00, 0x00 }; unsigned char ImportStub[144] = { 0x5C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x45, 0x78, 0x69, 0x74, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x00, 0x6B, 0x65, 0x72, 0x6E, 0x65, 0x6C, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x00, 0xB1, 0x01, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6F, 0x78, 0x41, 0x00, 0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C }; unsigned char DataStub[16] = { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x00, 0x00, 0x00 };
The memory is properly allocated and commited, the byte content is properly copied, and the code section works.
However, when reaching to 0040100E which is the imported kernel function MessageBoxA, it crashes with a memory access violation. The above code (produced by olly), is found in the codeCode:00401000 6A 00 PUSH 0 00401002 68 00304000 PUSH 403000 ; ASCII "Hello World!" 00401007 68 00304000 PUSH 403000 ; ASCII "Hello World!" 0040100C 6A 00 PUSH 0 0040100E E8 0D000000 CALL 00401020 00401013 6A 00 PUSH 0 00401015 E8 00000000 CALL 0040101A 0040101A ^ FF25 00204000 JMP DWORD PTR DS:[402000] 00401020 ^ FF25 08204000 JMP DWORD PTR DS:[402008]
section copied by the app I've made.
The exact same code found in the original Hello World is:
The difference is obvious, here the pointer to the imported function works fine.Code:00401000 /. 6A 00 PUSH 0 ; /Type = MB_OK|MB_DEFBUTTON1|MB_APPLMODAL 00401002 |. 68 00304000 PUSH OFFSET 00403000 ; |Caption = "Hello World!" 00401007 |. 68 00304000 PUSH OFFSET 00403000 ; |Text = "Hello World!" 0040100C |. 6A 00 PUSH 0 ; |hOwner = NULL 0040100E |. E8 0D000000 CALL <JMP.&user32.MessageBoxA> ; \USER32.MessageBoxA 00401013 |. 6A 00 PUSH 0 ; /ExitCode = 0 00401015 \. E8 00000000 CALL <JMP.&kernel32.ExitProcess> ; \KERNEL32.ExitProcess 0040101A $- FF25 00204000 JMP DWORD PTR DS:[<&kernel32.ExitProcess 00401020 $- FF25 08204000 JMP DWORD PTR DS:[<&user32.MessageBoxA>]
I have kept the section addresses the same in both cases.
Can anyone please help on what it is that I am missing ?
My guess is that the host application has kernelbase someplace different, and thus the import table that I copy becomes invalidated, or that because the host application does not use MessageBoxA, and thus user32.dll is not loaded, the MessageBoxA does not exist. However when I try to import MessageBoxA ( and by extension user32.dll ) the memory allocated stops working
presumably due to the application having a .rsrc, .reloc, i.e.: Occupying the 0x00400000 address for itself.
I have tried relocating the base address of the host to an even lower memory ( 0x00200000 ), but it still won't work. I've tried manually removing the .rsrc and .reloc but the host app won't work without it in case I use MessageBoxA ( but will work without it, when deleting those sections, funny ).
When observing the actual debugged code, following the address 0040300E contains the exact same bytes in both cases: the function address of MessageBoxA ( which isn't imported in the host app ) and 00401015 has the function address of ExitProcess, which is imported by the host app. And that is why my guess is that this is an issue of simply loading and importing properly ( i.e. doing a dependency and import walk prior to executing the code section ).
I really want the opinion of a guru on this, cuz if it is simply a matter of getting the right kernel and function addresses, I can probably solve it through PEB.
Thank you
NOTE: I know that hardcoding addresses is bad practice. This is just a test app. A proper one would find the addresses from the crypted PE and use those instead.


20Likes
LinkBack URL
About LinkBacks
Reply With Quote
however in this particular case, the code fails on messagebox call, as actual address of messagebox was not fixed by your loader... first of all you need to add pe-headers at baseaddress, parse it, and fix every entry in import table... a lot of examples exists over the internet, just google it... something like 
