当人们通常钩住一个函数时,他们用分支指令将要钩挂的函数的前几个指令修改到他们想做的事情,然后分支回到原始函数并还原它,问题在于实际上什么都没有安全。您试图隐藏的任何值都可以轻松找到(这些值可以通过函数挂钩而不是其他方式找到,但这就是我在此问题中所关注的全部)

假设您要努力在C++应用程序中实现像MD5这样的哈希算法(我还没有,这只是一个示例),仅出于此示例的考虑,假设您具有这样的MD5函数
void GENERATEMD5(const char *plain, char *out);
你会这样称呼它

char hashResult[33] = { 0 };//32 + 1 because of null terminator
GENERATEMD5(passwordInputBuffer, hashResult);
memset(passwordInputBuffer, 0, 32);//set password buf back to null

任何人都可以轻松地钩住这个GERERATEMD5()函数,并在将参数传递给函数时简单地将其打印出来。例
void md5FuncHook(const char *plain, char *out)
{
    md5Hook(plain, out);

    console::print("Plain: %s - Hash: %s", plain, out);
}

我在考虑这个问题,只有一种方法可以检测出函数是否已被钩住(假设他们通过修改函数的前几条指令来钩住函数)。那将是检查该函数的前几个字节,然后确认它们是应该的。
例如,如果我们知道函数GERERATEMD5()的前几个字节是
int GERERATEMD5_Function_bytes_0 = 0x12341234;//just random bytes for the example
int GERERATEMD5_Function_bytes_1 = 0x12341234;//just random bytes for the example
int GERERATEMD5_Function_bytes_2 = 0x12341234;//just random bytes for the example
int GERERATEMD5_Function_bytes_3 = 0x12341234;//just random bytes for the example

然后我们可以做这样的事情
void checkIfGENERATEMD5HasBeenHooked()
{
    int GERERATEMD5_Function_bytes_0 = 0x12341234;//just random bytes for the example
    int GERERATEMD5_Function_bytes_1 = 0x12341234;//just random bytes for the example
    int GERERATEMD5_Function_bytes_2 = 0x12341234;//just random bytes for the example
    int GERERATEMD5_Function_bytes_3 = 0x12341234;//just random bytes for the example

    int readGENERATEMD5FunctionBytes0, readGENERATEMD5FunctionBytes1, readGENERATEMD5FunctionBytes2, readGENERATEMD5FunctionBytes3;
    memcpy(&readGENERATEMD5FunctionBytes0, (char *)(&GENERATEMD5 + 0x00), 0x04);
    memcpy(&readGENERATEMD5FunctionBytes1, (char *)(&GENERATEMD5 + 0x04), 0x04);
    memcpy(&readGENERATEMD5FunctionBytes2, (char *)(&GENERATEMD5 + 0x08), 0x04);
    memcpy(&readGENERATEMD5FunctionBytes3, (char *)(&GENERATEMD5 + 0x0C), 0x04);

    if(GERERATEMD5_Function_bytes_0 == readGENERATEMD5FunctionBytes0 && GENERATEMD5_Function_bytes_1 == readGENERATEMD5FunctionBytes1 && GENERATEMD5_Function_bytes_2 == readGENERATEMD5FunctionBytes2 && GENERATEMD5_Function_bytes_3 == readGENERATEMD5FunctionBytes3)
    {
        //our GENERATEMD5() function is clean
    }
    else
    {
        //hook detected or some other form of function modification detected
    }
}

但是,尽管我尝试了一切,但似乎从未奏效。我假设的问题来自读取函数本身字节的位置,例如对memcpy的调用实际上并未读取位于&GENERATEMD5 + OFFSET的字节。我是在做错什么吗?还是有一种更好/不同的方式来完成我要完成的工作? (顺便说一句,是的,我知道攻击者在钩住函数时可能会使用除我上面所述以外的其他许多钩子(Hook)方法,并且通常还有许多其他方法可从可执行文件中获取敏感信息,但是这个问题有与此无关,所以请专注于问题,而不是只说“做到这一点是没有意义的”或“有简单的方法可以绕开它”之类的话,等等。)

最佳答案

我已经做到了。问题在于,加载程序将在重定位期间修改代码,因此您不能每次都依赖于操作数字段具有相同的值。就我而言,我使用了反汇编库,并且仅对操作码字节进行了哈希处理。我使用BASTARD在运行时进行反汇编,但该项目已久违。我认为现在有更好的选择。

原则上,您可以以不会发生重新定位修正的方式编写目标函数,但这将带来更多麻烦,而不是值得的。

09-05 12:49