Using functions from an exe as a Dll
SkimLite On
Sometimes when you are messing around you will want to be able
to call functions from a compiled executable on your own user
supplied data. Several different ways to do this come to mind
including but not limited to:
- inline patching - is usually used for permanent
changes and is a bit cumbersome to just use off the cuff
testing.
- debugger manipulations - are usually the first choice
because they are very easy to accomplish, however to use them
repeatedly over and over again can get be quite time consuming.
- dll injection - see blog post on
using an embedded dll to turn malware into an IPC decryption server for dealing with super complex decoding schemes.
- byte buffers - not bad for small subs that use all
relative jumps and do not call any api or other internal
functions. These are some pretty big limitations and can quickly
rule this technique out for more complex layouts.
- inline asm ports - again not bad, although can be kind of
annoying to format, cleanup and get running as nature intended again.
again usually limited to key parts of functions you wish to explore.
- emulation envirnoments - see this blog post
for using libemu x86 emulator to embed functions at arbitrary offsets and run them.
So what we need is a way to use compiled functions from an
exe on our own data. These functions need to be able to be
called without modification, and must work regardless of usage
of other internal function calls, or API usage.
So basically what we are going to do, is use code that was
compiled as an exe, as a dll.In terms of the PE structure the
binary differences between an exe and a standard dll are not that
great. However we will need to:
- Work around the missing export table
- patch the entry point to mimic a dllmain
- set the dll attribute in pe header
Thanks to the guys over at the openrce.org
message board for also pointing out that if the relocations were stripped for the target exe,
we will also have to ensure that it can load at its preferred base address or else LoadLibrary
will fail.
For our test, we will create a very basic executable that mimics a
string decoder as you might find in a simple virus which attempts
to obscure strings it uses for its routines.
Note that the target executable for techniques such as this are
really aimed at straight C apps.
So our simple target looks like this:
void decode(char *s){
int i, l = strlen(s);
for(i=0;i<l;i++){
s[i] = s[i] ^ 0x21;
}
}
void main(){
char *e ;
e = (char*)malloc(26); //heap allocated memory so writable
strcpy(e,"}rNGUV@SD}lHBSNRNGU}bTSSDOUwDSRHNO}sTO");
decode(e);
printf("%s\n\n",e);
}
Locating decoders is the easy part, figuring out how they
operate and coding equivalents can quickly get beyond timely RE to reverse.
This technique is a stab at finding a way to quickly reuse what is already
compiled, debugged, and in use, for our own purposes.
The first step, is to disassemble the target and calculate the target functions
rva. You can do this either manually, or by using a pe calculator such as the
one that comes with LordPE. If you dont know how to calculate it manually
you can read through some code here
The next step, is to try to figure out the functions prototype and calling convention.
Thats an exercise left up to the experience of the reader and beyond the scope of
this article. When in doubt, watch the program executing normally from your debugger
and follow the arguments on the stack.
Next we embed a simple dllmain at the programs entry point.
If you are feeling very lazy, and know the routine you are after does not
use any API calls, it seems you can also just zero out the entrypoint field
and it will probably still load the image into memory.
The following represents a simple dllmain.
C7 C0 01 00 00 00 mov eax, 1
C2 0C 00 retn 0Ch
You must also watch out to make sure the decoding mechanism
does not need any variables, character tables or other data initialized to be used
successfully. You will have to determine such things and their work arounds on a
case by case basis.
Now you can just rename the exe to .dll and you are pretty much ready to go.
With all of these things in place, we can now call our target function with a small loader
program such as this:
void main(){
char *e;
void (*myFxPointer)(char*);
int fx_rva = 0x1020;
int dllBase = LoadLibrary("dll_exe.dll");
if(dllBase<1){
printf("Loadlibrary Failed");
return;
}
printf("DllBase = 0x%x\n", dllBase);
_asm{
mov eax, dllBase
add eax, fx_rva
mov myFxPointer, eax
}
e = (char*)malloc(30);
strcpy(e,"}rNGUV@SD}lHBSNRNGU}bTSSDOUwDSRHNO}sTO");
myFxPointer(e);
printf("Decoded string = '%s'\n\n",e);
getch();
}
In the samples included, I have based the loader at 0x800000 to avoid possible
conflicts with where the exe wants to load. If the relocations stripped attribute
is set in the pe header, you will have to make sure that the exe loads where it wants
or else the LoadLibrary will fail.
If another dll pops into the picture when the loader
is started at this address, you may have to rebase the dll to clear the way
for the
target module. If you find something like a .nls file taking up your target load address
you can call UnMapViewOfFile(baseAddress) to clear the way for it.
For a quick run through of the code, we first load our "dll" into memory with
the conventional LoadLibrary call.The Handle value loadlibrary returns is actually
also the dlls base load address, which may vary.
Since we dont have an export table to call GetProcAddress on, we use the static rva
value to calculate its location for our self. The function offset will be the dlls
base load address + the functions relative virtual offset. (or you can just use the static
address if you are forced to have it load at its original base)
Once the functions location is calculated, we then set our function pointer to this
address. All three of these things happen in the following lines of code:
_asm{
mov eax, dllBase
add eax, fx_rva
mov myFxPointer, eax
}
Remember that you must define your function pointer with the prototype that
you figure out from RE. Here it is declared as a cdecl function that returns no
value and takes a char* as its only argument:
void (*myFxPointer)(char*);
With all of that in place, all we have left to do is actually call it just like any
other function with our own test data:
e = (char*)malloc(30);
strcpy(e,"}rNGUV@SD}lHBSNRNGU}bTSSDOUwDSRHNO}sTO");
myFxPointer(e);
printf("Decoded string = '%s'\n\n",e);
Anyway, this was more of a fun experiment than anything else. At the
very least though I think this will be quite usable for and endless array of
dirty hacks and can imagine some times where it may be downright handy.
I should also be blunt in pointing out that this is a dirty
hack, so dont be to suprised if you find places where it blows up.
Here are the source/binaries if you want to play with it
yourself.
Have fun
-dzzie
Another update to this article. If the code you are planning on analyzing does not make use of any api
calls, such as a decoding function there is another technique that might actually be easier. I ended up
using this trick today when i had to work on a memory dump which couldnt be loaded with loadlibrary.
Actually I finally got it to load, but the base address would never be where it expected to be, so..
You can use VirtualAlloc to allocate a buffer exactly where you want it to be, then you can read in
the entire raw file. As long as there are no relocations to process and no api then you can directly call
into it like a shellcode buffer. If it does use an api or two, you can do some restorations manually with
your loader.
Pretty simple and works with files that LoadLibrary will barf on without fixups like memory dumps.
int LoadFileAtAddress(char* filename, unsigned int address, unsigned int padding){
DWORD l;
OFSTRUCT o;
HANDLE h = (HANDLE)OpenFile(filename, &o , OF_READ);
if(h == INVALID_HANDLE_VALUE ){
printf("Could not open file %s\n", filename);
return 0;
}
int bufsz = GetFileSize(h,NULL);
if( bufsz == INVALID_FILE_SIZE){
printf("Could not get filesize\n");
CloseHandle(h);
return 0;
}
printf("Allocation Base: %x Size: %x Padding: %x End: %x\n", address,bufsz, padding, address+bufsz+padding);
bufsz += padding;
printf("Trying to clear way for alloc...\n");
int x = address;
while(x < address+bufsz+padding){
UnmapViewOfFile( (void*)x );
FreeLibrary( (HMODULE)x );
x += 0x1000;
}
void* mem = VirtualAlloc((void*)address,bufsz, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if((int)mem!=address){
printf("Could not obtain desired base address...");
CloseHandle(h);
return 0;
}
ReadFile(h, mem, bufsz ,&l,0);
CloseHandle(h);
return (int)mem;
}
|