Using functions from an exe as a Dll



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;
}