Blacksoulman
asked on
Api Spying
Im trying to make one function that will work for all hooked apis in the iat.
Heres what i want to do.
Save original api address so i can call it later. Replace original api address with my own function address.
Now when an api is called it jumps to that ONE function address.
Call the original api i saved somewhere(hardpart). How and where would i save this address?
ret
Ok the question is not how do i patch the iat. I know how to do that.
It is what this ONE "dummy" function should do in order to call the original api and work for ALL apis in the iat. Something like...
Procedure GenericHookProc;stdcall;
begin
asm
//call original api that i saved somewhere <<how do i do this?
//get the api function name
ret //resume code in the module
end;
end;
Oh and if any of you know of an api spy that works for SYSTEM process in XP lemme know. None out there work for SYSTEM processes, that ive seen.
Heres what i want to do.
Save original api address so i can call it later. Replace original api address with my own function address.
Now when an api is called it jumps to that ONE function address.
Call the original api i saved somewhere(hardpart). How and where would i save this address?
ret
Ok the question is not how do i patch the iat. I know how to do that.
It is what this ONE "dummy" function should do in order to call the original api and work for ALL apis in the iat. Something like...
Procedure GenericHookProc;stdcall;
begin
asm
//call original api that i saved somewhere <<how do i do this?
//get the api function name
ret //resume code in the module
end;
end;
Oh and if any of you know of an api spy that works for SYSTEM process in XP lemme know. None out there work for SYSTEM processes, that ive seen.
ASKER
Thank you. Im starting to digest it especially that last part because i was already thinking of passing some parameter related to the old api to my hook function by pushing an extra parameter on the old api stack(eax). And then when it jmps to my code block pop eax back off and call the api. Is this what you were saying? If not where are you putting the apiindex or func pointer.
I am by far no asm guru at all. So if anyone has any more ideas im going to need to see the code, with comments, because im not sure i could implement it myself.
I am by far no asm guru at all. So if anyone has any more ideas im going to need to see the code, with comments, because im not sure i could implement it myself.
Well, if you are no asm guru, you should better forget doing this... :-) Hooking APIs without knowing how many parameters and which calling convention the APIs have is quite difficult. As Russell said, you need to make sure that the registers stay correct. Also you must not change the stack, cause some parameters may lie there, too. All this stuff is only possible by writing asm code very carefully.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
i will give you points but im just wondering how else i could spy on all apis besides a centralized hooked procedure and a hook function for each api?
Well... this IS about the only way. (Short of declaring seperate hook functions for each api call you wanted to hook).
If you take a look at the code I gave you, it does a CALL (vs JMP) to the generic hook procedure, so I ** believe** you should be ok in regards to the stack and registers. Also, I hope the "patch" code makes sense now, and why it is needed. Without it, eg: if the redirection from the api call just entered into your generic hook function, you would have a hard time:
1.) Figuring out where the call came from.
2.) Where the jmp needs to go.
Using the code above, you could use a TList or other suitable container to store record/code information you build up while walking an IAT for a module. You could also split the EAX register into a high/low word; storing the module index (hopefully you would never have more than 65535) in one part, and the function index in the other.
Just some thoughts,
Russell
ASKER
alright just a follow up question
everything works fine up until
Pointer(Pointer(Pointer(@l pCode^[2]) ^)^):=Patc hTable[dwI ndex].lpPa tchAddr;
here it crashes and im not really sure what this is for anyway. I mean youve already used lpcode[2] with MoveMemory(@lpCode^[2], @dwIndex, 4);
So, if i leave this line out it calls the patch address in memory and the GenericHookProc but doesnt call the original function.
everything works fine up until
Pointer(Pointer(Pointer(@l
here it crashes and im not really sure what this is for anyway. I mean youve already used lpcode[2] with MoveMemory(@lpCode^[2], @dwIndex, 4);
So, if i leave this line out it calls the patch address in memory and the GenericHookProc but doesnt call the original function.
Sorry for the delay in responding....
1.) What is the "crash" exactly? I am guessing that you mean an exception; perhaps you could elaborate further. Also, can you add a check for the result of VirtualProtect(...) which is called right before to make sure that it is succeeding.
2.) lpCode WAS used previously, but if you check the code again, you will see that the pointer var is reset before being used in this scenario.
// Set the absolute jump in the IAT to our patch memory
lpCode:=lpBase[dwIndex]; <----- Set to new address
VirtualProtect(lpCode, 6, PAGE_EXECUTE_READWRITE, dwProtect);
Pointer(Pointer(Pointer(@l pCode^[2]) ^)^):=Patc hTable[dwI ndex].lpPa tchAddr;
This is the jump to the old (original function), which is why it is not getting called if you leave this piece out. Anyways, let me know the result of (1) above and I can help you further.
Russell
1.) What is the "crash" exactly? I am guessing that you mean an exception; perhaps you could elaborate further. Also, can you add a check for the result of VirtualProtect(...) which is called right before to make sure that it is succeeding.
2.) lpCode WAS used previously, but if you check the code again, you will see that the pointer var is reset before being used in this scenario.
// Set the absolute jump in the IAT to our patch memory
lpCode:=lpBase[dwIndex]; <----- Set to new address
VirtualProtect(lpCode, 6, PAGE_EXECUTE_READWRITE, dwProtect);
Pointer(Pointer(Pointer(@l
This is the jump to the old (original function), which is why it is not getting called if you leave this piece out. Anyways, let me know the result of (1) above and I can help you further.
Russell
ASKER
i couldnt find any contact info for you whatsoever . Do you have a website or email address youd be willing to share?
ok i use this for the lpBase instead:
ImportCode := Pointer(dword(ImageDosHead er) + ImageImportEntry^.FirstThu nk);//VA of function
So i dont need the array of apis. What do i put in place of lpBase[] ? If i already have the address from the ImageDescriptor. I tried something like this.
PatchTable[dwIndex].lpAddr := ImportCode;//old api address
//i don't know what to put in place for this..
PatchTable[dwIndex].lpApiA ddr:=Point er(Pointer (Pointer(@ PByteArray (lpBase[dw Index])^[2 ])^)^);//w hat is at [2]?? The third api pointer?
I already stored the old api address in lpAddr. So i set everything that used lpAddr with ImportCode. At the end it looks like
ImportCode^ := lpCode;//old api address now points to patch mem
VirtualProtect(lpCode, 6, PAGE_EXECUTE_READWRITE, dwProtect);
Pointer(Pointer(Pointer(@l pCode^[2]) ^)^):=Patc hTable[dwI ndex].lpPa tchAddr;
//dont know what do with this last line either.
What exactly is at the 3rd byte of lpCode?
And since i cant list every function manually i tried to get the import name table. I opened another question here: https://www.experts-exchange.com/questions/20925997/Import-Name-Table.html
those points are waiting for you. :)
ok i use this for the lpBase instead:
ImportCode := Pointer(dword(ImageDosHead
So i dont need the array of apis. What do i put in place of lpBase[] ? If i already have the address from the ImageDescriptor. I tried something like this.
PatchTable[dwIndex].lpAddr
//i don't know what to put in place for this..
PatchTable[dwIndex].lpApiA
I already stored the old api address in lpAddr. So i set everything that used lpAddr with ImportCode. At the end it looks like
ImportCode^ := lpCode;//old api address now points to patch mem
VirtualProtect(lpCode, 6, PAGE_EXECUTE_READWRITE, dwProtect);
Pointer(Pointer(Pointer(@l
//dont know what do with this last line either.
What exactly is at the 3rd byte of lpCode?
And since i cant list every function manually i tried to get the import name table. I opened another question here: https://www.experts-exchange.com/questions/20925997/Import-Name-Table.html
those points are waiting for you. :)
ASKER
alright i dont know how the other answer solves my question to this one.
Is the return address of the api function at lpcode[2]? I took off one of the pointer() in both and it didnt crash but it didnt do anything either.
Is the return address of the api function at lpcode[2]? I took off one of the pointer() in both and it didnt crash but it didnt do anything either.
Blacksoulman,
I have really been swamped which has made it hard to find "free" time to keep doing further research with this question, but I do want to address your questions.
First, the example code I gave you made use of the IAT, but in an indirect way. (If you already know this stuff, then I apologize, but your questions have been in regards to the code at [2]..).
You already know the IAT is a pointer to an array of pointers, for example (example only):
IAT - > of $00442274
$00442274 [$77E7A29B] // GetTickCount
$00442278 [.........] // GetVerison
... [.........]
The example code was extracting the IAT info through the use of the function address's, which translate to this in the exe.
@GetTickCount = FF2570224400 // 6 bytes of code in memory
Translated to asm, this is:
JMP DWORD PTR [$00442274]
So my code was skipping the 2 bytes that make up the absolute jump. The remaining 4 bytes make up the address of the IAT entry. (which hold the actual function address). The code then cast lpCode[2] (3rd byte) as a PByteArray, then as Pointer, to get the contents of the IAT memory, which in this case is the actual function address of $77E7A29B. The contents are then replaced with the patch code memory.
So, leading on....
I have spent some time working with IAT parsing. At first I tried to use the FindFirst/Next that I gave you, but then realized that some function/library names get duped, thus I ended up double-hooking them. Not pretty ;-). So I revamped a unit to handle the IAT info in a somewhat more elegant fashion. Plus, it is able to check to see if a function has already been hooked by determining if the GetProcAddress is the same as what the the IAT^ contents hold. I also added a hooking function in there as well.
Now to your problem....
I have revamped the code to hook by modifiying the IAT directly. One note though: this should probably not be done on the system dlls for Win95/98/ME systems. (I'm hoping that Madshi would be willing to offer his expert advise on this?, for points of course).
Also, it is also not advisable to hook EVERY imported function call, as your generic hook might end up being called infinitely if something in the hook used an api that was hooked. For example, say you hooked MessageBox and then called MessageBox in the GenericHookProc. Oops ;-). You get the drift I'm sure....
Anyways, that is why the IAT code I did was pertinent to this. With the IAT code, I/you have the ability to walk all imported dlls, as well as their functions, by NAME no less.
If you have any other questions/problems then let me know, and I apologize ahead of time for any delays in my future responses.
Regards,
Russell
----
unit IAT;
//////////////////////////
// IAT utility functions
//////////////////////////
interface
uses
Windows, SysUtils, Classes;
//////////////////////////
// Data Types
//////////////////////////
type
IndirectAddr = ^Pointer;
//////////////////////////
// Import description info structure
//////////////////////////
type
PImageImportDesc = ^TImageImportDesc;
TImageImportDesc = packed record
FuncNameList: DWORD;
TimeDateStamp: DWORD;
ForwarderChain: DWORD;
Name: DWORD;
FirstThunk: DWORD;
end;
//////////////////////////
// Visible functions from this unit
//////////////////////////
function IsIATInfoLoaded: Boolean;
function LoadIATInfo: Boolean;
function UnloadIATInfo: Boolean;
function GetIATLibraryCount: Integer;
function GetIATFunctionCount(Librar
function GetIATLibraries(List: TStrings): Boolean;
function GetIATFunctions(LibraryNam
function GetIATFunctions(LibraryInd
function GetIATFunctionAddr(Library
function IsIATFunctionHooked(Librar
function HookIATFunction(LibraryNam
implementation
//////////////////////////
// Protected variables
//////////////////////////
var
Libraries: TStringList = nil;
Loaded: Boolean = False;
// Helper function for parsing name tables
function NextFunctionName(lpName: PChar): PChar;
begin
result:=StrEnd(lpName);
while (result^ = #0) do Inc(result);
end;
// Determines if IAT info has already been loaded
function IsIATInfoLoaded: Boolean;
begin
// Return loaded state
result:=Loaded;
end;
// Get the count of imported libraries
function GetIATLibraryCount: Integer;
begin
// Make sure we are loaded
if not(Loaded) then LoadIATInfo;
// Result is the count of libraries
result:=Libraries.Count;
end;
// Hook the IAT by installing a new function pointer into the table
function HookIATFunction(LibraryNam
var lpIAT: IndirectAddr;
dwProtect: DWORD;
begin
// Get the IAT address first
lpIAT:=GetIATFunctionAddr(
// Check IAT address
if Assigned(lpIAT) then
begin
// Unprotect so we can modify it
if VirtualProtect(lpIAT, SizeOf(lpIAT^), PAGE_EXECUTE_READWRITE, dwProtect) then
begin
// Set the new function address
lpIAT^:=NewFunction;
// Reset the memory protection
VirtualProtect(lpIAT, SizeOf(lpIAT^), dwProtect, dwProtect);
// Flush the instruction cache
FlushInstructionCache(GetC
// Success
result:=True;
end
else
// VirtualProtect failed
result:=False;
end
else
// Failed to get IAT
result:=False;
end;
// Determine if the function has been hooked
function IsIATFunctionHooked(Librar
var lpIAT: IndirectAddr;
hMod: THandle;
begin
// Get the IAT address first
lpIAT:=GetIATFunctionAddr(
// Check assignment
if Assigned(lpIAT) then
begin
// Now we need to get the proc address and check against IAT contents.
// Notes: from what I understand, this will not work for Win95/98/Me, so
// be forewarned.
hMod:=GetModuleHandle(PCha
// Check module handle
if (hMod = 0) then
// Should have been loaded
result:=False
else
// Perform check
result:=not(GetProcAddress
end
else
// Address is nil
result:=False;
end;
// Get the IAT function address for the specified function
function GetIATFunctionAddr(Library
var Functions: TStringList;
dwIndex: Integer;
begin
// Make sure we are loaded
if not(Loaded) then LoadIATInfo;
// Set default result
result:=nil;
// Find the library first
if Libraries.Find(LibraryName
begin
// Get the function list
Functions:=Libraries.Objec
// Check list assignment
if Assigned(Functions) then
begin
// Find the function name
if Functions.Find(FunctionNam
begin
// Found the function, get the IAT pointer
result:=Pointer(Functions.
end;
end;
end;
end;
// Get the list of imported functions for the library at the given index
function GetIATFunctions(LibraryInd
begin
// Make sure we are loaded
if not(Loaded) then LoadIATInfo;
// Check the index
if (LibraryIndex < 0) or (LibraryIndex > Pred(Libraries.Count)) then
// Invalid index specified
result:=False
else
// Call the other GetIATFunctions
result:=GetIATFunctions(Li
end;
// Get the function count for the library
function GetIATFunctionCount(Librar
var Functions: TStringList;
dwIndex: Integer;
begin
// Make sure we are loaded
if not(Loaded) then LoadIATInfo;
// Attempt to find the library name
if Libraries.Find(LibraryName
begin
// Get the function list
Functions:=Libraries.Objec
// Return count from function list
result:=Functions.Count;
end
else
// Library not found
result:=(-1);
end;
// Get the list of imported functions for the specified library
function GetIATFunctions(LibraryNam
var Functions: TStringList;
dwIndex: Integer;
begin
// Make sure we are loaded
if not(Loaded) then LoadIATInfo;
// Attempt to find the library name
if Libraries.Find(LibraryName
begin
// Get the function list
Functions:=Libraries.Objec
// Check list assignment
if Assigned(List) and Assigned(Functions) then
begin
// Fill in the passed list
List.Clear;
List.AddStrings(Functions)
// Success
result:=True;
end
else
// List or Functions was nil
result:=False;
end
else
// Library name not founc
result:=False;
end;
// Get the list of imported libraries
function GetIATLibraries(List: TStrings): Boolean;
begin
// Make sure we are loaded
if not(Loaded) then LoadIATInfo;
// Check list assignment
if Assigned(List) then
begin
// Fill in the passed list
List.Clear;
List.AddStrings(Libraries)
// Success
result:=True;
end
else
// List was nil
result:=False;
end;
// Unloads the IAT information and releases all allocated memory
function UnloadIATInfo: Boolean;
var dwIndex: Integer;
begin
// Check loaded state
if Loaded then
begin
// Iterate the libraries and free all function lists
for dwIndex:=Pred(Libraries.Co
begin
Libraries.Objects[dwIndex]
end;
// Free the library list
Libraries.Free;
Libraries:=nil;
// Turn loaded state off
Loaded:=False;
end;
// Result is the reverse of the loaded state
result:=not(Loaded);
end;
// Loads the IAT information for the process
function LoadIATInfo: Boolean;
var ImageNTHeaders: PImageNtHeaders;
ImageDosHeader: PImageDosHeader;
ImageDesc: PImageImportDesc;
Functions: TStringList;
lpIAT: IndirectAddr;
lpszName: PChar;
dwEndDesc: DWORD;
dwIndex: Integer;
begin
// Are we already loaded
if not(Loaded) then
begin
// Create list for libraries
Libraries:=TStringList.Cre
with Libraries do
begin
Sorted:=True;
Duplicates:=dupIgnore;
end;
// Get start of PE32 header and NT header
ImageDosHeader:=Pointer(Ge
ImageNTHeaders:=Pointer(In
// Get import entry description and ending point for imports
with ImageNTHeaders^.OptionalHe
begin
ImageDesc:=Pointer(DWORD(I
dwEndDesc:=VirtualAddress+
end;
// Check import entry
if Assigned(ImageDesc) then
begin
// Last item in the entry will be null
while (ImageDesc^.Name > 0) do
begin
// Check offsets
while (ImageDesc^.FirstThunk <= dwEndDesc) and (ImageDesc^.FirstThunk > 0) do
begin
// Get the libary name and see if it has already been added
lpszName:=PChar(DWORD(Imag
// Check libraries list
if not(Libraries.Find(lpszNam
begin
// Create list to hold the function names for the library
Functions:=TStringList.Cre
with Functions do
begin
Sorted:=True;
Duplicates:=dupIgnore;
end;
// Add the library name and function list to the libraries list
dwIndex:=Libraries.AddObje
end;
// Get the function list from the libraries list
Functions:=Libraries.Objec
// Get the start of the IAT table
lpIAT:=Pointer(DWORD(Image
// Iterate the IAT table
while Assigned(lpIAT) and Assigned(lpIAT^)do
begin
// Get the function name
lpszName:=NextFunctionName
// Has this function already been added?
if not(Functions.Find(lpszNam
begin
// Add the function name and the IAT address for the function
Functions.AddObject(lpszNa
end;
// Get the next IAT entry
Inc(lpIAT);
end;
// Get the next import library
Inc(ImageDesc);
end;
end;
end;
// Done loading
Loaded:=True;
end;
// Result is loaded state
result:=Loaded;
end;
initialization
// Set starting defaults
Libraries:=nil;
Loaded:=False;
finalization
// Cleanup
UnloadIATInfo;
end.
--------------
unit SampleHook;
interface
uses
Windows, SysUtils, Classes, IAT;
//////////////////////////
// Record to hold patch information (forms a singly linked list)
//////////////////////////
const
MAX_IDENT = 255;
type
PPatchRecord = ^TPatchRecord;
TPatchRecord = packed record
lpAddr: Pointer;
lpApiAddr: Pointer;
lpPatchAddr: Pointer;
lpszLib: Array [0..MAX_IDENT] of Char;
lpszName: Array [0..MAX_IDENT] of Char;
lpNextPatch: PPatchRecord;
end;
//////////////////////////
// Patch code structure
//////////////////////////
type
//////////////////////////
//
// Patch Code layout
// --------------------------
// 1 BYTE(s) : PUSH EAX
// 5 BYTE(s) : MOV EAX, [Pointer of Patch Record]
// 6 BYTE(s) : CALL dword ptr [@PatchCode[20]]
// 1 BYTE(s) : POP EAX
// 6 BYTE(s) : JMP dword ptr [@PatchCode[24]]
//
// PatchCode[20] = @GenericHookProc
// PatchCode[24] = @Actual Api Function
//
//////////////////////////
PPatchCode = ^TPatchCode;
TPatchCode = packed record
Code: Array [0..31] of Byte;
end;
//////////////////////////
// Generic hook proc to recieve the redirected call from any of the
// hooked api calls (must be a register call ONLY)
//////////////////////////
procedure GenericHookProc(PatchRecor
//////////////////////////
// Global variables
//////////////////////////
var
// Patch list head node
lpPatchHead: PPatchRecord = nil;
//////////////////////////
// Hooking functions
//////////////////////////
function HookIAT: Boolean;
function UnhookIAT: Boolean;
implementation
var
bHooked: Boolean = False;
//////////////////////////
// Example of hooking all functions from kernel32.dll that start with "G"
//////////////////////////
function HookIAT: Boolean;
var Functions: TStringList;
lpIAT: IndirectAddr;
lpPatch: PPatchRecord;
lpCode: PByteArray;
dwCount: Integer;
dwOffset: Integer;
begin
// Check hook state
if not(bHooked) then
begin
// Get the functions for kernel32.dll
Functions:=TStringList.Cre
if GetIATFunctions('kernel32.
begin
// Walk the function list
for dwCount:=0 to Pred(Functions.Count) do
begin
// Check the function name
if (Functions[dwCount] = '') then Continue;
if (Functions[dwCount][1] <> 'G') then Continue;
// Get the IAT for the function
lpIAT:=GetIATFunctionAddr(
if not(Assigned(lpIAT)) then Continue;
// Allocate a new patch record and put it on to the head of the list
lpPatch:=AllocMem(SizeOf(T
lpPatch^.lpNextPatch:=lpPa
lpPatchHead:=lpPatch;
// Set the patch table fields
StrPCopy(@lpPatch^.lpszLib
StrPCopy(@lpPatch^.lpszNam
lpPatch^.lpAddr:=lpIAT;
lpPatch^.lpApiAddr:=lpIAT^
// Allocate patch memory
lpPatch^.lpPatchAddr:=Virt
// Build the instruction codes for the patch
lpCode:=lpPatch^.lpPatchAd
// We want to pass the PatchRecord into the hook function so we
// can determine why the hook was called.
//
// PUSH EAX
// MOV EAX, lpPatch
//
lpCode^[0]:=$50;
lpCode^[1]:=$B8;
dwOffset:=Integer(lpPatch)
MoveMemory(@lpCode^[2], @dwOffset, 4);
// Now generate the call to the GenericHookProc
//
// CALL DWORD PTR [GenericHookProc]
//
lpCode^[6]:=$FF;
lpCode^[7]:=$15;
Pointer(Pointer(@lpCode^[2
dwOffset:=Integer(@lpCode^
MoveMemory(@lpCode^[8], @dwOffset, 4);
// Now we need to pop EAX so the stack is restored
//
// POP EAX
lpCode^[12]:=$58;
// Now we need to call the original API function address
//
// JMP DWORD PTR [ApiAddress]
//
Pointer(Pointer(@lpCode^[2
dwOffset:=Integer(@lpCode^
lpCode^[13]:=$FF;
lpCode^[14]:=$25;
MoveMemory(@lpCode^[15], @dwOffset, 4);
// Finally we need to hook the IAT entry for this function
// so it will call our patch memory, instead of the API address
//
HookIATFunction(lpPatch^.l
end;
end;
// Free the function list
Functions.Free;
// Now hooked
bHooked:=True;
end;
// Set result
result:=bHooked;
end;
function UnhookIAT: Boolean;
var lpPatch: PPatchRecord;
begin
// Return result based on state
result:=bHooked;
// Are we hooked?
if bHooked then
begin
// Unhook all functions
while Assigned(lpPatchHead) do
begin
// Get the function from top of the linked list and relink
lpPatch:=lpPatchHead;
lpPatchHead:=lpPatch^.lpNe
// Replace the IAT contents with the old function address
HookIATFunction(lpPatch^.l
// Dispose of the patch record patch memory
VirtualFree(lpPatch^.lpPat
// Dipose of the patch record
FreeMem(lpPatch);
end;
// Remove the hooking flag
bHooked:=False;
end;
end;
procedure GenericHookProc(PatchRecor
begin
MessageBox(0, PatchRecord^.lpszName, nil, MB_OK);
end;
initialization
// Set state
bHooked:=False;
finalization
// Unhook if hook state is set
if bHooked then UnhookIAT;
end.
ASKER
"Ok the question is not how do i patch the iat. I know how to do that. "
I said this so people wouldnt have to rewrite code to walk the iat.
You answered my last question in the first part of your comment. I did not want to make you write all that. Im already using afxCodeHook to walk the iat. All i needed to know was that the actual api address was at [2]. When i understood that i was able to get it with getprocaddress() with the import function names.
But thank you anyway. Im sure whoever reads this will be practically an expert at the PE.
PS. Where is your contact info??!!
I said this so people wouldnt have to rewrite code to walk the iat.
You answered my last question in the first part of your comment. I did not want to make you write all that. Im already using afxCodeHook to walk the iat. All i needed to know was that the actual api address was at [2]. When i understood that i was able to get it with getprocaddress() with the import function names.
But thank you anyway. Im sure whoever reads this will be practically an expert at the PE.
PS. Where is your contact info??!!
Sorry the code was overkill, but I didn't want to make assumptions on what you were using code wise/your experience level/etc. No offense meant, ok...
And, I will probably get in trouble for this (EE site wise, I don't think they like it too much)... but:
russell_dana@msn.com
No site, no nada, just an e-addr. Like to keep a semi-lo profile. Not that it's a big secret, as I did provide my contact info with a dissasembly conversion that is available at Programmer's Heaven:
http://www.programmersheaven.com/zone2/cat529/32918.htm
It is not as elegant as what Madshi has done, but it does a pretty decent job at disassembling memory instructions to something understandable (ie: easily recognized as what is seen in the Delphi CPU window). Not that I'm pushing it btw ;-), just though it might interest you as well.
Anyways, hope all is good, and I hope I have answered your question(s) to your satisfaction.
Kind Regards,
Russell
> this should probably not be done on the system dlls for Win95/98/ME systems.
That's right. In win9x you should skip all modules above $80000000. But you should also skip all modules below $80000000 which have a shared IAT! Most people are missing that fact when doing IAT patching, which can result in OS instability.
That's right. In win9x you should skip all modules above $80000000. But you should also skip all modules below $80000000 which have a shared IAT! Most people are missing that fact when doing IAT patching, which can result in OS instability.
I will offer my "humble" opinion on this, but others (ie: Madshi, etc) could probably offer more input on this....
Patching the IAT (at least for statically linked dll's) is easy. When an API call is made to one of the api's, a relative CALL is made to the IAT for the dll, and then an absolute JMP is made to the actual function address.
do api ----> IAT ----> JMP absolute to API ... RET
So, in a normal hook, you would save off the 6 bytes that make up this instruction into memory (allocated via virtual alloc, with PAGE_EXECUTE_READWRITE permissions). You then replace the 5/6 bytes with the relative/absolute jump to your hooking function address.
do api ----> IAT ----> JMP hook function
Now the problem (as I'm sure you have realized) is this. How to figure out (in your scenario) which IAT index was actually called, then redirected to your hooking function?? You have a many-to-one relationship with your hook, which makes this VERY difficult.
If it was me, when creating a jump to the hook, I would do the following (creating instructions in the memory block for the hook):
In the IAT, rewrite the code to JMP absolute to the "patch" code block (memory) you have allocated for your "patch". In that "patch" memory block, add 11/12 bytes of code which amount to:
call {address of hooking function relative from this memory} // 5 bytes
// Could also be 6 bytes, jmp absolute to the hooking function
jmp {absolute index retrieved from the IAT} // 6 bytes
do api ----> IAT ----> JMP "patch block" ---> call Hook --> JMP absolute API
This will let your hook function be called when any of the hooked address in the IAT are called. Problem is, it tells you nothing about "which" function was called. Plus, I believe you may have to be careful about preserving the registers in your hook (have to read the Intel manuals again regarding CALL), because you have no idea how many will be required by the actual api call itself. The only thing this does is redirect the IAT to your your "patch" code block in memory --> then to your hook --> then to the actual API call.
You COULD extend this further, and PUSH the EAX register, move the absolute address from the IAT to EAX (Or, push the index of the IAT call), call your hook, pop EAX, and then finally jump to the old IAT address:
eg
push EAX // 1 byte
mov EAX, {absoulte address from the IAT / Index of IAT call}
call {address of hooking function relative from this memory} // 5 bytes
// Could also be 6 bytes, jmp absolute to the hooking function
pop EAX // 1 byte
jmp {absolute index retrieved from the IAT} // 6 bytes
Your hooking function should then be written to recieve a pointer (or integer of IAT index). Also, get rid of the stdcall; its already handled by the code generation (cleanup). The function should have no calling conventions added to it, leaving it as register.
procedure GenericHookProc(Func: Pointer);
// or procedure GenericHookProc(FuncIndex:
asm
// Original api will be called after we return from this procedure
ret
end;
Anyways, this it just my suggestion/idea, and I'm sure others probably know **much** more than I do regarding this, so I would wait for other experts to offer their input.
If you want though, I can take a look at putting this (the suggestions) to actual code in the morning.
Hope this helps some,
Russell