How to retrieve every function name in a loaded application (executable or dll) - Part I
I have used Delphi code to implement this, so pseudocode will be near to Delphi...
1. Create an instance of tagMODULEENTRY32:
tagMODULEENTRY32.dwSize := SizeOf(MODULEENTRY32);
2. Get process information inside the sanapshot:
hSnapShot := CreateToolHelp32SnapShot(TH32CS_SNAPMODULE,lpProcessInformation.dwProcessId);
3. Retrieve first module loaded in your process:
Module32First(hSnapShot,tagMODULEENTRY32)
If the function succeeds, you have got some module information, like this:
tagMODULEENTRY32.modBaseAddr
tagMODULEENTRY32.modBaseSize
Store it in an struct inside an array, and then, start a loop to get the others processes info:
Module32Next(hSnapShot,tagMODULEENTRY32)
If the function succeeds, you have got several module information, like this:
tagMODULEENTRY32.modBaseAddr
tagMODULEENTRY32.modBaseSize
The loop ends when the previous function doesn't succeed.
4. For everyone of the modules previously stored in your array, get the export table. This will be used later.
You just need to read PE header, and get the export table rva & size from data directories.
Get the IMAGE_EXPORT_DIRECTORY structure and store it in an array for every process loaded.
5. Get the import table addresses minimum and maximum bounds. There could be several methods to do it. I prefer reading the entire code section and get all references to imported functions by analyzing every opcode like this table shows:
Code:
FF35 | PUSH [virtual address]
A1 | MOV EAX,[virtual address]
8B0D | MOV ECX,[virtual address]
8B15 | MOV EDX,[virtual address]
8B1D | MOV EBX,[virtual address]
8B25 | MOV ESP,[virtual address]
8B2D | MOV EBP,[virtual address]
8B35 | MOV ESI,[virtual address]
8B3D | MOV EDI,[virtual address]
FF15 | CALL [virtual address]
FF25 | JMP [virtual address]
For everyone of the addresses, validate that it is inside memory of your main process, then get the handle, and then try to find its function name or ordinal, as follows. If you succeed, it belongs to IAT. Get with a clever algo this:
Mimimum IAT address RVA
Maximum IAT address RVA
For every iteration, you only need to validate that a handle is valid if the RVA of the handle is out of minimum and maximum RVA previosly computed.
To find a valid handle for imported function, first check in a loop that it belongs to any of the processes loaded:
Code:
for i := 0 to Length(TableProcess) - 1 do
if (hndFunc > TableProcess[i].modBaseAddr) AND
(hndFunc < (TableProcess[i].modBaseAddr + TableProcess[i].modBaseSize)) then
begin
GetFunctionNameAndOrdinal(...)
end;
To be continued...
Nacho_dj