示例图片三

新闻资讯

联系方式

加密狗复制克隆网
电  话:13528540969
联系人:  刘先生
Email:4642967@qq.com
网  址:www.crackdongle.com
地  址 :广东省深圳市地王大厦38层8801号

当前位置: 网站首页 > 新闻资讯 > 业界资讯
业界资讯

加密狗程序破解之 API HOOK技术

API HOOK,就是截获API调用的技术,在程序对一个API调用之前先执行你的函数,然后根据你的需要可以执行缺省的API调用或者进行其他处理,假设如果想截获一个进程对网络的访问,一般是几个socket API : recv,recvfrom, send, sendto等等,当然你可以用网络抓包工具,这里只介绍通过API HOOK的方式来实现, 主要原理是在程序运行中动态修改目标函数地址的内存数据,使用jmp语句跳转到你的函数地址,执行完后再恢复内存数据, 汇编代码是:

    mov eax, pNewAddr jmp eax

读写进程内存方法:1.读进程内存:    VirtualProtect(lpAddress, nSize, PAGE_READONLY, &dwOldProtect);    ReadProcessMemory(hProcess, lpAddress, lpBuffer, nSize, &dwRead);    VirtualProtect(lpAddress, nSize, dwOldProtect, &dwOldProtect);

 

2.写进程内存:

 

    VirtualProtect(lpAddress, nSize, PAGE_READWRITE, &dwOldProtect);    WriteProcessMemory(hProcess, lpAddress, lpBuffer, nSize, &dwWrite);    VirtualProtect(lpAddress, nSize, dwOldProtect, &dwOldProtect);

 

在很多年前这种技术非常的流行,有各种各样的工具和SDK,我自己也实现了一个C++ class,名为 CAdHookApi, 主要几个函数是:

 

    class CAdHookApi      {    public:        // 指定DLL的某个函数进行HOOK        HANDLE Add(LPCTSTR lpszModule, LPCSTR lpcFuncName, void *pNewAddr, DWORD dwData = 0);        // 给定一个函数地址进行HOOK        HANDLE Add(void *pOldAddr, void *pNewAddr, const BYTE *verifyData = NULL, DWORD verifySize = 0, DWORD dwData = 0);        BOOL   Remove(HANDLE hHook);        BOOL   Begin(HANDLE hHook);        BOOL   End(HANDLE hHook);        BOOL   Begin2(void *pNewAddr);        BOOL   End2(void *pNewAddr);        int    BeginAll();        int    EndAll();    };

 

举例说明使用方法:

    假设一个软件是试用软件,试用7天,最笨的办法就是改本机时间,但如果用API HOOK技术就可以很容易做到,可以先用CFF Explorer或者Dependency查看一下该软件是调用哪个函数来获取系统当前时间的,假如是GetLocalTime函数(当然获取时间的函数还有很多API),那么我就可以截获GetLocalTime,返回一个永不过期的时间.

 

1.首先,声明一个全局变量:

 

    static CAdHookApi     gHooks;

 

 

2.确定要截获API的参数,API GetLocalTime对应的DLL是KERNEL32.DLL, API定义为:

 

    void WINAPI GetLocalTime(LPSYSTEMTIME lpSystemTime);

 

    写一个新的函数,定义和原函数保持一致:

 

    void WINAPI my_GetLocalTime(LPSYSTEMTIME lpSystemTime)    {    #if 1    // 执行缺省调用        CAdAutoHookApi autoHook(&gHooks, my_GetLocalTime);        GetLocalTime(lpSystemTime);    #else    // 改变函数的行为,返回固定的时间        // 2012-12-28 10:00:00        lpSystemTime->wYear         = 2012;        lpSystemTime->wMonth        = 12;        lpSystemTime->wDayOfWeek    = 0;        lpSystemTime->wDay          = 28;        lpSystemTime->wHour         = 10;        lpSystemTime->wMinute       = 0;        lpSystemTime->wSecond       = 0;        lpSystemTime->wMilliseconds = 0;    #endif    }

 

 

3.直接HOOK已知的函数地址:

    如果已知函数地址和函数定义,可以直接对地址进行HOOK,在HOOK之前还可以先对内存数据进行检验,只有数据一致才HOOK.

 

// 004026B0 ; static int my_sub_4026B0(BYTE *pbData){CAdAutoHookApi autoHook(&gHooks, my_sub_4026B0);sub_4026B0_func sub_4026B0 = (sub_4026B0_func)(0x004026B0);string hexData1 = toHexString((const char *)pbData, strlen((const char *)pbData));int ret = sub_4026B0(pbData);string hexData2 = toHexString((const char *)pbData, strlen((const char *)pbData));logOutput(formatString("ApiDebugger - sub_4026B0(%s=>%s)",hexData1.c_str(), hexData2.c_str()));return ret;}const BYTE verifyData[] = { 0x55, 0x8B, 0xEC, 0x81, 0xEC, 0x2C, 0x01, 0x00, 0x00 };void *addr = (void *)0x004026B0;if(gHooks.Add(addr, my_sub_4026B0, verifyData, sizeof(verifyData), 0) != NULL){logOutput(formatString("ApiDebugger - hook sub_4026B0 ok.\r\n"));}else{logOutput(formatString("ApiDebugger - hook sub_4026B0 failed.\r\n"));}

 

4.函数首次HOOK是在DLL加载时完成的,DLL入口增加代码:

 

    BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)    {        switch (ul_reason_for_call)        {        case DLL_PROCESS_ATTACH:            {                // 截获KERNEL32.DLL的API GetLocalTime到你的函数地址my_GetLocalTime                gHooks.Add(_T("KERNEL32.DLL"), "GetLocalTime", my_GetLocalTime);                // 开始HOOK所有的                gHooks.BeginAll();            }            break ;        case DLL_THREAD_ATTACH:        case DLL_THREAD_DETACH:            break ;        case DLL_PROCESS_DETACH:            {                gHooks.EndAll();            }            break;        }        return TRUE;    }

 

 

这样就完成了,只要你的DLL加载到一个进程中,相应的函数就被你截获了.

 

下面谈一下如何让一个程序加载你的DLL,一般有两种方式:

1.修改原程序的Import Table,增加导入你的DLL(静态加载):

    使用工具:CFF Explorer,是Explorer Suite(http://www.ntcore.com/)中的一个工具 用于PE文件的修改,下面这个操作就是让notepad.exe加载rand.dll的操作:

221913llfhl1wys2ywzoau.jpg

 

    只要Rebuild Import Table,然后再Save/Save As就可以保存新的文件,这样你的dll就自动的被加载了,DLL加载的时候也就实现了API HOOK。

 

    这种方式因为对原程序进行了修改,如果程序有CRC校验,运行肯定就不正确了,就需要通过破解去除CRC校验部分的判断.

 

2.动态DLL加载:

    在原程序运行之后,通过API CreateRemoteThread 把自己的DLL注入到另一个进程.使用DLL注入工具,这个工具是我多年前写的:

222001tvbo4eweyz122dv4.jpg

 

    这种方式最大的好处是不需要对原程序进行修改,可以躲避程序CRC校验.

 

最后例举一些应用场景:

 

1.加密狗的通用破解方法,仅针对固定数据读取的有效(有算法的加密狗无效):

    1)HOOK几个API,加密狗一般最终都是使用CreateFile打开设备,调用API DeviceIoControl与加密狗进行数据交互:

 

        gHooks.Add(_T("KERNEL32.DLL"),  "CreateFileA",                      my_CreateFileA);        gHooks.Add(_T("KERNEL32.DLL"),  "CreateFileW",                      my_CreateFileW);        gHooks.Add(_T("KERNEL32.DLL"),  "DeviceIoControl",                  my_DeviceIoControl);        static int gCallCounter = 0;        BOOL WINAPI my_DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,                                       LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped                                       )        {            BOOL ret = TRUE;            CAdAutoHookApi autoHook(&gHooks, my_DeviceIoControl);        #if 1            ret = DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize,                lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped);            if(ret)            {                // 带狗时记录数据                WriteDataToFile(formatstring(L"1\\%d.in",   gCallCounter).c_str(), lpInBuffer, nInBufferSize);                gCallCounter ++;            }        #else            {                // 拔掉后狗直接从已保存的文件中返回数据,实现狗数据的模拟                int nRet = 0;                *lpBytesReturned = ReadDataFromFile(formatstring(L"1\\%d.in",  gCallCounter).c_str(), lpOutBuffer, nOutBufferSize);                gCallCounter ++;            }        #endif            return ret;        }

 

    2)使用刚才提到的方法进行DLL导入

    3)带狗保存数据,数据记录完成后,用保存的数据进行狗的模拟

 

2. 360 CrackMe加密API的截获

 

    1)HOOK以个API:

 

    // HOOK IsDebuggerPresent可以让函数直接返回FALSE    gHooks.Add(_T("KERNEL32.DLL"),      "IsDebuggerPresent",        my_IsDebuggerPresent);    gHooks.Add(_T("ADVAPI32.DLL"),      "CryptAcquireContextW",     my_CryptAcquireContextW);    gHooks.Add(_T("ADVAPI32.DLL"),      "CryptImportKey",           my_CryptImportKey);    gHooks.Add(_T("ADVAPI32.DLL"),      "CryptCreateHash",          my_CryptCreateHash);    gHooks.Add(_T("ADVAPI32.DLL"),      "CryptHashData",            my_CryptHashData);    gHooks.Add(_T("ADVAPI32.DLL"),      "CryptDeriveKey",           my_CryptDeriveKey);    gHooks.Add(_T("ADVAPI32.DLL"),      "CryptDecrypt",             my_CryptDecrypt);    BOOL WINAPI my_IsDebuggerPresent(VOID)    {        return FALSE;    }    int WINAPI my_CompareStringW(LCID Locale, DWORD dwCmpFlags, PCNZWCH lpString1, int cchCount1,                               PCNZWCH lpString2,int cchCount2)    {        CAdAutoHookApi autoHook(&gHooks, my_CompareStringW);        logOutput(formatString("ApiDebugger - CompareStringW.\r\n"));        int ret = CompareStringW(Locale, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2);        logOutput(formatString("ApiDebugger - CompareStringW(%S, %S).\r\n", lpString1, lpString2));        return ret;    }    BOOL WINAPI my_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR szContainer, LPCWSTR szProvider,                                      DWORD dwProvType, DWORD dwFlags)    {        CAdAutoHookApi autoHook(&gHooks, my_CryptAcquireContextW);        BOOL ret = CryptAcquireContextW(phProv, szContainer, szProvider, dwProvType, dwFlags);        logOutput(formatString("ApiDebugger - CryptAcquireContextW(0x%08X, %S, %S, 0x%08X, 0x%08X) : %S.\r\n",            (int)(*phProv),            (szContainer != NULL) ? szContainer : L"NULL",            (szProvider != NULL) ? szProvider : L"NULL",            dwProvType, dwFlags,            ret ? L"TRUE" : L"FALSE"            ));        return ret;    }    BOOL WINAPI my_CryptImportKey(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen, HCRYPTKEY hPubKey,                               DWORD dwFlags, HCRYPTKEY *phKey)    {        CAdAutoHookApi autoHook(&gHooks, my_CryptImportKey);        BOOL ret = CryptImportKey(hProv, pbData, dwDataLen, hPubKey, dwFlags, phKey);        string hexData = toHexString((const char *)pbData, dwDataLen);        logOutput(formatString("ApiDebugger - CryptImportKey(0x%08X, %s, 0x%08X, 0x%08X, 0x%08X) : %S.\r\n",            (int)hProv, hexData.c_str(), (int)hPubKey, dwFlags, (int)(*phKey),             ret ? L"TRUE" : L"FALSE"            ));        return ret;    }    BOOL WINAPI my_CryptCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash)    {        CAdAutoHookApi autoHook(&gHooks, my_CryptCreateHash);        BOOL ret = CryptCreateHash(hProv, Algid, hKey, dwFlags, phHash);        logOutput(formatString("ApiDebugger - CryptCreateHash(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X) : %S.\r\n",            (int)hProv, (int)Algid, (int)hKey, dwFlags, (int)phHash,            ret ? L"TRUE" : L"FALSE"            ));        return ret;    }    BOOL WINAPI my_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD dwDataLen, DWORD dwFlags)    {        CAdAutoHookApi autoHook(&gHooks, my_CryptHashData);        BOOL ret = CryptHashData(hHash, pbData, dwDataLen, dwFlags);        string hexData = toHexString((const char *)pbData, dwDataLen);        logOutput(formatString("ApiDebugger - CryptHashData(0x%08X, %s, 0x%08X) : %S.\r\n",            (int)hHash, hexData.c_str(), dwFlags,            ret ? L"TRUE" : L"FALSE"            ));        return ret;    }    BOOL WINAPI my_CryptDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData, DWORD dwFlags, HCRYPTKEY *phKey)    {        CAdAutoHookApi autoHook(&gHooks, my_CryptDeriveKey);        BOOL ret = CryptDeriveKey(hProv, Algid, hBaseData, dwFlags, phKey);        logOutput(formatString("ApiDebugger - CryptDeriveKey(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X) : %S.\r\n",            (int)hProv, (int)Algid, (int)hBaseData, dwFlags, (int)phKey,            ret ? L"TRUE" : L"FALSE"            ));        return ret;    }    BOOL WINAPI my_CryptDecrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags,                                BYTE *pbData, DWORD *pdwDataLen)    {        CAdAutoHookApi autoHook(&gHooks, my_CryptDecrypt);        string hexData1 = toHexString((const char *)pbData, *pdwDataLen);        writeDataToFile("CryptDec_IN.bin", pbData, *pdwDataLen);        BOOL ret = CryptDecrypt(hKey, hHash, Final, dwFlags, pbData, pdwDataLen);        string hexData2 = toHexString((const char *)pbData, *pdwDataLen);        writeDataToFile("CryptDec_OUT.bin", pbData, *pdwDataLen);        logOutput(formatString("ApiDebugger - CryptDecrypt(0x%08X, 0x%08X, %S, 0x%08X, %s=>%s) : %S.\r\n",            (int)hKey, (int)hHash, Final ? L"TRUE" : L"FALSE",            dwFlags, hexData1.c_str(), hexData2.c_str(),             ret ? L"TRUE" : L"FALSE"            ));        return ret;    }

 

3. 程序网络访问抓包,前面提到的几个api : recv,recvfrom, send, sendto, ...

 

当然API HOOK的功能还远不止这些,可以分析目标程序的特点做更多的处理,有时间我再写一个用这种方法破解HASP SRM AES-128加密狗的经验吧。

分享到:
点击次数:  更新时间:2018-08-05 10:00:58  【打印此页】  【关闭