stevemk14ebr / PolyHook_2_0

C++20, x86/x64 Hooking Libary v2.0
MIT License
1.6k stars 225 forks source link

Eat out of module hooks #5

Closed AppleSky6 closed 5 years ago

AppleSky6 commented 5 years ago

EffectTracker eatEffectTracker;

typedef void(__stdcall*tEatTestExport)(int a); tEatTestExport oEatTestExport;

extern "C" declspec(dllexport) int stdcall EatTestExport(int a) { return a; }

int __stdcall hkEatTestExport(int a) { MessageBox(0, "0", "0", 0); eatEffectTracker.PeakEffect().trigger(); }

void main() { PLH::EatHook hook("EatTestExport", L"test.exe", (char)&hkEatTestExport, (uint64_t)&oEatTestExport); hook.hook();

tEatTestExport pExport = (tEatTestExport)GetProcAddress(GetModuleHandle(nullptr), "EatTestExport");

eatEffectTracker.PushEffect();
pExport(1);
eatEffectTracker.PopEffect().didExecute();
hook.unHook();

}

stevemk14ebr commented 5 years ago

please explain the issue you are having in depth

AppleSky6 commented 5 years ago

My program can not be executed normally, is it a code problem?

stevemk14ebr commented 5 years ago

I will try your code when I have free time. 'Normally' doesn't help, what is the error your see, or explain what is unexpected

AppleSky6 commented 5 years ago

My hook __stdcall exported function failed。Is this type of hook not supported?

AppleSky6 commented 5 years ago

Can you give me an example of eat hook Windows API, such as MessageBox?

AppleSky6 commented 5 years ago

Source.txt Unable to correctly export messageboxA from hook user32.dll, there is no error code. Thank you for helping me solve the problem

stevemk14ebr commented 5 years ago

definitely a bug here, i'll take a look when i get some time, thanks for the report.

AppleSky6 commented 5 years ago

You write great code and I love your style. Recently, a project needs to refer to your lib file. I don't know when it will be processed or what I can do to help。

stevemk14ebr commented 5 years ago

Appreciate the kind words, what do you mean by

I don't know when it will be processed or what I can do to help。

I found the bug too. The issue is in how the EAT works, it's AddressOfFunctions array is actually an array of offsets relative to the DLL. This is why it breaks when you do a WinAPI hook, it find the export fine then tries to replace the entry with an offset to the callback by this offset calculation:

*pExport = (uint32_t)(m_fnCallback - m_moduleBase);

https://github.com/stevemk14ebr/PolyHook_2_0/blob/master/sources/EatHook.cpp#L24 https://github.com/stevemk14ebr/PolyHook_2_0/blob/master/sources/EatHook.cpp#L69

Unfortunately the m_moduleBase is the base of User32.dll, and not of the exe. This then calculate the wrong offset, and points to some garbage location inside User32.dll once the EAT is traversed again. To fix you'd need to allocate something within User32.dll to transfer control to the exe, it's slightly complicated to do this right (unless i'm missing something). It works in my other unit tests because the exports are in the exe itself, so when the offset calculation happens the base is the same for the export and the callback.

http://bsodtutorials.blogspot.com/2014/03/import-address-tables-and-export.html

So congrats you found a flaw for out of module EAT hooks, unfortunately I think this is something I won't fix for a while until I get a new implementation idea. I'd use a different hooking method if I were you.

This doesn't effect IAT hooks, the IAT is not an offset table but a legit table of pointers.

AppleSky6 commented 5 years ago

Thank you for your help. I will support your project.

AppleSky6 commented 5 years ago

I can't speak English, maybe there is something wrong with semantic translation. I really appreciate your help.

L-Leite commented 5 years ago

Sorry about that, I overlooked that issue.

@stevemk14ebr we could calculate if we are in front of the target's base address. If we are, good, just calculate the offset and write the result to the EAT entry. If not, allocate memory after the target's base address (using VirtualAlloc?), write a jump to our actual hook function in the allocated memory and point the EAT entry to it. What do you think?

stevemk14ebr commented 5 years ago

No worries @Ochii i reviewed and missed it too. Yea i think doing a little trampoline is the best way as far as ease of implementation.

stevemk14ebr commented 5 years ago

I've pushed the start of the implementation, it can at least detect this case and fail appropriately now. I'll properly finish it later when i have some time. I'll accept PR's though too 😄

L-Leite commented 5 years ago

Would it be fine to use the existing Detour methods? Or do you prefer that the EAT class does stuff on its own?

stevemk14ebr commented 5 years ago

I'm considering factoring out the 'makeJmp' methods of the detours into some namespace/object so they can be shared by EAT and Detours. The EAT should be separate from the detours as much as possible and if they share functionality then it should be first broken out into a third place.

stevemk14ebr commented 5 years ago

The implementation is going to have to control the allocation by manually walking the virtual pages. I tried the following code to brute force the allocation address such that it's <= 32 bits and thus in range and it never broke out:

/* We cannot control allocation address here, so try it until we get one within range*/
bool needFree = false; // could use m_trampoline == null, but i like locality (less side-effects)
do {
    // if we are coming back around again, free old attempt
    if (needFree) {
        needFree = false;
        delete[] m_trampoline;
        m_trampoline = nullptr;
    }

    m_trampoline = new uint8_t[32]; 
    needFree = true;
    MemoryProtector prot((uint64_t)m_trampoline, sizeof(uint8_t) * 32, PLH::ProtFlag::R | PLH::ProtFlag::W | PLH::ProtFlag::X, false);
    *m_trampoline = (uint8_t)0xCC;
    offset = (uint64_t)m_trampoline - m_moduleBase;
} while (offset > std::numeric_limits<uint32_t>::max());

So we need to instead do something like what i did in V1: https://github.com/stevemk14ebr/PolyHook/blob/master/PolyHook/PolyHook.hpp#L143

EDIT: done with allocation https://github.com/stevemk14ebr/PolyHook_2_0/commit/6409258edfe041d239dd218e0519ac78973f0571 😁

stevemk14ebr commented 5 years ago

Please try master. I've hopefully resolved this and would like to close: 5cd7d5021e82f3813eb8bad515e68830b5de9621

FFFF0h commented 5 years ago

Hi, I had the same issue and now it seems that the problem is solved. BUT, I'm facing a new one: I cannot hook multiple WINAPI. The first hook always work, but the following EAT hooks failed. Maybe, I'm not doing it the right way. Can you help me ?

decltype(GetSystemTime)* Original_GetSystemTime; void WINAPI Hook_GetSystemTime(PSYSTEMTIME systemTime) { odprintf("[EAT] GetSystemTime called!"); GetSystemTime(systemTime); }

decltype(GetLocalTime)* Original_GetLocalTime; void WINAPI Hook_GetLocalTime(PSYSTEMTIME systemTime) { odprintf("[EAT] GetLocalTime called!"); GetLocalTime(systemTime); }

void HookMe() { // GetSystemTime PLH::EatHook hook_GST("GetSystemTime", L"kernel32.dll", (char)&Hook_GetSystemTime, (uint64_t)&Original_GetSystemTime); if (!hook_GST.hook()) { odprintf("[EAT] EAT hooking GetSystemTime failed!"); return false; }

// GetLocalTime
PLH::EatHook hook_GLT("GetLocalTime", L"kernel32.dll", (char*)&Hook_GetLocalTime, (uint64_t*)&Original_GetLocalTime);
if (!hook_GLT.hook())
{
    odprintf("[EAT] EAT hooking GetLocalTime failed!");
    return false;
}

}

stevemk14ebr commented 5 years ago

darn probably not enough free pages to alloc one for each export hooked. Ok so i need to make my page walking smarter and re-use the same page for multiple hook types, give me a day or so.

stevemk14ebr commented 5 years ago

Update: 350f49601b1eff8442beefab264095fea59c57a2 adds page-reuse across hooks. Current problem is small PAGE_NO_ACCESS region after every module, WIP: https://www.unknowncheats.me/forum/general-programming-and-reversing/307472-alloc-page-walk-issue.html#post2283408

stevemk14ebr commented 5 years ago

check it now, i think i fixed it

FFFF0h commented 5 years ago

It works ! thanks