Trao đổi với tôi

http://www.buidao.com

4/23/10

[Hooking] Hooking Lesson 1

well, it's actually pretty straightforward. i'm interested in the following functions: connect(), getpeername(), and closesocket()
all of which are part of winsock and reside in ws2_32.dll
winsock's functions all happen to have the same prolog (what sets up a function): mov edi, edi; push ebp; mov ebp, esp
those three instructions happen to take up 5 bytes (2, 1, 2, respectively), the same size as a 32-bit jump. so these functions are candidates for trampolining.
now, what that means is that you patch the beginning of the function with a jump to your own custom version of the function. this is advantageous in that any way you call the patched function, it ends up calling your replacement.
so you patch beginning of the function with a jump to your replacement, and write the replacement just like you'd write it if you were to implement the function normally -- nothing special at all.
now, where it gets a bit complex is if you want to call the function you replaced from your replacement. this is often the case, since you usually want to manipulate the input to the function
so the problem with calling the original function is that if you do so normally... you just end up jumping back into your function :P
so you have to do a little trick. you need to do whatever the instructions you replaced were supposed to do, then jump to the point after your patch.
in this case, the first instruction (mov edi, edi) does nothing -- it's really just a two-byte nop. the second and third instructions, though, are important, since they set up the local stack frame.
so what you need to do is run those last two instructions, then jump into the original function + 5 bytes, to get over your jump.
if you do that, you can call the original function as if nothing happened.
that's really about it for the technique itself. a few important notes, though:
1) code is almost always write-protected, so you have to give yourself write privledges. VirtualProtect will do this on windows. you want to set it back to the original protection value once you're done, though, or the process could detect tampering.
2) any time you modify code in memory, you MUST invalidate the code cache. if you don't, your jump may be ignored or it'll run the original first instruction and then the last 3 bytes of your jump or somesuch -- not good. FlushInstructionCache on windows fixes that
3) there's the chance that something could call the function in the middle of you patching it, so you really should suspend the threads of the process before you patch and resume when you're done. Suspend/ResumeThread on windows does that (although you're going to have to enumerate the running threads -- not easy from C, so i did it in the C# part)
and that's about it.
well. first one was. does everything on windows use winsock?
cause if not wouldnt that make your code only work for the stuff that does?
as for the winsock shit, everything non-malicious will use winsock, but there's malware that uses the networking syscalls directly. only way to catch that is via a driver, which is what i'm hoping Ophichius will do :P
ah
well i understand the concept of it. not the details of the execution but that would take far too long for me to understand
thanks :)
witeness: well, what details don't you understand?
um. well
whats the code cache?
wite: it's the processor's cache for code. as you execute code, the processor caches bits of it for speed. invalidating it makes the processor clear it out, and next time you execute it it has to fetch it from memory. if you don't do that, the processor may have all or some of the unpatched code in its cache and Bad Things (TM) will happen. (sadly, i forgot about this and was tryingto figure out why my code was only working if you hadn't called connect() yet)
so. you run your code, do you have to inject it somehow into the other process to patch the method? or how does it get called
witeness: yes, my DLL has to be injected into the target process. let me explain how that works (and this part is used in pretty much any patching technique, as you'll see when i explain some)
<@Daeken> so, DLL injection is a very strange technique but i think it'll make a lot of sense
<@Daeken> DLLs are loaded at runtime via the LoadLibrary (technically LoadLibraryA, since you want the ASCII version, but it's documented as LoadLibrary) method. this takes the name as a string and returns the address where the function is loaded
<@Daeken> so our goal is to call LoadLibraryA in the target process, to make it load our DLL
<@Daeken> well, conveniently, windows has a function called CreateRemoteThread() that lets you start a thread in another process
<@Daeken> thread functions have the prototype: void *ThreadProc(void *argument) ... clearly, we can use this to call LoadLibraryA in the process
<@Daeken> so we know we want to use CreateRemoteThread, but we have a problem... if we have a string in our process, we can't just give the address of that as the argument to LoadLibraryA, since it's going to try and read it in the target process
<@Daeken> the solution is to allocate memory in the target process for the string, copy it into that, and then pass /that/ address into LoadLibraryA
<@Daeken> to do that, you use VirtualAllocEx to allocate the memory, WriteProcessMemory to copy it in, and then just pass the address (given to you by VirtualAllocEx) into LoadLibraryA
<@Daeken> one important thing to remember is that you should use GetProcAddress to get the address of LoadLibraryA -- if you don't, your process' import table could fuck it up, and you might end up crashing the target process.
<@Daeken> now, this gets our DLL loaded in the target function, but there's some other stuff to keep in mind.
<@Daeken> now that your DLL is in the process, you need to actually make it do something. there are two techniques for this:
<@Daeken> 1) DllMain
<@Daeken> 2) use CreateRemoteThread to call a function in your injected dll
<@Daeken> both of these have their advantages
<@Daeken> DllMain is called whenever your DLL is loaded, unloaded, etc. this makes it easy to hook what you want when the DLL is loaded without doing anything more in the injection code. the problem is that if you've got the injection code inside the DLL you're injecting (which i did in NetHooker, since i didn't want yet another DLL floating around), then DllMain will be called in your injector and could cause Bad Things (TM).
<@Daeken> the other technique is more complex, but it gives you more control. to use this technique...
<@Daeken> first, you need to know where your DLL got loaded in the process. to do this, you have to store the thread handle returned by CreateRemoteThread() (the thread you created to load the DLL initially) in a variable, then you call WaitForSingleObject(thread, INFINITE) to wait until the thread returns. once it returns, you use GetExitCodeThread() to get the return value from LoadLibraryA, which is where your DLL is loaded.
<@Daeken> second, you need to figure out where the function you want to call actually resides inside your binary.
<@Daeken> the way i do this (there are lots of ways) is just to use GetModuleHandle() in your process to get where your DLL is loaded in the injector itself, then subtract that from the location of the function. this gives you where it is inside of the DLL
<@Daeken> third, you need to figure out where that function is inside the target process. this part is simple -- take the base address from step 1 and add the function location from step 2.
<@Daeken> fourth, call CreateRemoteThread to call the function.
<@Daeken> at this point, your code is completely loaded and you can do all of your initialization (hooking, loading other libraries, whatever) in that function
There isnt a method to return the location of where your DLL is loaded(rather that using GetExitCodeThead())?
It doesn't seem like GetExitCodeThread is specifically designed for giving you the location of your DLL - or is that the point, being that this is a bit hackish :)
Joe: well, you could, but you'd just have to do another CreateRemoteThread() and then get the return from GetExitCodeThread() anyway
I see.
So GetExitCodeThread() really returns where your DLL is loaded.
MKay.
Joe: the problem is that you can't just call functions in the other process. CreateRemoteThread() lets you call them, GetExitCodeThread() gets the return value
Ah, I see
Joe: in this case, yes, but it's just whatever the thread returns. it just happens to be that LoadLibraryA is our thread.
okay, now, second question(and last) - was a bit confused about part 2. How does subtracting GetModuleHandle() from the location of the function(what function?) give you the location of your function in the DLL?
Joe: good question. first, the function i'm referring to is just whatever function (in your DLL) you want to call in the target process
Joe: second, GetModuleHandle() gives you the address of your DLL in your injector, and you have the location of your function in your injector. since the function is inside the DLL, you can just subtract the two to find where in the DLL the function exists. and since we know where the DLL is in the target process, we can use this to figure out the function's location in that process.
<@Daeken> next technique i'll cover is IAT (Import Address Table) hooking.
<@Daeken> this is a more traditional way of hooking functions. the summary of how it works is that you inject your DLL into the binary (like i explained above), find the function you want to hook, and then replace its address in the IAT with your replacement function.
<@Daeken> so the first thing you need to do is get the address where the exe is loaded. GetModuleHandle(NULL) will give that to you
<@Daeken> then comes the beast... you have to parse the binary's header. i'm not going to give every detail here (way too complex), but here's the basic idea: get the NT header (starts with PE\0\0), get the 'optional header' (which isn't actually optional), then you get the address of the import table from the DataDirectory's inside that. it's really just walking down a bunch of structs -- check the PE file format docs for more information.
<@Daeken> the import table contains the dll to load from, function name, and then the address where the function resides (the last bit being added into the import table by the binary loader when these DLLs are loaded)
<@Daeken> so what you need to do is iterare over each entry in the import table and find the function you want (just do a string compare), and then when you find it, you just set the address of your replacement function in the proper entry.
<@Daeken> i didn't give details of how to get to the info and such since it's complex in implementation, but the theory behind it is extremely simple.
<@Daeken> core problem with the IAT method is that it only works if the binary actually imports it that way. if they use LoadLibrary and GetProcAddress to get the function they want, this won't work.

reflink: http://www.assembla.com/wiki/show/nethooker/Hooking_Lesson_1