Search This Blog

Showing posts with label API import. Show all posts
Showing posts with label API import. Show all posts

Tuesday, March 6, 2012

Faking KERNEL32.DLL - an Amateur Sandbox

As a part of my work (read "fun") of maintaining this blog, I am constantly checking the statistic information on traffic sources and keywords (it's nice to know that people are getting here via Google) in order to see whether my readers are getting what they are looking for (personally, I see no reason in simply "streaming my consciousness to the masses" as this is not the point of this blog). Sometimes, it gives an idea of what is missing but still related to system and low level programming.

A couple of days ago, I saw that someone was looking for a way to load and use fake KERNEL32.dll and I realized that this information has not yet been covered here. There is no source code for this article as I am a bit short on time to write it, but I will do my best to provide as much information as possible so, those who would want to try it would have no problem doing that.


KERNEL32.DLL
First notable thing about KERNEL32.dll is that it is always loaded, regardless of whether a running executable imports anything from it (this is briefly covered here). Same as NTDLL.dll (well, KERNEL32.dll imports from it). This library provides interfaces for interaction with deeper levels of "user land" part of the operating system for the running executable and some of other dynamic link libraries loaded into process' memory. 

Knowing all that, the first thought may be: "how are we going to fake it if all the rest depends on it?". The solution is easier than one could think at first. However, we should keep in mind, that some programs may import from NTDLL.dll directly, bypassing the KERNEL32.dll (which used to happen quite often in the world of malware), meaning that once you faked KERNEL32.dll, you may have to fake NTDLL,as well.


Preparations
We should start with writing a good old simple DLL/code injector. It is easier to dissect the victim process from inside. This is the simplest part and it is covered in this and this posts of this blog. Roughly saying, the injector should be able to create a victim process in suspended state by passing the CREATE_SUSPENDED process creation flag to CreateProcess API.

Writing the code or the DLL we are going to inject is a harder task as this code is intended to perform the tasks described below in order of execution.

Load Fake KERNEL32.dll
Let's assume, that we already have a ready to use fake KERNEL32.dll (we'll get back to creation of fake dll a bit later). This is quite simple - call LoadLibrary function from your code. One thing worth mentioning is that MSDN is not suggesting to use LoadLibrary in your DllMain function. Therefore, if you decide to use DLL Injection instead of code injection, then better use the approach described in "Advanced DLL Injection" article. 

Fake KERNEL32.dll should simply import all API's from the original one. Don't be mistaken - import, not forward it's exports at least as long as we are talking about API functions, but you may safely forward exported objects and variables to the original one.

Resolve Victim's Imports
By the time we get our code/DLL running inside the suspended victim process, all of it's imports should already have been resolved. What we still have to do, is to replace all API addresses exported from the original KERNEL32.dll with corresponding addresses in our fake one.

Here is a link to Microsoft's specifications of MS PE and MS COFF file formats - would be useful digging through imports and export.

Hide the Original KERNEL32.dll
While performing the aforementioned actions may be enough in case of a regular application, we should take some precautions in case of malicious code. My suggestion is to hide the original KERNEL32.dll by replacing its entry in the list of LDR_MODULE structures in PEB with the one describing our fake KERNEL32.dll, just like we would hide an injected DLL in the "Hiding Injected DLL in Windows" article.


Creation of Fake KERNEL32.dll
This may sound scary, but there is no need to worry (at least not too much). All that we need in order to create one, is a C compiler (or whatever high level language you prefer) and any assembler (I use FASM as usual).

Dump KERNEL32.dll to ASM Source
No, of course we do not have to disassemble the whole DLL and dump it to a corresponding Assembly source. Instead, what we have to do, is write a small application in high level language (you may try to do it in Assembly if you want) that would parse the export table of the original KERNEL32.dll and create a set of Assembly source files: one for code, one for data (if needed), one for import and one for export sections.

Want it or not, but the application has to generate a bit of Assembly code for at least transferring the execution flow to an API function in the original KERNEL32.dll. For example, if we have no interest in, let's say, ExitProcess, then our fake ExitProcess should look similar to this:

fake_ExitProcess:
   ; As we are not tracing/logging this function, we simply let the
   ; original ExitProcess shoot
   jmp dword [real_ExitProcess]

However, the code would be different for APIs of interest. For example, the CreateFileA API would be implemented like this:

fake_CreateFileA:
   ; We pass control to a fake CreateFileA, which is implemented in
   ; a separate DLL imported by our fake KERNEL32.dll
   ; Parameters are already on the stack, so we simply jump.
   ; Don't forget to declare the fake function as STDCALL 
   ; (remember #define WINAPI __declspec(stdcall) ? )
   jmp dword [our_CreateFileA]

The Assembly source file containing code for the import section would then contain the following:

section '.idata' import data readable writable
   library original, 'kernel32.dll',\            ;Import original KERNEL32.dll
           fake,     'our_dll_with_fake_api.dll' ;Import a DLL with fake APIs

   import original,\
      real_ExitProcess, 'ExitProcess'

   import fake,\
      our_CreateFileA, 'whatever you call it here'


Now, finally, we get to the export section's code:

section '.edata' export data readable
   export 'KERNEL32.dll',\              ;FASM does not care about what you type here, 
                                        ;so let's be fake to the end and pretend 
                                        ;to be KERNEL32.dll
      fake_ExitProcess, 'ExitProcess',\
      fake_CreateFileA, 'CreateFileA'


Finally the main source file, the one that would assemble all the rest together:

format PE DLL at 0x10000000

include 'code.asm'
include 'import.asm'
include 'export.asm'

section '.reloc' fixups data discardable

compile it with FASM and you have your fake KERNEL32.dll.


Implementation of Fake API
As it has been mentioned above, there are some functions we would want to trace. Those should have some custom implementation, preferably in a separate DLL (which would be loaded by Windows loader at the time it resolves our fake KERNEL32.dll's dependencies). Below is a diagram of the interactions between all the modules:
Interactions between modules involved in faking.


And here is an example of such fake API:

HANDLE WINAPI fake_CreateFileA(
                               LPCSTR lpFileName,
                               DWORD dwDesiredAccess,
                               DWORD dwShareMode,
                               LPSECURITY_ATTRIBUTES lpSA,
                               DWORD dwCreationDisposition,
                               DWORD dwFlagsAndAttributes,
                               HANDLE hTemplateFile)
{
   fprintf(log_file, "CreateFileA(list params here)\n", params);
   return CreateFileA(lpFileName, 
                      dwDesiredAccess, 
                      dwShareMode, 
                      lpSA, 
                      dwCreationDisposition,
                      dwFlagsAndAttributes,
                      hTemplateFile);
}

Of course, you may implement addition mechanisms within this DLL, e.g. let it communicate with another application via sockets or pipes, but this is deserves a separate article.


P.S. 
My personal suggestion is to insert more code into each function inside the fake KERNEL32.dll so that it would look more realistic to the victim process (should it try to do anything with it).



Hope this article was useful. 

See you at the next.






Friday, December 9, 2011

Flat Assembler "Export" Macro with Custom Ordinal Base

I have recently come across the need to build dynamic link libraries with custom ordinal base (different from 1). After searching the net and seeing lots of people writing their own export macros, I came to a conclusion that Occam's Razor principle still works here and decided to make simple modifications to the original export macro provided with FASM package. The modifications are marked with red.


; Macroinstruction for making export section
macro export dllname, ordinalbase, [label, string]
{ common
    local module,addresses,names,ordinal,count
    count = 0
   forward
    count = count+1
   common
    dd 0,0,0,RVA module, ordinalbase
    dd count,count,RVA addresses,RVA names,RVA ordinal
    addresses:
   forward
    dd RVA label
   common
    names:
   forward
    local name
    dd RVA name
   common
    ordinal: count = 0
   forward
    dw count
    count = count+1
   common
    module db dllname,0
   forward
    name db string,0
   common
    local x,y,z,str1,str2,v1,v2
    x = count shr 1
    while x > 0
     y = x
     while y < count
      z = y
      while z-x >= 0
       load v1 dword from names+z*4
       str1=($-RVA $)+v1
       load v2 dword from names+(z-x)*4
       str2=($-RVA $)+v2
       while v1 > 0
load v1 from str1+%-1
load v2 from str2+%-1
if v1 <> v2
break
end if
       end while
       if v1<v2
load v1 dword from names+z*4
load v2 dword from names+(z-x)*4
store dword v1 at names+(z-x)*4
store dword v2 at names+z*4
load v1 word from ordinal+z*2
load v2 word from ordinal+(z-x)*2
store word v1 at ordinal+(z-x)*2
store word v2 at ordinal+z*2
       else
break
       end if
       z = z-x
      end while
      y = y+1
     end while
     x = x shr 1
    end while }


As simple as that.
Hope this note may help someone.

Saturday, November 26, 2011

Advanced DLL Injection

It has been a while since my last article. Special thanks to those who decided to stay with me despite the long break and welcome to new readers!

In this article I am going to cover such a trivial (as it may seem) subject as DLL injection. For some reason, most of the tutorials on the web only give us a brief coverage of the topic, mostly limited to invocation of LoadLibraryA/W Windows API function in the address space of another process. While this is not bad at all, it gives us the least flexible solution. Meaning that all the logic MUST be hardcoded in the DLL we want to inject. On the other hand, we may incorporate all the configuration management (loading config files, parsing thereof, etc) into our DLL. This is better, but still fills it with code which is only going to run once.

Let us try another approach. What we are going to do, is write a loader (an executable what will inject our DLL into another process) and a small DLL, which will be injected. For simplicity, the loader will also create the target process. Being a Linux user, I used Flat Assembler and mingw32 for this task, but you may adjust the code for whatever environment you prefer.

A short remark for nerds before we start. The code in this article does not contain any security checks (e.g. checking correctness of the value returned by specific function) unless it is needed as an example. If you decide to try this code, you'll be doing this at your own risk.

So, let the fun begin.


Creation of target process

Let's assume, that the loader has already passed the phase of loading and parsing configuration files and is ready to start the actual job.

Windows provides us with all the tools we need to start a process. There are more then one way of doing that, but let us use the simplest and use CreateProcess API function. Its declaration looks quite frightening, but we'll make it as easy as possible:

   BOOL WINAPI CreateProcess(
      __in_opt    LPCTSTR lpApplicationName,
      __inout_opt LPTSTR lpCommandLine,
      __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,
      __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,
      __in        BOOL bInheritHandles,
      __in        DWORD dwCreationFlags,
      __in_opt    LPVOID lpEnvironment,
      __in_opt    LPCTSTR lpCurrentDirectory,
      __in        LPSTARTUPINFO lpStartupInfo,
      __out       LPPROCESS_INFORMATION lpProcessInformation
   );

We only have to specify half of the parameters when calling this function and set all the rest to NULL. This function has two variants CreateProcessA and CreateProcessW as ASCII and Unicode versions respectively. We are going to stick with ASCII all way long, so, our code would look like this (due to the fact that "CreateProcess" is rather a macro then function name, we should explicitly specify A version as some compilers tend to default to W versions):

CreateProcessA(nameOfTheFile, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInformation);

Don't forget to set the cb field of startupInfo to (DWORD)sizeof(STARTUPINFO), otherwise it would not work.

If the function succeeds, we get all the information about the process (handles and IDs) in the processInformation structure, which has the following prototype:

typedef struct _PROCESS_INFORMATION
{
   HANDLE hProcess;    //Handle to the process
   HANDLE hThread;     //Handle to the main thread of the process
   DWORD  dwProcessId; //ID of the new process
   DWORD  dwThreadId;  //ID of the main thread of the process
}PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

By now, the process has been created, but it is suspended. Meaning that it has not started its execution yet and will not until we call ResumeThread(processInformation.dwThreadId) telling the operating system to resume the main thread of the process, but this is going to be the last action performed by our loader.


Lancet

One may call it a shellcode, but it has nothing to do with the viral payload or any other malicious intent (unless, someone would say that breaking into address space of another process is malicious by definition). It is the code, that we are going to inject into the target process. It, theoretically, may be written in any language as long as it may be position independent and compiled into native instructions (in our case x86 instructions), but I prefer to do such things in Assembly language.

It is always a good idea, to think of what your code is intended to do before writing a single line of it, in this case it is a golden idea. The code needs to be small, preferably fast and stable as it  is a bit of a headache to debug once it has been injected.

There are two basic tasks that you would want to assign to this code: 
  • Load our DLL
  • Call the initialization procedure exported by our dLL
and one unavoidable condition - it has to be a function declared as ThreadProc callback, due to the fact that we are going to use the CreateRemoteThread function in order to launch it. The prototype of a ThreadProc callback function looks like this:

DWORD WINAPI ThreadProc( __in LPVOID lpParameter);

which means that it has to return a value of type DWORD (which is actually unsigned int). It accepts one parameter, which may either be an actual value (but you have to cast it to LPVOID type) or a pointer to an array of parameters. One more thing about this function (the last but not the least!) it is an stdcall function - WINAPI macro is defined as __declspec(stdcall). This means that our function has to take care of cleaning the stack before return. In our case it is quite easy, simply use ret 0x04 (assuming that size of LPVOID is 4 bytes).

Another important thing to mention - you will, obviously need to know how many bytes your function occupies in order to correctly allocate memory in the address space of the target process and move your code there. In addition to allocation of one block of executable memory for our function, you will also need to allocate one block for data - configuration settings to be passed to the injected DLL. It is easy to pass the address of the parameters as an argument to our ThreadProc.

The skeleton of the function would look like this:

lancet:
   push  ebp
   mov   ebp, esp
   sub   esp, as_much_space_as_you_need_for_variables
   push  registers_you_are_planning_to_use

   ;function body

   pop   registers_you_used
   mov   esp, ebp
   pop   ebp
   ret   0x04
lancet_size = $-lancet

The last line gives us the exact size of the function in bytes. The following is the source file template:


format MS COFF ;as we are going to link this file with our loader
public lancet as '_lancet'
section '.text' readable executable
lancet:
   ;our function goes here
   ;followed by data
   loadLibraryA  db 'LoadLibraryA',0
   init          db 'name_of_the_initialization_function',0
   ourDll        db 'name_of_our_dll',0
   kernel32      db 'kernel32.dll',0
lancet_size = $-lancet
public lsize as '_lancet_size'
section '.data' readable writeable
   lsize         dd lancet_size




So, what are we going to insert into the "function body"? First of all, as our code, once it is injected, has no idea of where in the memory it is, we should save our "base address" and calculate all the offsets relative to that address. This is done in a simple manner. We call the next address and pop the return address into our local variable.


   call @f
  @@:
   pop  dword [ebp-4]
   sub  dword [ebp-4], @b-lancet


that's it. Now the variable at [ebp-4] contains our "base address". Each time we want to call another function or access our data (strings with names, remember?) we should do the following:


   mov  ebx, [ebp-4]
   add  ebx, ourDll-lancet
   push ebx
   mov  ebx, [ebp-8] ;assume that we stored the address of LoadLibraryA at [ebp-8]
   call dword ebx


The code above is an equivalent of LoadLibraryA("name_of_our_dll") .


Now about the execution itself. Although, we now know where we are, we have no idea of what the address of LoadLibraryA is. There are, at least, two ways to get that address nicely. First has been described in my "Stealth Import of Windows API" article. The second is also interesting - PEB. Yes, we are going to access the Process Environment Block, find the LDR_MODULE structure which refers to KERNEL32.DLL and get its base address (which is also a handle to the library). Some may say that this way is not reliable, not stable and even dangerous, but I will say, that statements like these are not serious. We are not going to change anything in those structures. We are only going to parse them.


How do we find the PEB? This is quite simple. It is located at [FS:0x30]. Once we have it, we are on our way to PEB_LDR_DATA address, which is at PEB+0x0C.  In order to parse the PEB_LDR_DATA structure, we should declare the following in our Assembly code:


struc list_entry
{
   .flink dd ?   ;pointer to next list_entry structure
   .blink dd ?   ;pointer to previous list_entry structure
}


struc peb_ldr_data
{
   .length      dd ?
   .initialized db ?
                db ?
                db ?
                db ?
   .ssHandle    dd ?
   .inLoadOrderModuleList list_entry ;we are going to use this list
   .inMemoryOrderModuleList list_entry
   .inInitializationOrderModuleList list_entry
}


struc ldr_module
{
   .inLoadOrderModuleList list_entry ;pointers to previous and next modules in list
   .inMemoryOrderModuleList list_entry
   .inInitializationOrderModuleList list_entry
   .baseAddress   dd ?           ;This is what we need!
   .entryPoint    dd ?
   .sizeOfImage   dd ?
   .fullDllName   unicode_string ;full path to the module file
   .baseDllName   unicode_string ;name of the module file
   .flags         dd ?
   .loadCount     dw ?
   .tlsIndex      dw ?
   .hashTable     list_entry
   .timeDateStamp dd ?
}


I leave the implementation of the module list parsing function up to you. You just have to keep in mind that the string you are going to check are represented by the UNICODE_STRING structure (described in the article referenced above). Another thing to remember, is that it is better to implement case insensitive string comparison function.


Once you find the LDR_MODULE wich baseDllName is "kernel32.dll" you have its handle (simply in the baseAddress field). You may use the _get_proc_address function from the same article (mentioned above) in order  to get the address of the LoadLibraryA function. Having that address, you are ready to load your DLL (do the actual injection). Personal suggestion - do not put lots of code into the DllMain function. 


LoadLibraryA returns a handle to the newly loaded DLL, which you can use in order to locate you initialization function (remember it has to be exported by your DLL and preferably use the stdcall convention). After you _get_proc_address of your initialization function, call it and pass the address of the data block as a parameter (it was passed to our lancet function as a parameter on stack):


   push dword [ebp+8]  ;parameter passed to lancet is here
   call dword [ebp-12] ;assume that you stored the address of the initialization 
                       ;function here


That's it. Your code may now return. The DLL has been injected and initialized.


Injection
somehow, we have missed the exciting process of injection of our lancet code. Don't worry, I have not forgotten about it.


As I have mentioned above, we have to allocate two blocks - for code and data. This can be done by calling the VirtualAllocEx function, which allows memory allocations in the address space of another process.


LPVOID WINAPI VirtualAllocEx(
   __in     HANDLE hProcess,
   __in_opt LPVOID lpAddress,
   __in     SIZE_T dwSize,
   __in     DWORD  flAllocationType,
   __in     DWORD  flProtect
);


Use MEM_COMMIT as flAllocationType and PAGE_EXECUTE_READWRITE and PAGE_READWRITE for allocation of code and data block respectively. This function returns the address of allocated block in the address space of the specified process or NULL.


The WriteProcessMemory API function is used to copy your code and data into the address space of the target process.


BOOL WINAPI WriteProcessMemory(
   __in  HANDLE  hProcess,
   __in  LPVOID  lpBaseAddress,
   __in  LPCVOID lpBuffer,
   __in  SIZE_T nSize,
   __out SIZE_T*lpNumberOfBytesWritten
);


Once you have copied both the data and the code, you will want to call your thread function. The only way to call a function which resides in the memory of another process is by calling the CreateRemoteThread API.


HANDLE WINAPI CreateRemoteThread(
   __in  HANDLE hProcess, //the handle to our process
   __in  LPSECURITY_ATTRIBUTES lpThreadAttributes, //may be NULL
   __in  SIZE_T dwStackSize, //may be 0
   __in  LPTHREAD_START_ROUTINE, //the address of our code block
   __in  LPVOID lpParameter, //the address of our data block
   __in  DWORD  dwCreationFlags, //may be 0
   __out LPDWORD lpThreadId  //may be NULL
);


This function returns a handle to the remote thread, which, in turn, may be passed to the WaiForSingleObject API function, so that we can get notification on its return.

I decided not to cover the possibilities of what your DLL can do while attached to the target process and leave this completely up to you.




I hope this article was not too muddled and, may be, even helpful.


Have fun coding and see you at the next post.


Tuesday, October 4, 2011

Stealth Import of Windows API

At good old times, memory was an expensive resource and developers had to take care of the size of the programs they create. Imagine how hard they had to work before there were high level languages (like C), before compilers became smart enough to handle all size optimization issues. Speed was also among the concerns, as the hardware was not as fast as it is now. Another headache was the need to interact with the underlying operating system. Or, to be more precise, the need to implement the interfaces (at pre-libc times). Modern operating systems provide a built-in mechanism for that. This mechanism is called API - Application Programming Interface. This mechanism is a blessing and curse in one. On one hand it greatly simplifies the interaction with the OS, on the other hand it just makes your software more vulnerable to hackers and/or malware. In some cases the usage of APIs just gets exaggerated.

Let us check that with a simple example - the well known "Hello World" application in C created with Microsoft Visual C++ 2010 Express. The size of the executable is 27 kilobytes. The image of the executable has 7 sections while it could well be implemented with only two sections (one for code and one for data). 

The import directory looks even more exciting. Well, MSVCRT.dll is unavoidable as it is the C language interface to Windows Operating System. But there are 28 imported APIs from KERNEL32.dll and most of them seem to be placed here by mistake. GetTickCount for example. Do we care about timing when we only want to output a single string and leave? No, we do not.

Anyway, the issue of compiler's heuristic is outside the scope of this article. Let's concentrate on the API functions. In general, it is a great thing that lets us deal with application development without a need to implement every single interaction with Operating System and saves us a lot of time. Good on one hand, but bad on the other. Having the list of API functions for certain software provides a clear understanding of what, and what is even more important, how that software is intended to do. This may be good when you deal with malware research, but not as good when you are trying to protect your legitimate software from being hacked.

Unfortunately, there are thousands of software products that use IsDebuggerPersent API as their only protection mechanism. Isn't this ridiculous? No, it is not. It is rather sad, I'd say. Of course, there are numerous packers/cryptors/protectors out there, but the problem is that the more known your solution is, the more vulnerable it gets. There are some linkers that provide you with import section obfuscation abilities, but again, the problem is that they are known.

One of the possible solutions for this problem is the Stealth Import of APIs. This is a simple, powerful but underestimated technique. There are many developers, most of the developers, I should say, who believe that it is impossible to create and, even more important, launch a Windows executable without imports at all. "You need to import KERNEL32.dll at least!" - they would say. Unfortunately, not all of us are aware of the fact that both NTDLL.dll and KERNEL32.dll are automatically mapped into the process's address space regardless of the executable's import table. It is obvious, that having them loaded in memory, makes it possible to locate any API function and load any library should there be a need for it.  We may not know, but the operating system itself provides us with all the tools we may need for that. 


Get Handle of KERNEL32.DLL or NTDLL.DLL
Normally, if we need to get a handle to certain module that we know is loaded in memory, we simply call the GetModuleHandle API function or LoadLibrary in those cases where the module is not loaded in memory. But this is not the normal situation. We do not have access to those functions (yet). What should we do? The answer is simple - SEH or Structured Exception Handling mechanism (remember we said that the OS provides us with everything?).

All we need to do, is to get the address of the first exception handler in the chain of handlers. This chain is accessible through the first entry in the TIB (Thread Information Block) which is pointed by [FS:0]. This is as simple as

   ;Get the initial exception handler
   mov eax,[fs:0]


We now have the pointer to the last added EXCEPTION_REGISTRATION record and only need to iterate through the rest of the records in order to get to the first record which normally points to either KERNEL32.DLL or NTDLL.DLL (on Windows Vista, 7). The following code does exactly this:

.search_default_handler:
   cmp dword[eax],0xFFFFFFFF
   jz .found_default_handler
   ;go to the previous handler
   mov eax,[eax]
   jmp .search_default_handler 

The last (or to say it right - the first) record would have its prev field equal to -1 and its handler field ([eax+4] in our case) contains the address of the default exception handler located in one of the dlls mentioned above. What's next? 

Things are really easy if we are on Windows XP, as we have an address inside the KERNEL32.DLL and all we have to do is make it page aligned 

   mov eax,[eax+4]
   and eax,0xFFFF0000

then "scroll" the pages towards lower addresses and check each page for 'MZ' signature

.look_for_mz:
   cmp word [eax],'MZ'
   jz .got_mz
   sub eax,0x10000
   jmp .look_for_mz


Once we find the 'MZ' signature, we have the handle to the library. My advice - save it somewhere. The problem is - we still do not know which library this is (KERNEL32.DLL or NTDLL.DLL). In normal situation we would call GetModuleFileName, but, again, we are not in a normal situation. The solution is easy. Having the base address of the module we already have everything we may need. Does offset 0x3C look familiar? If not, then you should probably read this document. At this offset from the base address we have a WORD which is the offset of the PE signature ('PE\0\0') which is followed by the COFF header. We should check it anyway

   mov bx,[eax+0x3C]
   movzx ebx,bx
   add eax,ebx
   mov bx,'PE'
   movzx ebx,bx
   cmp [eax],ebx
   jz .found_pe

There is not much we can do if the zero flag is not set after comparison, which means that the PE signature has not been found. We just need to restore the stack to the state it was in when the process started, zero the eax register and execute ret instruction. This would terminate our process. Basically, this would mean that we have to review our previous code. On the other hand, if zero flag is set, this mean that we have successfully got to the COFF header. 

Now we need to locate the export directory. Its RVA (relative virtual address) and size should appear right after the Optional Header, which, in turn, appears right after the COFF header. We may skip the headers themselves and get straight to the export IMAGE_DATA_DIRECTORY entry

   add eax,0x78

Yes, as simple as that. Now [eax] points to the RVA of the export directory and [eax+4] to its size

typedef struct _IMAGE_DATA_DIRECTORY
{
   DWORD RVA;     //EAX points here
   DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

The next step is to read the RVA of the export table and add it to the image base address (the handle we obtained earlier)

   mov eax,[eax]
   add eax,[image_base_address]

Conrgatulations! We are finally at the Export Directory Table! 

Right now, we are interested in particular field of this table, namely the "Name RVA" which is located at [eax+0x0C] and points to NULL terminated ASCII string containing the name of this very library. The procedure is almost identical to the previous one

   mov eax,[eax+0x0C]
   add eax,[image_base_address]


We are one step from knowing what library this is. However, we have to implement a simple strcasecmp function. Why strcasecmp instead of strcmp? Just because strcasecmp is case-insensitive and we do not have to guess whether the library name is upper or lower, or even mixed case (like "KERNEL32.dll"). By respectively comparing the library name with strings 'kernel32.dll' and 'ntdll.dll' we identify the library.

We are in KERNEL32.DLL!
If this is the case, then we are lucky as we only have to locate the address of GetProcAddress API by parsing the export table (this deserves a separate article) or we may still use our custom version of GetProcAddress. We are able to obtain addresses of any API that is exported by KERNEL32.DLL as we have its handle. More than that, we are able to load additional libraries by first locating the LoadLibraryA or LoadLibraryW addresses. Basically, we are done.

We are in NTDLL.DLL...
This case is less desired but it may occur if our software runs on Windows Vista and higher. Needless to say that we have no access to GetProcAddress or LoadLibraryA (yet!).  Instead we have LdrLoadDll or LdrGetDllHandle API functions exported by NTDLL.DLL. Here are the prototypes of these functions:

NTSYSAPI NTSTATUS NTAPI LdrLoadDll(
     IN PWCHAR          PathToFile OPTIONAL,
     IN ULONG           Flags OPTIONAL,
     IN PUNICODE_STRING ModuleFileName,
     OUT PHANDLE        ModuleHandle);

Let's skip the optional values as we may safely set them to 0. The first non optional parameter is ModuleFileName, but what does PUNICODE_STRING mean? It is a pointer to a structure, that describes a UNICODE string. This structure may be easily build on stack. Here is its declaration:

typedef struct _LSA_UNICODE_STRING
{
   USHORT Length;
   USHORT MaximumLength;
   PWSTR  Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRNIG, UNICODE_STRING, *PUNICODE_STRING;

Length - this field specifies the length, in bytes, of the string pointed by Buffer, not including the terminatinf NULL;
MaximumLength - total size, in bytes, of the memory allocated for Buffer;
Buffer - pointer to a wide-character string (like 'K', 0, 'E', 0, 'R', 0, 'N', 0, 'E', 0, 'L', 0, '3', 0, '2', 0, '.', 0, 'D', 0, 'L', 0, 'L', 0, 0, 0).

The PHANDLE ModuleHandle is the pointer to a location in memory where the function should store the handle to a loaded library.

Now, let's turn to LdrGetDllHandle

NTSYSAPI NTSTATUS NTAPI LdrGetDllHandle(
     IN PWORD           pwPath OPTIONAL,
     IN PVOID           Unused OPTIONAL,
     IN PUNICODE_STRING ModuleFileName,
     OUT PHANDLE        pHModule); 

Let's skip the optional parameters again. Especially the "Unused" one.
ModuleFileName - is a pointer to UNICODE_STRING structure which describes the name of the DLL;
pHModule - a pointer to a location in memory where the function should store the result (the handle of the DLL).

We still have to implement a custom GetProcAddress function in order to retrieve these. The sample code is located at the end of this article. 

Once we have the addresses of these functions, we should first try to get the module handle of the 'KERNEL32.dll' by calling the LdrGetDllHandle and if it fails, we then try to load it with LdrLoadDll. If both functions fail - restore the stack, execute ret and check your code.

Once we have the module handle of the KERNEL32.DLL, we are free to use the API functions it exports (e.g. GetProcAddress, LoadLibrary, etc.).

As you can see, this technique is simple in deed. More than that it allows you to implement additional protection mechanisms like code obfuscation, SEH usage and many more in order to protect one of the most hack-sensitive parts of your software - the import section.

Hope this post was helpful. See you at the next post!

P.S. Custom GetProcAddress function. It is far from being perfect but is enough for what we need it.


;This is our custom GetProcAddress
;get_proc_address(HMODULE hModule, PCSTR procName)

if used _get_proc_address
baseAddress=         -4
numberNamePointers=  -8
exportAddressTableVA=-12
namePointerVA=       -16
ordinalTableVA=      -20
ordinalBase=         -24

_get_proc_address:
        push ebp
        mov ebp, esp
        sub esp,24
        push ebx ecx edx esi edi ebx
        mov esi,[ebp+8]                 ;ESI -> base address
        mov ebx,esi                     ;EBX is going to point to export table
        push ebx
        mov bx,[ebx+0x3C]
        movzx ebx,bx
        add ebx,[esp]
        add esp,4

        ;Set variables
        mov [ebp+baseAddress],esi
       add ebx,0x78                 ;now EBX points to the export table directory entry
        mov ebx,[ebx]
        add ebx,[ebp+baseAddress]
        mov eax,[ebx+24]                ;number of name pointers
        dec eax                         ;This is done in order to compare 0 based index
        mov [ebp+numberNamePointers],eax
        mov eax,[ebx+16]
        mov [ebp+ordinalBase],eax       ;ordinal base
        mov eax,[ebx+28]
        add eax,[ebp+baseAddress]
        mov [ebp+exportAddressTableVA],eax       ;VA of address table
        mov eax,[ebx+32]
        add eax,[ebp+baseAddress]
        mov [ebp+namePointerVA],eax     ;VA of name pointers table
        mov eax,[ebx+36]
        add eax,[ebp+baseAddress]
        mov [ebp+ordinalTableVA],eax    ;VA of ordinal table

        ;Reset offset counter
        xor ecx,ecx
    .search_loop:
        push ecx
        shl ecx,2                       ;Offset must be multiple of 4, so we multiply counter by 4
        mov ebx,[ebp+namePointerVA]
        add ebx,ecx                      ;EBX now points to one of the exported API functions name pointer
        mov ebx,[ebx]
        add ebx,[ebp+baseAddress]
        push ebx dword[ebp+12]
        call _strcmp
        test eax,1
        jnz .found_api_name
        pop ecx
        cmp ecx,[ebp+numberNamePointers]
        jz .not_found_a_thing
        inc ecx
        jmp .search_loop

    .found_api_name:
        pop ecx
        ;We now have the offset of the api in ECX register
        mov ebx,[ebp+ordinalTableVA]
        shl ecx,1
        add ebx,ecx                     ;EBX now points to the correct ordinal value
        mov bx,[ebx]
        movzx ebx,bx                    ;EBX contains an offset into the export address table
        shl ebx,2                       ;Multiply it by 4
        mov eax,[ebp+exportAddressTableVA]
        add ebx,eax
        mov eax,[ebx]
        add eax,[ebp+baseAddress]       ;now the EAX register contains the address of the exported function

    .out:
        pop ebx edi esi edx ecx ebx
        mov esp,ebp
        pop ebp
        ret 8

    .not_found_a_thing:
        xor eax,eax
        jmp .out
end if