使用C++钩取GetTickCount函数

5

我不太擅长C++,更喜欢用C#和PHP。我被分配了一个需要使用GetTickCount并钩入应用程序的项目。但现在有些困难,因为它似乎没有按计划工作...这是钩入的代码,我知道它能工作,因为我以前在项目中使用过。唯一让我不确定的是其中的GetTickCount部分。我尝试使用GetTickCount64,认为它可以解决我的问题(它没有崩溃我注入的内容),但后来发现它根本不起作用,所以也没有崩溃。

bool APIENTRY DllMain(HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
{
 switch(dwReason)
 {
 case DLL_PROCESS_ATTACH:

  DisableThreadLibraryCalls(hDll);
  CreateThread(0,0, (LPTHREAD_START_ROUTINE)KeyHooks, 0, 0, 0);
  GetTickCount_orig = (DWORD (__stdcall *)(void))DetourFunction((PBYTE)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetTickCount"), (PBYTE)GetTickCount_hooked);

 case DLL_PROCESS_DETACH:
  DetourRemove((PBYTE)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetTickCount"), (PBYTE)GetTickCount_hooked);

  break;
 }
 return true;
}

以下是用于 GetTickCount 的代码的其余部分。
DWORD oldtick=0;
DWORD (WINAPI *GetTickCount_orig)(void);
DWORD WINAPI GetTickCount_hooked(void)
{ 
 if(oldtick==0)
 {
  oldtick=(*GetTickCount_orig)();
  return oldtick;
 }
 DWORD factor;
 DWORD ret;

 ret = (*GetTickCount_orig)();
 factor = 3.0;
 DWORD newret;

 newret = ret+((oldtick-ret)*(factor-1));

 oldtick=ret;
 return newret; 
}

您是否发现了任何错误或需要更改的地方?我们非常感谢您的帮助。谢谢!


这里的实际问题是什么? - wj32
尝试注入时导致应用程序崩溃。我的 QueryPerformanceCounter 注入正常,没有问题。 - E3pO
9
在 case DLL_PROCESS_DETACH 前加入 break; 怎么样?因为现在的写法是,在初始化之后立即移除钩子。 - valdo
2个回答

3

"KeyHooks"线程是什么?如果它期望调用被劫持的API,那么在创建线程之前你应该进行API劫持。

GetTickCount_orig是否有设置?

GetTickCount可能是一个非常短的API,这对Detours来说会造成问题(字节数不足以进行挂钩)。

您的DetourRemove正在移除GetTickCount64而不是GetTickCount。

此外,如果Detours不起作用,还有mhook库,其许可证更加简单。


所有的Keyhooks所做的就是查看用户是否按住了Shift键。void KeyHooks(void){while(true){makemetrue = false;while(GetAsyncKeyState(0x14)){makemetrue = true;}Sleep(50);}} 另外...我不确定GetTickCount_orig是否已经设置。当我没有GetTickCount64时,它会导致我要注入的内容崩溃。 - E3pO
拿出你的调试器,看看GetTickCount_orig是否已设置。此外,注意valdo上面提到的缺失的break语句(我简直不敢相信我没看到那个:))。 - Graham Perks

1

不要修改 oldtick

你只需要保存一次,然后就可以了

// accelerating time by factor of "factor"
return oldtick + (realtick - oldtick) * factor;

编辑:

另一个可能的问题是,GetTickCount(至少在我的电脑上,XP 32位)没有标准的“可挂钩”序言:

8B FF     mov     edi, edi
55        push    ebp
8B EC     mov     ebp, esp

如果没有这个,它只能从IAT中挂钩,而且每个调用它的模块都必须这样做。我怀疑DetourFunction是按进程工作的,因此使用序言来挂钩API。

要解决这个问题,您可以尝试挂钩每个模块的IAT,或手动修补它,但这样一来,在挂钩时就无法调用原始版本。

EDIT2:使用跳转是最常见的方法,但这意味着我们必须覆盖函数开头的5个字节。它的主要问题不是函数的大小,而是其开头的代码。当然,任何东西都可以被覆盖,但是如果您想在挂钩打开时调用旧函数(如本问题),则必须知道您正在覆盖什么。
您不想覆盖半个操作码,并且必须执行覆盖部分。这意味着在通用情况下,您将需要一个完整的反汇编器。

为了简化这个过程,大多数函数都以额外的2字节NOP开头:mov edi, edi,因此它们的序言有5个标准且易于重定位的字节。


我认为Detours并不愚蠢,它可以与任意指令一起使用。他们不是提出了“蹦床”钩子的想法吗? - wj32
@E3pO:所以我是对的 :) 它被jmp无条件地修补,因此在挂钩时无法调用原始函数。那么你就必须手动执行它。 - ruslik
1
@ruslik: 这是为了使打补丁更容易。这并不意味着在开头没有填充的情况下无法打补丁。 @E3pO: 我认为你需要使用DetourFunctionWithTrampoline。 - wj32
它没有崩溃,但我认为它什么也没做... 我可能错了。 - E3pO
有可能有人不介意私信我他们的Aim或Skype吗?弄清楚这个会很好。我愿意付费。 - E3pO
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接