Jump to content
Tuts 4 You

Having problems writing my own exe packer


0ron

Recommended Posts

Posted

Hey guys!

Well I'm currently doing some experimenting with coding an exe packer/protector. I've got it working up to the point of loading and packing an executable written in C++ compiled with Dev-c++ and it works fine. Whenever I compile the same source code in Visual Studio 2008 pack it and run it I get "Not a valid executable file!" Anyone have an ideas? I can post the code if need be.

Thanks in advance :)

Posted

You're messing some non-optional directory up. My first guess would be the base relocations. If I remember correctly, MingW doesn't use to add them by default whereas MSVC does. The protected executables' code might be loaded at a different base with your stub jumping to a constant like 401000. You'd have to relocate the code you're adding as well (e.g., add relocation infos in the target's reloc directory).

Posted

Not quite sure how to do that. If I posted the code would you be willing to point out what I'm doing wrong?

Posted

Posting an "invalid" sample (before and after protecting it) might already be enough to spot your mistake. Given your answer I assume you're not fixing the relocations (which, see above, might be the issue but I'm just guessing here).

As for relocations, you could handle it this way: Compile your stub (e.g. the code you're going to add to the executable) as dll-file (exe is fine as well as long as you're building it with relocations; /FIXED:NO iirc). Then read its BASERELOC VA (DataDirectory), containing a nested IMAGE_RELOCATION array. You'll need to fix each HIGHLOW relocation to match the target executable's ImageBase and you'll need to add each relocation to the target executable's relocation directory.

This should be the basic approach. I think it was BigBoote's tutorial (to be found on t4y) which covers this technique more in-depth.

Posted

Turns out your completely right. I ran a few test with it and got it somewhat working. Anyways, here's my code for relocating the sections, can't seem to get this part working. Maybe you can tell me where I'm going wrong. :)

void perform_relocations (HANDLE hBinary)

{

PIMAGE_OPTIONAL_HEADER pOptionalHeader = GetOptionalHeader(hBinary);

// Check for relocation table

if (pOptionalHeader->DataDirectory[iMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress == 0)

return; // Relocation table does not exist

DWORD load_address = (DWORD)hBinary;

//compute offset

PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)load_address;

PIMAGE_NT_HEADERS nt_hdr = (PIMAGE_NT_HEADERS)(load_address + dos_header->e_lfanew);

//PIMAGE_NT_HEADERS32 nt_hdr = (PIMAGE_NT_HEADERS32)((unsigned char*)load_address) + dos_header->e_lfanew;

DWORD reloc_offset = load_address - nt_hdr->OptionalHeader.ImageBase;

//if we're where we want to be, nothing further to do

if ( reloc_offset == 0 )

return;

//gotta do it, compute the start

PIMAGE_BASE_RELOCATION ibr_current = (PIMAGE_BASE_RELOCATION)(pOptionalHeader->DataDirectory[iMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + load_address );

//compute the end

PIMAGE_BASE_RELOCATION ibr_end = (PIMAGE_BASE_RELOCATION)((unsigned char*)ibr_current) + pOptionalHeader->DataDirectory[iMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

//loop through the chunks

while (ibr_current < ibr_end && ibr_current->VirtualAddress )

{

DWORD RVA_page = ibr_current->VirtualAddress;

int count_reloc = (ibr_current->SizeOfBlock + sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);

WORD awRelType = (WORD)((unsigned char*)ibr_current + sizeof(IMAGE_BASE_RELOCATION));

for (int i = 0; i < count_reloc; ++i)

{

WORD wType = awRelType >> 12;

WORD wValue = awRelType & 0x0fff;

if (wType == IMAGE_REL_BASED_HIGHLOW)

{

//do it

*((DWORD*)(RVA_page + wValue + load_address)) += reloc_offset;

}

}

}

}

Posted (edited)

i dont understant your 1st post, packer doesnt run if you compile with vs2k8 or if you pack 2k8 exe with it.

for programs compiled with vs2k5 and 2k8 you need vc++ redistributable 2005 sp1 and 2008 sp1. aka msvcr8.dll and ..9.dll

next every this vs2005 and 2008 exe adds manifest at end of exe where it says it needs some name="Microsoft.VC80.CRT" version="8.0.50727.762"

and without manifest in exe or without manifest and policies installed in windows\winsxs exe will not load.

if you parse rsrc sections you must know to not compress it.

i hope this helps.

Edited by human
Posted

human, if I remember correctly the windows loader throws a different message when failing to load the CRT, not sure about the behavior when the manifest is missing though. But this could have been the issue as well, manifests didn't come to my mind. Never thought they'd have been that important. :sweat:

0ron, would you mind explaining what exactly isn't working properly? Does it throw an exception, does it relocate the code in a wrong matter, ...? My first guess would be that the section offset is different which is left to be aligned in your snippet (by the way, you can use code-tags in your posts to increase readability).

Example regarding the section offsets: You got your stub section at 0x401000, but you'd like to add it to an executable at 0x502000 (imagebase 0x500000). You'd not only have to add 0x100000 (imagebase aligning) but additionally 0x1000 since the new section RVA differs as well.

Hope this helps as well.

Posted

for programs compiled with vs2k5 and 2k8 you need vc++ redistributable 2005 sp1 and 2008 sp1. aka msvcr8.dll and ..9.dll

This isn't always the case, it depends on how you link to the CRT. If you are statically linking to the CRT then you do not need these DLLs as the functions used by the program are compiled into the exe itself. You only need the runtime files if you dynamically link.

next every this vs2005 and 2008 exe adds manifest at end of exe where it says it needs some name="Microsoft.VC80.CRT" version="8.0.50727.762"

and without manifest in exe or without manifest and policies installed in windows\winsxs exe will not load.

That also depends on how you link to the CRT. If you statically link to the CRT it is not required for you to have the manifest in the file and it will load without it.

Posted (edited)

human, if I remember correctly the windows loader throws a different message when failing to load the CRT, not sure about the behavior when the manifest is missing though. But this could have been the issue as well, manifests didn't come to my mind. Never thought they'd have been that important. :sweat:

0ron, would you mind explaining what exactly isn't working properly? Does it throw an exception, does it relocate the code in a wrong matter, ...? My first guess would be that the section offset is different which is left to be aligned in your snippet (by the way, you can use code-tags in your posts to increase readability).

Example regarding the section offsets: You got your stub section at 0x401000, but you'd like to add it to an executable at 0x502000 (imagebase 0x500000). You'd not only have to add 0x100000 (imagebase aligning) but additionally 0x1000 since the new section RVA differs as well.

Hope this helps as well.

Right now it's throwing an exception upon entering the while loop. I'm guessing it's an out of range exception or something similar. It's something to do with this code...

PIMAGE_BASE_RELOCATION ibr_current = (PIMAGE_BASE_RELOCATION)(pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + load_address);

Here's the exact message "Unhandled exception at 0x00ed9b89 in Searing Arrow.exe: 0xC0000005: Access violation reading location 0x00265000." I must be getting the load address wrong or the RVA or something. Any ideas?

Edited by 0ron
Posted (edited)

The line you pasted should be fine, though dependent on pOptionalHeader and load_address. Do some signature checking on the signatures/magic values in Dos-, Nt- and Optional Header. Why don't you read the Optional Header pointer directly from your IMAGE_NT_HEADER structure pointer? I don't think there's need for a special function just to retrieve something you are about to retrieve anyway. ;)

Do some error checking on your local variables (and parameters) and/or single step through the code to check them and let us know what you found. The next main suspect should be GetOptionalHeader function.

Edited by metr0
Posted

Ok I've done a little bit of testing. The problem is definitely in this code...


PIMAGE_BASE_RELOCATION ibr_current = (PIMAGE_BASE_RELOCATION)(pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + loadAddr);
PIMAGE_BASE_RELOCATION ibr_end = (PIMAGE_BASE_RELOCATION)((DWORD)ibr_current) + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;while (ibr_current < ibr_end && ibr_current->VirtualAddress)
{
DWORD RVA_page = ibr_current->VirtualAddress;
...

Upon trying to access the ibr_current->VirtualAddress it throws and exception because there is no value what so ever for the VirtualAddress or SizeOfBlock.

Posted (edited)

you have some variable or taking of it totally ****ed up.

please check first if load_address is bigger that 400000h

if you load small exe system can alloc free memory before imagebase of most exe's

but if its big exe then it will never happen and this means only one things, your variables are totally ****ed up.

if load_adress>400000h then 265000h impossible.

due it means your variables give negative value of less than FFFFFFFFFFE65000h. and this should be positive value.

Edited by human
Posted

Ok I must be complete stupid...I've re written it following an article I found. It runs fine and relocates anything compiled with MSVS 5 but its not working for MSVS 6 and up. Here's the code is there something else I need to add?

Thanks again =)

void perform_relocations (HANDLE hInstance) 
{
PIMAGE_DOS_HEADER pdosheader = (PIMAGE_DOS_HEADER)hInstance;
PIMAGE_NT_HEADERS pntheaders = (PIMAGE_NT_HEADERS)((DWORD)hInstance + pdosheader->e_lfanew);
//PIMAGE_SECTION_HEADER psectionheader = (PIMAGE_SECTION_HEADER)(pntheaders + 1);
PIMAGE_BASE_RELOCATION pbaserelocation;
DWORD dw;
PWORD pw;
PDWORD pdw;
TCHAR buff[1024]; if ((DWORD)hInstance != pntheaders->OptionalHeader.ImageBase && pntheaders->OptionalHeader.DataDirectory[15].Size)
{
pbaserelocation = (PIMAGE_BASE_RELOCATION)((DWORD)hInstance + pntheaders->OptionalHeader.DataDirectory[15].Size);
while (pbaserelocation->VirtualAddress != 0)
{
pw = (PWORD)((DWORD)pbaserelocation + sizeof(IMAGE_BASE_RELOCATION));
for (int x = 0; x < (pbaserelocation->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))/2; x++)
{
pdw = (PDWORD)(pbaserelocation->VirtualAddress + (DWORD)hInstance + (*pw & 0xFFF));
switch(*pw >> 12)
{
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGHLOW:
dw = *pdw;
dw = dw - pntheaders->OptionalHeader.ImageBase + (DWORD)hInstance;
if (dw < (DWORD)hInstance || dw > (DWORD)hInstance + pntheaders->OptionalHeader.SizeOfImage)
{
wsprintf(buff, (LPCWSTR)L"*pdw = 0x%08x", *pdw);
MessageBox(HWND_DESKTOP, buff, (LPCWSTR)L"Bad pointer:", MB_OK );
} else {
*pdw = dw;
}
break;
default:
case IMAGE_REL_BASED_HIGH:
case IMAGE_REL_BASED_LOW:
// these are not used in Win32 exe's
wsprintf(buff, (LPCWSTR)L"*pw = 0x%04x *pdw = 0x%08x", *pw, *pdw);
MessageBox(HWND_DESKTOP, buff, (LPCWSTR)L"Unexpected relocation type:", MB_OK);
break;
} pw++;
}
pbaserelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pbaserelocation + pbaserelocation->SizeOfBlock);
}
}
}
Posted

"Not working" isn't implying an exception any more I guess? How "doesn't it" work this time? Given the code's based on whatever article it should be fine then so it's rather the way you use it. The code you posted above does not seem to be aware of a possibly different section position (e.g. offset; see a previous post of mine describing the problem).

Sorry for my late reply. Nevertheless, I deleted your previous post merely containing the word "Anyone". ;)

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...