Log in

View Full Version : api hooking


4oh4
November 24th, 2001, 17:12
The target makes a call to a function in kernel32.dll. How should I go about altering the output from that function? I recall some essays on fravia's site that dealt with renaming the system dll and then replacing it with another one which either passed control to the original dll or handled the stuff itself, acting as a sort of api filter. Elicz did some work on api hooks as well, but admittedly a bit more than what I'm looking for.

It's not a big deal, just something that I thought would be a good learning experience.

I don't think process patching will work here because the target makes this call at various unset times. It seems a bit much to take the replace-the-system-dll-with-another-one route. Is there a method that's somewhere inbetween (complexity-wise)?

...just a thought, but is there a way to "lock" memory access in such a way that I could patch the variable (used in the api call) in memory once and it wouldn't get altered by subsequent calls to the same api function?

4oh4
November 24th, 2001, 22:28
Well, I've searched back through fravia's site and read and reread Lone Runner's essay (http://www.woodmann.net/fravia/fragas1.htm). That technique seems to be the best I guess. The only thing (so far) that I'm unsure of is how exactly he deals with the undocumented functions. There are 122 (I think it was) of them in kernel32.dll.

If anyone's worked through this before, I'd appreciate any help I could get.

Fake51
November 24th, 2001, 23:35
When I cracked the older versions of vbox, I patched the kernel to look for a specific pattern, more or less only emitted by vbox (actually it was only emitted by vbox, as it was the name of one of the vbox dll's). All it took was patching the first five bytes of the api to a far jump to whatever part of memory was usable (enuff of that in kernel32.dll), then do a short check to see if the right procedure had called, and then jump back. Doing so is not noticeable in speed, since windows sux anyways (and since u know what u're doing and u don't try to take over the cpu for hours at a time). For this I used a vxd. Worked perfectly.

Fake

Kayaker
November 25th, 2001, 00:44
Hi 4oh4,

Just an idea, what about creating a loader which injects your dll into the address space of the target. Then you should be able to patch the IAT jump table for that particular API to call your dll function instead of the API. Your dll then calls the API but deals with the return value before passing that back to the original call.

For a good loader/ dll injection implementation take a look at Iczelion's IczDump, your dll is a black box at this point. Once loaded you'd have to find the IAT jump table entry. If this is a one-off target you could probably just find it in memory and patch it with the address of your dll's function. If you want a more generic API-hooking technique you'd have to walk the import directory to find the jump table address for your API(s).

You've probably seen the articles at h**p://www.internals.com/articles_main.htm as well as the APISpy32 site, Good info here. MS has published its own Detours API hooking source, though I never digested that one. Check out how Harlequin handled patching Kernel32 to intercept the TerminateProcess call in his recent essay as well. Though I think he had problems with determining the ProcessId of the calling process. Inject your dll and you have complete control over the address space of the target program

Just out of curiousity what API are you trying to intercept?

Kayaker

garph0
November 25th, 2001, 07:31
for the injecting parte there is also a good work of yoda called forcelibrary. a quick search should give you some link
for the hooking part s0rr0w made a tool: you can find it at
http://francesco.netsigners.com/
there there is also an old version of my injector sw (i think sorrow named it GIT or something like that), but probably Yoda's one is more effective
afaik for the undocumented functions there should be no problem, but you need to know the signature (parameters) and what each parameters mean.
hope this helps

work well

g

4oh4
November 25th, 2001, 13:57
Well I typed a long post and apparently my cookie timed out or something cause it was all lost. -dammit-

Anyways, I'll make this post much more brief to avoid anymore problems.

Fake51:
I read that (or maybe just a similar) essay on fravia's site. IMHO LoneRunner's method is more flexible though, since it doesn't have to be target specific. Also, I have very little experience dealing with vxds. While this is a learning exercise for me, I don't want to overwhelm myself.

Kayaker:
Since this is a one time reversing task I'm going to give your loader suggestion a shot. I ran across those essays you linked to in my search last night....all except the one from Harlequin. I wonder why the tut search engine didn't turn it up. I've read it before, but never actually tried his technique because I read a thread on this board I believe about a problem with his code. Btw, GetVolumeInformation is the api I'm after.

Anyways, just to make sure that I understand the theory here:

my loader has to:
1) load the target
2) wait for it to unpack itself (neolite 2.0 w/ max compression)
3) locate the GetVolumeInfo entry in the IAT (either on-the-fly so to speak or from previous sice excursions)
4) patch that entry with my dll's function address

my dll's function has to:
1) call the original GetVolumeInfo api
2) alter the values returned by it to fit my needs
3) return the bogus info to the target

Is that right, or did I miss/forget something? If that's basically it, then the only thing I don't completely understand at this point is the IAT patching. The dll and loader coding won't be a problem.

I've got a pe.txt (by luvelmeyer or something like that). If I read and completely understand the description of the import directory and all the other import related topics will that be enough? ...or are there other essays that I'll need to read?

garph0:
I ran across forcelibary in last night's search. I'm sure befor I'm finished I will have searched through all the source examples that I've found on api hooking, but for now I'd like to code the loader myself. That way I'll learn more. That link is dead though. I google'd for s0rr0w and found a msg board that was in french, but dealt with reversing and the like. There was a link to s0rr0w.net but it too is dead. I've never seen your program or GIT.

I've decided against LoneRunner's proxy dll method, but I'd like to try it sometime in the future. I still don't understand how he dealt with the undocumented functions though. How would I go about getting the parameters for the functions when all that I can get from dumbin's output is the ordinal numbers of the functions?


Thanks for all the help!

edit:
That wasn't so brief after all eh?

JMI
November 25th, 2001, 15:16
4oh4,

The article you were looking for is

http://www.woodmann.net/fravia/harlequin_Kernel32.htm

Another that might be of interest is

http://www.woodmann.net/fravia/pna3.htm

Its entitled "How to hook any API function in kernel32.dll
with simple exports of your own DLL " by pna and also from the essay database.

Good luck

P.S. I've located Hookit.zip in my archive if you need/want it. Let me know. I believe it is the one from the pna article. I also have the Fake.dll asm file and probably have the SrcGen.exe, but haven't quickly located it yet.

garph0
November 25th, 2001, 20:25
hi 4oh4
sorry, i didn't notice that sorrow site is down... i'll post a good link as soon as i'll have it.
should you need an hand i'll be glad to help you (if i'm able, of course )

good work,

g

4oh4
November 28th, 2001, 22:29
Thank JMI, I'd like to take a look at those files if you wouldn't mind up'ing them.

Thanks for the offer of help garph0. I appreciate it.

As a matter of fact I've got a couple of questions.

The first is that my replacement function for GetVolumeInfo isn't working quite right. I pasted the masm source below. What this is supposed to do is (once the hook is established) when the target calls GetVolInfo this gets called instead. Then I call GetVolInfo, retrieve the volume serial and alter it, and finally return the altered volume serial to the target. Keep in mind that I'm only grabbing the volume serial and filling the dword with the address of the actual volume serial. I'm not altering it at all, and I'm not returning any other info at all to the target. The target in this case not being the actual target, but a little test program I wrote for the occasion. I hope that makes sense.

Code:

MyGetVolumeInformation proc lpRootPathNameWORD,lpVolumeNameBufferWORD,nVolumeNameSizeWORD,
lpVolumeSerialNumberWORD,lpMaximumComponentLengthWORD,
lpFileSystemFlagsWORD,lpFileSystemNameBufferWORD,
nFileSystemNameSizeWORD
;just for completeness sake (even though I don't deal with
;them now I provided buffers for all the data
invoke GetVolumeInformation,addr szRoot,addr volBuff,30h,addr volSer,addr maxComp,\
addr sysFlags,addr sysName,30h
;mov offset of the volume serial into eax
mov eax,offset volSer
;mov that offset into the buffer passed to MyGetVolInfo
mov [lpVolumeSerialNumber],eax
;clear eax
xor eax,eax
;and mov 1 into it so that it doesn't return an error
;in case the target has error checking in place
mov eax,01h
ret
MyGetVolumeInformation endp


Now keep in mind again that all the fake function is doing at the moment is grabbing the volume serial only and filling the provided buffer with that serial. But it's not working on my little test app. Could someone who's better with asm coding than me tell me what the problem is. Of course it has to be the way that I'm taking the volume serial and moving it into the buffer, but I don't see the error.


The second question has to do with the actual IAT patching. Do I just use do something like this:

From my loader:
1) use LoadLibrary to load my dll
2) use GetModuleHandle and GetProcAddress to get the address of GetVolumeInformation (and MyGetVolumeInformation as well)
3) use WriteProcessMemory to replace the address of the original api with the address of mine

Is there more to it? (of course between steps 1 and 2 I'd have to use CreateProcess to launch the target)

Thanks for all the assistance. It's much appreciated.

edited to disable the smilies that were fucking up my code

JMI
November 29th, 2001, 02:13
4oh4:

Here's a copy of HookIt. I'm not sure it's the one from the pna essay, but it should do the trick. I haven't found the Fake.dll file yet and I don't have the ScrGen.exe file. I recall that I didn't file this essay until long after it was written and don't believe I was able to contact pna to get his original. If I find Fake.dll on my HD I'll send it along later.

Hope the HookIt file helps.

Kayaker
November 29th, 2001, 03:40
Quote:
Originally posted by 4oh4

edited to disable the smilies that were fucking up my code



Don't you hate those WORD smiley faces?

Hooking the API aside for the moment, you'd have to take into account the target program may be expecting more than just the VolumeSerialNumber returned it what it *thought* was its own call to GetVolumeInformation. So you fake your own call to fill the buffers the targets API call has already set up, referenced to the stack, and change the one you want.

This code works as an include module under a button or something. Every parameter is included, though your target may actually use NULL for some of them. DupeTarget proc is supposed to be how your patched target behaves, it pushes exactly the same things, but calls your dll function instead.

;================================================
DupeTarget proto
MyGetVolumeInformation proto : DWORD, : DWORD, : DWORD, : DWORD, : DWORD, : DWORD, : DWORD, : DWORD

.DATA

lpRootPathName db "C:\",0
lpVolumeNameBuffer dd 48 dup(?)
nVolumeNameSize DWORD (48)
lpVolumeSerialNumber DWORD ?
lpMaximumComponentLength DWORD ?
lpFileSystemFlags DWORD ?
lpFileSystemNameBuffer dd 48 dup(?)
nFileSystemNameSize DWORD (48)


.CODE

DupeTarget proc
invoke MyGetVolumeInformation, ADDR lpRootPathName, ADDR lpVolumeNameBuffer,\
nVolumeNameSize, ADDR lpVolumeSerialNumber,\
ADDR lpMaximumComponentLength, ADDR lpFileSystemFlags,\
ADDR lpFileSystemNameBuffer, nFileSystemNameSize
ret
DupeTarget endp


MyGetVolumeInformation proc Lprootpathname : Dword, Lpvolumenamebuffer : Dword,\
Nvolumenamesize : Dword, Lpvolumeserialnumber : Dword,\
Lpmaximumcomponentlength : Dword, Lpfilesystemflags : Dword,\
Lpfilesystemnamebuffer : Dword, Nfilesystemnamesize : Dword


invoke GetVolumeInformation, Lprootpathname, Lpvolumenamebuffer,\
Nvolumenamesize, Lpvolumeserialnumber,\
Lpmaximumcomponentlength, Lpfilesystemflags,\
Lpfilesystemnamebuffer, Nfilesystemnamesize

.if (eax)
PUSH eax ; save return value in case program uses GetLastError

push [ebp+14h]
pop eax ; push/pop frame pointer to 4th parameter into eax
mov eax, [eax] ; returned lpVolumeSerialNumber value
xor eax, 0A881A6DEh ; this creates DEADBEEF on my system ;-)

lea ebx, [ebp+14h] ; pointer to the lpVolumeSerialNumber pointer
mov ebx, [ebx] ; lpVolumeSerialNumber pointer
mov dword ptr [ebx], eax ; mov modified value into lpVolumeSerialNumber address

POP eax ; restore true return value
.endif
ret
MyGetVolumeInformation endp
;===============================================

Disassembly:

DupeTarget proc
0167:10001000 FF35CD4A0010 PUSH DWORD PTR [10004ACD]
0167:10001006 680D4A0010 PUSH 10004A0D
0167:1000100B 68094A0010 PUSH 10004A09
0167:10001010 68054A0010 PUSH 10004A05
0167:10001015 68014A0010 PUSH 10004A01 ; ADDR lpVolumeSerialNumber
0167:1000101A FF35FD490010 PUSH DWORD PTR [100049FD]
0167:10001020 683D490010 PUSH 1000493D
0167:10001025 6839490010 PUSH 10004939
0167:1000102A E801000000 CALL 10001030
0167:1000102F C3 RET

MyGetVolumeInformation proc
0167:10001030 55 PUSH EBP
0167:10001031 8BEC MOV EBP,ESP
0167:10001033 FF7524 PUSH DWORD PTR [EBP+24]
0167:10001036 FF7520 PUSH DWORD PTR [EBP+20]
0167:10001039 FF751C PUSH DWORD PTR [EBP+1C]
0167:1000103C FF7518 PUSH DWORD PTR [EBP+18]
0167:1000103F FF7514 PUSH DWORD PTR [EBP+14] ; Lpvolumeserialnumber
0167:10001042 FF7510 PUSH DWORD PTR [EBP+10]
0167:10001045 FF750C PUSH DWORD PTR [EBP+0C]
0167:10001048 FF7508 PUSH DWORD PTR [EBP+08]
0167:1000104B E8721A0000 CALL KERNEL32!GetVolumeInformationA
0167:10001050 0BC0 OR EAX,EAX
0167:10001052 7414 JZ 10001068

0167:10001054 50 PUSH EAX
0167:10001055 FF7514 PUSH DWORD PTR [EBP+14]
0167:10001058 58 POP EAX
0167:10001059 8B00 MOV EAX,[EAX]
0167:1000105B 35DEA681A8 XOR EAX,A881A6DE
0167:10001060 8D5D14 LEA EBX,[EBP+14]
0167:10001063 8B1B MOV EBX,[EBX]
0167:10001065 8903 MOV [EBX],EAX
0167:10001067 58 POP EAX
0167:10001068 C9 LEAVE
0167:10001069 C22000 RET 0020

==========================================

I guess you could implement your loader in various ways, walk the import directory of the target and find the jump table address to GetVolumeInformationA and patch it with your function, or maybe use GetProcAddress to find your kernels address and search for that hex string in the targets memory and patch it as bytes. Being packed makes it a bit of a bother, else you could simply inline patch in some dll loading code. You've got all the ideas for the loader right, I'm not sure about the exact IAT patching details myself.

Cheers,
Kayaker

4oh4
November 29th, 2001, 04:25
Thanks again for that file JMI.

.....and thanks to kayaker. You always seem to throw useful info my way!

I'm a bit coded-out for tonight but I'll take a better look through your code tomorrow. I especially appreciate all the comments and I'm sure that I won't have too much trouble understanding everything once I get some rest.

I was planning on providing support for all the params (not just the volume serial num). It's just the way I deal with this type of stuff. It's easier for me to sort out the (my) bugs by taking things one (baby) step at a time.

It's too bad that you're not familiar with this sort of IAT patching though. -shucks-

Btw, in case you're interested hutch released v7 of his masm32 package. I've dl'ed it but probably won't get around to installing it till tomorrow or the next day.

garph0
November 29th, 2001, 09:51
Hallo 40h4
kayaker has explained that stuff really well, and i was thinking of helping a little with the IAT patching stuff:
you may need some knowledge of the PE format, try getting this

"Peering Inside the PE: A Tour of the Win32 Portable Executable File Format" by Matt Pietrek (March 1994)

there are also some good works by John Robbins, but i don't remember the title right now...

putting it simple: every time you compile a program which imports some function from a dll, the linker does some work to provide support for relocation: since every DLL can be loaded in different places in memory from time to time, when you issue a call you don't have something like this

call _AddressOfFunction

but this

call _SomeAddressInTheSpaceOfYourProgram

and at SomeAddressInTheSpaceOfYourProgram you have something like this

jmp DWORD PTR [AnotherAddressInYourProgramSpace]

at AnotherAddressInYourProgramSpace you find the real imported function address. this is done simply because doing so the PE Loader has only to fix the values found at the last address (AnotherAddressInYourProgramSpace) to handle all the possible relocation issues.

the AnotherAddressInYourProgramSpace is placed in a table called IAT which means Import Address Table. You see that you can easily redirect all the calls from the original function to your function by simply patching that last value, and keeping the original value to be used as a function pointer if you need to call the original function.

So you need to find the Import Table in memory.
the PE loader works by memory mapping the whole executable, and then fixind the addresses in memory. Given an address tha you know being inside the target program, you can search backwards ad try to find a valid PE header (you can simply read the first bytes of the file before using it and then search for those, or you can seek for the strings MZ and PE, which are found in the exe header, look in the article for help
anyhow, when you safely land on a valid PE header in the target address space, you can safely suppose it's your header and the start reading header fields, and search for the IT.
The IT starts with a chain of IMAGE_IMPORT_DESCRIPTOR structs


Code:


all fields are DWORD size

-------------------
| Characteristic | RVA of IMAGE_IMPORT_BY_NAME struct
-------------------
| TimeDateStamp | build of the file
-------------------
| ForwarderChain | you may handle this in the future
-------------------
| Imported Dll Name | RVA of imported DLL's name
-------------------
| FirstThunk | RVA of IMPORT_ADDRESS_TABLE struct
-------------------




you need the 3 RVAs, let's suppose that you need to find the address of the function IsWindow() in User32.dll.
0) you find the PE header of the target program and read the IT address in the header

1) you start walking the list of IMAGE_IMPORT_DESCRIPTORS of IT, checking the name field, until you find the entry relative to User32.dll (remember that we are talking about RVAs, you need to add the ImageBase value (usually 0x400000 in windows for executables, but you should better read it from the PE header) for it to make sense:
Address = RVA + ImageBase)

2) ok, now that you have found the correct entry, you use the Characteristic RVA to find the Name Lookup Table (NLT) in memory (remember! is another RVA!).

the NLT is an array of DWORDs, each one is the RVA of a struct containing a WORD (function ordinal) and a zero terminated string (function name), so

3) you walk down the NLT until you find the function you need. let's suppose this is entry 7 in you NLT array.

4) the field FirtsThunk of the IMAGE_IMPORT_DESCRIPTOR contains the RVA of the Address Lookup Table (ALT), an array od DWORDs

5) every DWORD is the virtual address (NOT the RVA!) of the corresponding function in NLT, so in ALT[7] you have the address you were searching for (it points to the real funcion, not to the name as in NLT, excuse me if the drawing below isn't clear )

graphically:


Code:

NLT IAT (ALT)
--- ------------ ------ <----\
/--->| R |------>| 44 |<---| | |
RVA | | V | ------------ | VA | |
| | A | | GetMessage | | | |
------------------- | --- ------------ ------ |
| Characteristic |--/ | R |------>| 95 |<---| | |
------------------- | V | ------------ | VA | |
| TimeDateStamp | | A | | IsWindows | | | |
------------------- --- ------------ ------ |
| ForwarderChain | |
------------------- RVA -------------- |
| Imported Dll Name |--->| "User32.dll" | |
------------------- -------------- |
| FirstThunk |----------------------------------------------------/
------------------- RVA




ok then, i hope it's clear enough... quite a little playing with pointers and addresses, but once you get to the PE header you cannot miss the thing

work well,

g

PS

thanks to sorrow: much of the contents of this post comes from one of our works, and the pe playing is his field hi, bro.

Kayaker
November 29th, 2001, 14:54
Hiyas,

Anyone interested might want to look at the Messin' with the Import table project we had a while back. The attachments seem to have disappeared, but I'll try reuploading the original target I've got somewhere on my hd.

http://www.woodmann.net/forum/showthread.php?threadid=162&perpage=15&pagenumber=1

One of my recommended readings (easy to understand!)was
[yAtEs] Understanding Import Tables
which can now be found in the Immortal Descendants Archive ID-RIP on the main page (was under database/essays/yates/Import_tables.txt)

The equally good TiTi essay upped by Clandestiny (now deleted as well!) can be found here:
h**p://pluto.spaceports.com/~titiasm/essays/imptab.zip

Also check out FatBoyJoe's PE format by Offset listing while you're in the ID archive, *very* useful reference for walking the PE file (I think it was called something like 'exe-hdr.html')


As garph0 just nicely demonstrated, it's sometimes handy to get a reverser's condensed version of something like the Peitrek or Luevelsmeyer tome
h**p://spiff.tripnet.se/~iczelion/files/pe1.zip

Kayaker

JMI
November 29th, 2001, 17:08
If you downloaded the ID-Rip that Fatboy article is at

immortaldescendants/database/essays/fboyjoe/exe_hdr.html

for those of you who didn't, here it is as a text file. (the board doesn't permit uploading html formated files.)

Again thanks for a very informative thread.

Kayaker
November 29th, 2001, 20:05
If anyone is interested I reuploaded the include program for the Messin' with the Import table project

http://www.woodmann.net/forum/attachment.php?s=&postid=11206

4oh4
November 30th, 2001, 17:04
garph0:
Is the address that GetProcAddress returns actually the rva of the function in the name lookup table or the address of the function in the address lookup table?


edited for the sake of coherency

JMI
December 2nd, 2001, 16:17
Finally found the Fake It dll Asm mentioned in my prior posts on this thread about the pna article.

Hope it's of use.

garph0
December 3rd, 2001, 16:30
Quote:
Is the address that GetProcAddress returns actually the rva of the function in the name lookup table or the address of the function in the address lookup table?


GetProcAddress returns the address you find in the Address Lookup Table entry relative to that function. it's already an address you can use.

remember, though, that GetProcAddress gets the functions from the EXPORT TABLE of the modules it seeks.
so you have two types of hooking:
1) the functions you are going to hook are implicitally linked to your executable, so you can find them just walking the import table, and patch it in memory.
2) the functions you are seeking are loaded at run-time via the LoadLibrary/GetProcAddress couple. in this case you have two possible solutions:
2a) you at the beginning hook LoadLibrary (or LoadLibraryEx) and GetProcAddress, so you can track the functions loaded dynamically, and cheat with the return values if you need
2b) you can change the export table of the module containing the function so any subsequent call to GetProcAddress done FROM YOUR ADDRESS SPACE (beacause the modified page will be copied for your process use) will return the value you've written.

hope this clears something.

g

4oh4
December 4th, 2001, 15:26
garph0.....we're on the same sheet of music buddy.

You've not only answered my question, but the other questions I had too.

I'm not sure why, cause sice keeps taking a shit on me (I've only managed to successfully trace into the GetVolumeInfo call once), but ebp+14 == 0. The example that you posted (kayaker) doesn't seem to modify the volumeserial buffer. I'll try crossing my fingers and loading sice again tonight and hope it doesn't lock me up again. Trw gives me a black screen of death as it breaks on GetVolumeInfo, so I don't have another option but sice. Well I even tried debugging with w32dasm but ebp+14 was either filled with ?'s or 0's (I can't remember but I think it was 0's) so I can't check the contents of regs with w32dasm.

Kayaker
December 4th, 2001, 19:43
I don't know what the problem might be 4oh4. After the real call to GetVolumeInformation the buffers should contain the returned values of lpVolumeSerialNumber, lpMaximumComponentLength, lpFileSystemFlags, lpFileSystemNameBuffer:

Code:

016F:10004A15 762C1831 000000FF 00004006 33544146 1.,v.....@..FAT3
016F:10004A25 00000032 00000000 00000000 00000000 2

After changing VolumeSerialNumber:
016F:10004A15 DEADBEEF 000000FF 00004006 33544146 .........@..FAT3
016F:10004A25 00000032 00000000 00000000 00000000 2


Are you trying this in the program itself or your own code? Stack error?

Kayaker

4oh4
December 6th, 2001, 21:39
heya kayaker

I've tried it on both my own test app and the target itself. The author is changing the hdd serial based scheme to something else though so I suppose it doesn't *really* matter now.

This is very interesting of a thing for me though and I'm not giving it up, just moving it to the back burner while I work on other projects.

Thanks again everyone for the suggestions, files, and ideas.


edit:

I didn't see the question about the stack error. No there was no problem with the execution of the code. It's just that the actual volume serial was returned unaltered.