程序破解之 API HOOK技术 z
API HOOK,就是截获API调用的技术,在程序对一个API调用之前先执行你的函数,然后根据你的需要可以执行缺省的API调用或者进行其他处理,假设如果想截获一个进程对网络的访问,一般是几个socket API : recv,recvfrom, send, sendto等等,当然你可以用网络抓包工具,这里只介绍通过API HOOK的方式来实现, 主要原理是在程序运行中动态修改目标函数地址的内存数据,使用jmp语句跳转到你的函数地址,执行完后再恢复内存数据, 汇编代码是:
mov eax, pNewAddr[/size][size=3] 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 = );
// 给定一个函数地址进行HOOK
HANDLE Add(void *pOldAddr, void *pNewAddr, const BYTE *verifyData = NULL, DWORD verifySize = , DWORD dwData = );
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 = ;
lpSystemTime->wMonth = ;
lpSystemTime->wDayOfWeek = ;
lpSystemTime->wDay = ;
lpSystemTime->wHour = ;
lpSystemTime->wMinute = ;
lpSystemTime->wSecond = ;
lpSystemTime->wMilliseconds = ;
#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), ) != 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的操作:
只要Rebuild Import Table,然后再Save/Save As就可以保存新的文件,这样你的dll就自动的被加载了,DLL加载的时候也就实现了API HOOK。
这种方式因为对原程序进行了修改,如果程序有CRC校验,运行肯定就不正确了,就需要通过破解去除CRC校验部分的判断.
2.动态DLL加载:
在原程序运行之后,通过API CreateRemoteThread 把自己的DLL注入到另一个进程.使用DLL注入工具,这个工具是我多年前写的:
这种方式最大的好处是不需要对原程序进行修改,可以躲避程序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 = ;
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 = ;
*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:
// 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的工程).
程序破解之 API HOOK技术 z的更多相关文章
- API HOOK技术
API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...
- VB程序破解之API断点[bp __vbaVarTstEq]
软件名称:风云足彩1.7软件大小:2.1M下载地址:http://free.ys168.com/?zhinengxuanhao软件保护:注册码编写软件:Microsoft Visual Basic 5 ...
- Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术
catalogue 1. 引言2. 使用注册表注入DLL3. 使用Windows挂钩来注入DLL4. 使用远程线程来注入DLL5. 使用木马DLL来注入DLL6. 把DLL作为调试器来注入7. 使用c ...
- API HOOK
API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...
- API Hook完全手册
文章来源: http://blog.csdn.net/atfield 原文作者: ATField 整理日期: 2008-07-16 发表评论 字体大小: 小 中 大 注:本文是根据我两年前写的一个 ...
- API HOOK和PE文件的关系
api hook技术的难点,并不在于hook技术,而在于对PE结构的学习和理解.如何修改api函数的入口地址?这就需要学习pe可执行文件(.exe,.dll等)如何被系统映射到进程空间中,这需要学习p ...
- 20145319 《网络渗透》免考—API拦截技术
20145319 <网络渗透>免考-API拦截技术 概述 本次实验在window环境下进行,主要通过编写hook代码和注入程序,将我们的hook代码通过dll文件的形式注入到目标中,拦截其 ...
- Windows API Hook
原文地址:http://blog.sina.com.cn/s/blog_628821950100xmuc.html 原文对我的帮助极大,正是由于看了原文.我才学会了HOOK.鉴于原文的排版不是非常好, ...
- API Hook基本原理和实现
API Hook基本原理和实现 2009-03-14 20:09 windows系统下的编程,消息message的传递是贯穿其始终的.这个消息我们可以简单理解为一个有特定意义的整数,正如我们看过的老故 ...
随机推荐
- hdu 1063 Exponentiation
求实数的幂,这个用C++写的话有点长,但是用Java写就非常方便了…… ); System.out.println(an); } }}
- poj 3358 Period of an Infinite Binary Expansion
由乘2取整得到分数的小数位,可以找到规律!!! 例如:1/10,2/10,4/10,8/10,16/10,32/10,64/10…… 取整后:1/10,2/10,4/10,8/10,6/10,2/10 ...
- hdu 1370 Biorhythms
中国剩余定理……. 链接http://acm.hdu.edu.cn/showproblem.php?pid=1370 /**************************************** ...
- [hackerrank]John and GCD list
https://www.hackerrank.com/contests/w8/challenges/john-and-gcd-list 简单题,GCD和LCM. #include <vector ...
- LR_问题_在导入wsdl时出现parsing error
问题描述:使用LR录制webservice协议的脚本,在导入wsdl时出现parsing error,详见图 问题解决:在导入wsdl时输入的地址错误,只指定了地址的虚拟目录名称,未指定方法名称,应该 ...
- stringUtils是apache下的Java jar补充包
org.apache.commons.lang.StringUtils StringUtils中一共有130多个方法,并且都是static的, 所以我们可以这样调用StringUtils.xxx().
- 猜拳 GuessFist
import java.util.Scanner;import java.util.Random;/***跟电脑玩石头剪刀布,需要从控制台输入信息,*然后去判断,然后给予反馈信息*/public cl ...
- C#基础精华----枚举
enums枚举是值类型,数据直接存储在栈中,而不是使用引用和真实数据的隔离方式来存储. (1)默认情况下,枚举中的第一个变量被赋值为0,其他的变量的值按定义的顺序来递增(0,12,3...),因此以下 ...
- C++:派生类的构造函数和析构函数
4.2 派生类的构造函数和析构函数4.2.1 派生类构造函数和析构函数的执行顺序 通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数:当撤销派生类对象时,则先执行派生类 ...
- 转:socket编程在windows和linux下的区别
如无其它说明,本文所指Linux均表示2.6内核Linux,GCC编译器,Windows均表示Windows XP系统,Visual Studio 2005 sp1编译环境. 下面大概分几个方面进行罗 ...