Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VC9.0

Fetching User Land Virtual Address of an API from Kernel Land

4.83/5 (9 votes)
15 Dec 2008CPOL3 min read 26.9K   511  
How to get the virtual address of an API in a user land process from Ring0

Introduction

This article was posted by me sometime back, receiving good response. Now I am representing it with some revision. Sometimes it's quite desirable to get  the VA of a user land API from kernel. As a part of my System Research work, I found out an undocumented technique to achieve this. The main purpose of this article is to inform you about this undocumented but effective technique to fetch the virtual address of an API imported by a user land process.
For this purpose, we shall be making use of a Kernel mode device driver. Please note that this is an undocumented stuff and is a POC for educational purpose. Implement it at your own risk. Sorry, did not find much time, it has been tested on Windows XP & Windows 2k only.

Background

It is assumed that the reader has good understanding of assembly language programming. The source code is written in VC++ with embedded assembly language, one of my all time favourites. Why? Assembly is very much required for all those acrobatics .. you know. To get the VA of a user land API from kernel mode, we basically implement the method of grabbing the PEB of the user land process from Kernel land there by getting hold of the required base load VA of the required *.dll loaded by the process. Once we find this load base VA we can get hold of the IMAGE_EXPORT_DIRECTORY structure from where we find the particular function name and its corresponding VA.

Using the Code

Implementation is quite simple (really?). As briefed above, we first grab the PEB of the user land process of interest, which has the required *.dll loaded into its address space. Make sure that the *.dll exports the required API (whose VA is in question). For the sake of simplicity, let's take "WinExec" API which is exported by Kernel32.dll. As we know, most user land process imports APIs from Kernel32.dll (atleast one), making it omnipresent. Also with the humble knowledge that Kernel32.dll is always initialized second (you know which should be first then) in any given process. Again for the sake of simplicity, let's assume that our target process is "explorer.exe". Once we get the loaded base VA of the required *.dll (here kernel32.dll), we crawl towards the IMAGE_EXPORT_DIRECTORY field offset (NOTE: PE structures as well as PEB structures can change from version to version. Hey be cautious here, you are in kernel, otherwise happy BSOD). From here, we can get the values AddressOfNames array and AddressOfNameOrdinals (please refer to PE documentation for further details on this) and by looping through them we can find the exact API VA, which we require. Remember here we make use of another important and less documented Kernel API named KeStackAttachProcess, which lets us access the specific user land memory details from Kernel land by setting context to the user land process (internally sets the CR3 register accordingly, apart from some other checkings).

A word about the lone parameter of the procedure given below. This is the process id of the required process, here explorer.exe in our example. This we can very easily obtain from PEPROCESS structure. One of the attributes of PEPROCESS structure is UniqueProcesssId.
I am not explaining it here since you can find a couple of them on the Web.

C++
/////////////////////////////////////////////////////////////////////////////
//Developed by E. Murali Kartha
//CopyRight (C) E.M.Kartha, Must be run only from Kernel Mode.
//It's only for Educational purpose and is a POC.
//To be run on Intel 32 bit processors only.
//Here the below procedure is to get the function address of
//"winexec" (can be parameterized for any) from EAT of kernel32.dll
//For this first we get the peb of the process (here explorer.exe)
//then find the module base address of kernel32.dll and then get
//the EAT offset.-Kartha.
//Note the lone argument here pid can be obtained easily from the
//PEPROCESS structure variable like this PEPROCESS pSystemProcess->UniqueProcessId
ULONG get_FunctionAddress(UINT32 pid)
{
  PEPROCESS ep=NULL;
  PEB *_peb = NULL;
  ULONG peb;
  NTSTATUS ret;
  KAPC_STATE *ka_state=NULL;
  ULONG fADDR=0xffffffff;
  PIMAGE_EXPORT_DIRECTORY ped;//go through this structure declaration to find more.
  DWORD NumberOfFuncNames; //Number of functions in the function names array.
  PVOID hModuleBaseOfKernel32 = NULL; //for holding the retrieved loaded base VA.
  DWORD ImageExportDirectoryOffset; //start offset of the IMAGE_EXPORT_DIRECTORY
  PSTR pszModName;
  DWORD *AddressOfNames, *AddressOfFunctions;
  unsigned int index, i;


     if(PASSIVE_LEVEL != KeGetCurrentIrql()) {
          //ERR DISPLAY TBD
          return fADDR;
     }
     
     //ep will contain the EPROCESS address after this call
     ret=PsLookupProcessByProcessId((HANDLE)pid,&ep);
     if(!NT_SUCCESS(ret)) {
          //ERR DISPLAY TBD
          return fADDR;
     }
     
 //Get the PEB value here -Kartha.   
  peb = (DWORD)ep->Peb;
            
    //Note: Since PEB is a user mode address KeStackAttachProcess 
    //has to be called before trying
    //to get further fields from it-Kartha.
   if(peb) {
          ka_state=ExAllocatePoolWithTag(NonPagedPool,sizeof(KAPC_STATE),'trak');
    KeStackAttachProcess(&(ep->Pcb),ka_state);
  if ( !MmIsAddressValid((ULONG *) peb) )
  {
    //ERR DISPLAY TBD
    return fADDR;
  }
          __asm
    {
      push esi
   xor eax, eax
   mov eax, peb //move peb value to eax
   test eax, eax
   js find_kernel32_9x
   jmp find_kernel32_nt //just to satisfy VC++ compiler
   find_kernel32_nt:
   mov eax, [eax + 0x0c]
   mov esi, [eax + 0x1c]
   lodsd
   mov eax, [eax + 0x8]//get the Kernel32.dll loaded base VA
   jmp find_kernel32_finished
   find_kernel32_9x:
   mov eax, [eax + 0x34]
   lea eax, [eax + 0x7c]
   mov eax, [eax + 0x3c]
   find_kernel32_finished:
   mov [hModuleBaseOfKernel32], eax //move the base loaded address, for eg 0x7c800000
   pop esi
   //Finding the Export table RVA from PE Structure.
   //Remember currently eax carries the loaded module base V address of
   //kernel32.dll.
      xor ecx, ecx
   mov ecx, [eax+0x3c] //In PE IMAGE_DOS_HEADER +3c will give us the 
			// "offset to New Exe header"
      add ecx, eax //Remember the values which we retrieve are always RVA 
			//so add with base
   mov ecx, [ecx+0x78] //+0x78 takes as to the start of IMAGE_EXPORT_DIRECTORY
    add ecx, eax
            //ecx contains the VA of IMAGE_EXPORT_DIRECTORY
	   mov [ImageExportDirectoryOffset], ecx 
    }
  ped = (PIMAGE_EXPORT_DIRECTORY)ImageExportDirectoryOffset;
     if(ped)
  {
   pszModName = (PSTR)((PBYTE) hModuleBaseOfKernel32 + ped->Name);//Fetch the module name
   NumberOfFuncNames = ped->NumberOfNames;
  }
  //DPRINTA("\nModule Name is %s & Number of Func. Names is %d",pszModName, 
  //NumberOfFuncNames);
  //AddressOfNames will hold the RVA of an array which holds RVA's of all Function
  //Names-Kartha.
  AddressOfNames= (DWORD *)((PBYTE) hModuleBaseOfKernel32 + ped->AddressOfNames );
  //Loop through to find out each function, corresponding ordinal array value (index)
  //and then getting the function name. -Kartha.
  for(i = 0; i < NumberOfFuncNames; i++)
  {
       DWORD pThunkRVAtemp = (DWORD)((PBYTE) hModuleBaseOfKernel32 + *AddressOfNames);
       pszModName = (PSTR)pThunkRVAtemp;//address pointing to function name string.
    if(_strnicmp(pszModName,"winexec",7)==0)
    {
           WORD *AddressOfNameOrdinals = 
		(WORD *)((PBYTE) hModuleBaseOfKernel32 + ped->AddressOfNameOrdinals );
     AddressOfNameOrdinals += (WORD)i;//Go to the required address index 
				 //in the NameOrdinals array
     index = *AddressOfNameOrdinals;//get the value from this array location.
     AddressOfFunctions = (DWORD *)((PBYTE) hModuleBaseOfKernel32 + 
				ped->AddressOfFunctions );
     AddressOfFunctions += index;//get to the index location of Function address array.
     //Get the func addre RVA and add with base VA
     fADDR = (ULONG)((PBYTE) hModuleBaseOfKernel32 + *AddressOfFunctions);
     //DPRINTA("\nFound %s at loop index %d, Function address is 0x%08X",
     //pszModName, i, FuncAddress);
     break;
    }
    AddressOfNames++;
  }
  //DPRINTA("\n");
           
          KeUnstackDetachProcess(ka_state);
          ExFreePool(ka_state);
     }
     
     // cleanup
     ObDereferenceObject(ep);
     
     return fADDR;
}
/////////////////////////////////////  

Points of Interest

In a similar fashion, one can also get many more user land process details from Kernel.

History

  • 15th December, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)