吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 110747|回复: 199
收起左侧

[原创] [源程序]程序破解之 API HOOK技术

    [复制链接]
codelive 发表于 2014-5-8 22:56
本帖最后由 codelive 于 2014-5-8 23:04 编辑

作为刚加入吾爱的新人,希望能先分享我在破解方面的一些心得和技术,有不对的地方和更好的建议还请大牛和大神们指点,欢迎交流。

本帖提供破解360 CrackMe的API HOOK部分的全部源代码!!!

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


读写进程内存方法:
1.读进程内存:
[C++] 纯文本查看 复制代码
1
2
3
VirtualProtect(lpAddress, nSize, PAGE_READONLY, &dwOldProtect);
ReadProcessMemory(hProcess, lpAddress, lpBuffer, nSize, &dwRead);
VirtualProtect(lpAddress, nSize, dwOldProtect, &dwOldProtect);


2.写进程内存:
[C++] 纯文本查看 复制代码
1
2
3
VirtualProtect(lpAddress, nSize, PAGE_READWRITE, &dwOldProtect);
WriteProcessMemory(hProcess, lpAddress, lpBuffer, nSize, &dwWrite);
VirtualProtect(lpAddress, nSize, dwOldProtect, &dwOldProtect);


在很多年前这种技术非常的流行,有各种各样的工具和SDK,我自己也实现了一个C++ class,名为 CAdHookApi, 主要几个函数是:
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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.首先,声明一个全局变量:
[C++] 纯文本查看 复制代码
1
static CAdHookApi     gHooks;


2.确定要截获API的参数,API GetLocalTime对应的DLL是KERNEL32.DLL, API定义为:
[C++] 纯文本查看 复制代码
1
void WINAPI GetLocalTime(LPSYSTEMTIME lpSystemTime);

    写一个新的函数,定义和原函数保持一致:
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
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.
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 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入口增加代码:
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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的操作:
62a8419agd1dfea3ebfcc&690.jpg

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

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

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

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

最后例举一些应用场景:

1.加密狗的通用破解方法,仅针对固定数据读取的有效(有算法的加密狗无效):
    1)HOOK几个API,加密狗一般最终都是使用CreateFile打开设备,调用API DeviceIoControl与加密狗进行数据交互:

[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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的截获,这部分在我的一个帖子中有提到
    http://www.52pojie.cn/thread-257062-1-1.html


    1)HOOK以个API:
[C++] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
// 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加密狗的经验吧。


总结:虽然API HOOK是相对比较老的一门技术,但很多时候如果能结合这种方法就不需要花很大的精力去脱五花八门的壳和分析反汇编代码,直接锁定核心的API的调用,能够快速的进行数据分析,而且也不用对原有程序进行Patch, 简单有效.
另外,本人很少发帖,难免有错字和问题,欢迎批评指正。

更多细节和代码请下载附件源程序(VS2012的工程).








ApiDebugger360.source.zip

60.33 KB, 下载次数: 1315, 下载积分: 吾爱币 -1 CB

源程序

ApiHOOK.360CrackMe.zip

1.65 MB, 下载次数: 1800, 下载积分: 吾爱币 -1 CB

360CM

免费评分

参与人数 35吾爱币 +4 热心值 +35 收起 理由
qwerttqqaz + 1 + 1 我很赞同!
microhappy + 1 + 1 谢谢@Thanks!
MaxMadcc + 1 + 1 我很赞同!
miaohaojian123 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
KKcracker + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
水孩子 + 1 谢谢@Thanks!
yahong1001 + 1 我很赞同!
niuniu919 + 1 谢谢@Thanks!
GodIand + 1 受教了,感谢前辈!
WFXL + 1 我很赞同!
yber + 1 我很赞同!
Macc + 1 谢谢@Thanks!
773827986 + 1 热心回复!
YHZX_2013 + 1 学习了
dreamnyj + 1 好贴必顶啊。这个技术很管用的
wk544863858 + 1 我很赞同!
83988102 + 1 我很赞同!
chunjun_yao + 1 已答复!
yutao531315 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
love110 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
blmk + 1 我很赞同!
wanttobeno + 1 谢谢@Thanks!
光之优雅 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
www52pojiecn + 1 谢谢@Thanks!
拓海真一 + 1 膜拜ing
Godfather.Cr + 1 绝对的高质量!膜拜!
z6862 + 1 大神 求带!
小六升臣 + 1 我很赞同!
bisoso + 1 是否传授hook send拦截之类的 再写遍教材,.
你与明日 + 1 抓获一只大神!
御剑 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
brack + 1 略屌,受教了.
Shark恒 + 1 感谢,希望看到更多。
1094483658 + 1 热心回复,膜拜
小小小青年 + 1 膜拜大大呀。

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

易木马 发表于 2014-5-14 08:59
这回真张见识了!!
dw783701519 发表于 2014-5-21 19:13
裤裤 发表于 2014-5-28 09:19
ljtnine 发表于 2014-5-14 09:03
感谢分享
nevsayno 发表于 2014-5-17 13:52
很漂亮的代码,赞一个
飞蛾 发表于 2014-5-17 09:54
尼玛 高人年年有 今年特别多!
gravebanner 发表于 2014-5-22 00:42
我还是新手啊,,感觉还是跟不上节奏,,慢慢来吧
anyangmvp 发表于 2014-5-28 10:29
360的工程师,是不是都要学会汇编 呀。。
totowa 发表于 2014-6-5 12:07
好吧 完全看不懂,
clover201113 发表于 2014-6-5 01:08
大神  专门来顶一个  谢谢这么详细的教程
ht512 发表于 2014-5-8 23:00
整理的很全,楼主辛苦了!
qq7156792 发表于 2014-5-8 23:07
支持了,谢谢分享
wap13772 发表于 2014-5-8 23:08
牛人必顶
wx88866 发表于 2014-5-8 23:09
学习中,多看小说话
currwin 发表于 2014-5-8 23:15
很有用。真的非常感谢
头像被屏蔽
892644330 发表于 2014-5-8 23:15
可怜的360cm忘记加检测是否被虎克了 就这样被法克了..
Shock 发表于 2014-5-8 23:16
膜拜楼主  的确很好 值得收藏 可惜没语音视频 带上那个语音视频 我相信你在论坛很快就会变成风云人物的 细节上还要精致一点那就更完美 还有你所用到的一些工具 全部带上 吾爱有你更精彩
wlc120 发表于 2014-5-8 23:17
牛人必顶,谢谢;uozhu1
心断空 发表于 2014-5-8 23:23 来自手机
楼主大神。好深奥
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-5 18:47

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表