源程序程序破解之 API HOOK技术.docx
- 文档编号:30650436
- 上传时间:2023-08-18
- 格式:DOCX
- 页数:15
- 大小:292.47KB
源程序程序破解之 API HOOK技术.docx
《源程序程序破解之 API HOOK技术.docx》由会员分享,可在线阅读,更多相关《源程序程序破解之 API HOOK技术.docx(15页珍藏版)》请在冰豆网上搜索。
源程序程序破解之APIHOOK技术
【源程序】程序破解之APIHOOK技术
APIHOOK,就是截获API调用的技术,在程序对一个API调用之前先执行你的函数,然后根据你的需要可以执行缺省的API调用或者进行其他处理,假设如果想截获一个进程对网络的访问,一般是几个socketAPI:
recv,recvfrom,send,sendto等等,当然你可以用网络抓包工具,这里只介绍通过APIHOOK的方式来实现,主要原理是在程序运行中动态修改目标函数地址的内存数据,使用jmp语句跳转到你的函数地址,执行完后再恢复内存数据,汇编代码是:
moveax,pNewAddr[/size][size=3]jmpeax
读写进程内存方法:
1.读进程内存:
2.VirtualProtect(lpAddress,nSize,PAGE_READONLY,&dwOldProtect);
3.ReadProcessMemory(hProcess,lpAddress,lpBuffer,nSize,&dwRead);
4.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,主要几个函数是:
classCAdHookApi
{
public:
//指定DLL的某个函数进行HOOK
HANDLEAdd(LPCTSTRlpszModule,LPCSTRlpcFuncName,void*pNewAddr,DWORDdwData=0);
//给定一个函数地址进行HOOK
HANDLEAdd(void*pOldAddr,void*pNewAddr,constBYTE*verifyData=NULL,DWORDverifySize=0,DWORDdwData=0);
BOOLRemove(HANDLEhHook);
BOOLBegin(HANDLEhHook);
BOOLEnd(HANDLEhHook);
BOOLBegin2(void*pNewAddr);
BOOLEnd2(void*pNewAddr);
intBeginAll();
intEndAll();
};
举例说明使用方法:
假设一个软件是试用软件,试用7天,最笨的办法就是改本机时间,但如果用APIHOOK技术就可以很容易做到,可以先用CFFExplorer或者Dependency查看一下该软件是调用哪个函数来获取系统当前时间的,假如是GetLocalTime函数(当然获取时间的函数还有很多API),那么我就可以截获GetLocalTime,返回一个永不过期的时间.
1.首先,声明一个全局变量:
staticCAdHookApigHooks;
2.确定要截获API的参数,APIGetLocalTime对应的DLL是KERNEL32.DLL,API定义为:
voidWINAPIGetLocalTime(LPSYSTEMTIMElpSystemTime);
写一个新的函数,定义和原函数保持一致:
voidWINAPImy_GetLocalTime(LPSYSTEMTIMElpSystemTime)
{
#if1
//执行缺省调用
CAdAutoHookApiautoHook(&gHooks,my_GetLocalTime);
GetLocalTime(lpSystemTime);
#else
//改变函数的行为,返回固定的时间
//2012-12-2810:
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;
staticintmy_sub_4026B0(BYTE*pbData)
{
CAdAutoHookApiautoHook(&gHooks,my_sub_4026B0);
sub_4026B0_funcsub_4026B0=(sub_4026B0_func)(0x004026B0);
stringhexData1=toHexString((constchar*)pbData,strlen((constchar*)pbData));
intret=sub_4026B0(pbData);
stringhexData2=toHexString((constchar*)pbData,strlen((constchar*)pbData));
logOutput(formatString("ApiDebugger-sub_4026B0(%s=>%s)",
hexData1.c_str(),hexData2.c_str()));
returnret;
}
constBYTEverifyData[]={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-hooksub_4026B0ok.\r\n"));
}
else
{
logOutput(formatString("ApiDebugger-hooksub_4026B0failed.\r\n"));
}
4.函数首次HOOK是在DLL加载时完成的,DLL入口增加代码:
BOOLAPIENTRYDllMain(HMODULEhModule,DWORDul_reason_for_call,LPVOIDlpReserved)
{
switch(ul_reason_for_call)
{
caseDLL_PROCESS_ATTACH:
{
//截获KERNEL32.DLL的APIGetLocalTime到你的函数地址my_GetLocalTime
gHooks.Add(_T("KERNEL32.DLL"),"GetLocalTime",my_GetLocalTime);
//开始HOOK所有的
gHooks.BeginAll();
}
break;
caseDLL_THREAD_ATTACH:
caseDLL_THREAD_DETACH:
break;
caseDLL_PROCESS_DETACH:
{
gHooks.EndAll();
}
break;
}
returnTRUE;
}
这样就完成了,只要你的DLL加载到一个进程中,相应的函数就被你截获了.
下面谈一下如何让一个程序加载你的DLL,一般有两种方式:
1.修改原程序的ImportTable,增加导入你的DLL(静态加载):
使用工具:
CFFExplorer,是ExplorerSuite(用于PE文件的修改,下面这个操作就是notepad.exe加载rand.dll的操作:
只要RebuildImportTable,然后再Save/SaveAs就可以保存新的文件,这样你的dll就自动的被加载了,DLL加载的时候也就实现了APIHOOK。
这种方式因为对原程序进行了修改,如果程序有CRC校验,运行肯定就不正确了,就需要通过破解去除CRC校验部分的判断.
2.动态DLL加载:
在原程序运行之后,通过APICreateRemoteThread把自己的DLL注入到另一个进程.使用DLL注入工具,这个工具是我多年前写的:
这种方式最大的好处是不需要对原程序进行修改,可以躲避程序CRC校验.
最后例举一些应用场景:
1.加密狗的通用破解方法,仅针对固定数据读取的有效(有算法的加密狗无效):
1)HOOK几个API,加密狗一般最终都是使用CreateFile打开设备,调用APIDeviceIoControl与加密狗进行数据交互:
gHooks.Add(_T("KERNEL32.DLL"),"CreateFileA",my_CreateFileA);
gHooks.Add(_T("KERNEL32.DLL"),"CreateFileW",my_CreateFileW);
gHooks.Add(_T("KERNEL32.DLL"),"DeviceIoControl",my_DeviceIoControl);
staticintgCallCounter=0;
BOOLWINAPImy_DeviceIoControl(HANDLEhDevice,DWORDdwIoControlCode,LPVOIDlpInBuffer,DWORDnInBufferSize,
LPVOIDlpOutBuffer,DWORDnOutBufferSize,LPDWORDlpBytesReturned,LPOVERLAPPEDlpOverlapped
)
{
BOOLret=TRUE;
CAdAutoHookApiautoHook(&gHooks,my_DeviceIoControl);
#if1
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
{
//拔掉后狗直接从已保存的文件中返回数据,实现狗数据的模拟
intnRet=0;
*lpBytesReturned=ReadDataFromFile(formatstring(L"1\\%d.in",gCallCounter).c_str(),lpOutBuffer,nOutBufferSize);
gCallCounter++;
}
#endif
returnret;
}
2)使用刚才提到的方法进行DLL导入
3)带狗保存数据,数据记录完成后,用保存的数据进行狗的模拟
2.360CrackMe加密API的截获,
下图是用给的提示用户名:
360和正确的密码输入,返回APIHOOK的Logoutput:
解密的结果:
5375636365737300000000000000000000000000000000000000000000000000
就是字符串:
Success,HASH数据6C696E67647578就是:
lingdux,如果输入错误的密码,则不会调用到CryptDecrypt相关的函数,因此可能在此之前就有一个预先的判断。
5.写了一个程序对API过程进行了模拟,大概代码是:
//"lingdux"
constBYTEpassword[]={0x6C,0x69,0x6E,0x67,0x64,0x75,0x78};
CryptAcquireContextW(&hCryptProv,NULL,MS_ENHANCED_PROV_W,PROV_RSA_FULL,0);
CryptCreateHash(hCryptProv,CALG_MD5,0,0,&hHash);
CryptHashData(hHash,(BYTE*)password,sizeof(password),0);
CryptDeriveKey(hCryptProv,CALG_3DES_112,hHash,KEYLENGTH,&hKey);
解密:
CryptDecrypt(hKey,0,bEOF,0,pbBuffer,&dwCount);
加密:
CryptEncrypt(hKey,NULL,bEOF,0,pbBuffer,&dwCount,dwBufferLen);
所以综上所述,只要用户名和密码变换后加密的数据是:
1D55F56FED567B9533643A387D13D91FA80C08B173AF80820D5C5E91216546EBCB368D08EEA3691B,就是正确的。
6.对username和password的变换分析:
通过查看程序资源,可以得知usernameEditBox的ID是1001(3E9h),password的ID是1002(3EAh),所以可以很容易知道这个位置是获取输入的代码:
其实到这里,就已经很容易做一个Patch程序来暴利破解了,当然如果要实现注册机,就必须知道程序的具体算法,还需要再进行分析,本人由于时间关系,目前就分析到这里了,不过自己确信最终一定能够分析出算法,等我有时间会再继续。
最后非常感谢360公司举办的大赛,这个CrackMe确实是一个非常不错的,融合了多种反调试/防破解技术,是一个很好的学习程序,在破解的同时自己的水平也有了提高。
1)HOOK以个API:
//HOOKIsDebuggerPresent可以让函数直接返回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);
BOOLWINAPImy_IsDebuggerPresent(VOID)
{
returnFALSE;
}
intWINAPImy_CompareStringW(LCIDLocale,DWORDdwCmpFlags,PCNZWCHlpString1,intcchCount1,
PCNZWCHlpString2,intcchCount2)
{
CAdAutoHookApiautoHook(&gHooks,my_CompareStringW);
logOutput(formatString("ApiDebugger-CompareStringW.\r\n"));
intret=CompareStringW(Locale,dwCmpFlags,lpString1,cchCount1,lpString2,cchCount2);
logOutput(formatString("ApiDebugger-CompareStringW(%S,%S).\r\n",lpString1,lpString2));
returnret;
}
BOOLWINAPImy_CryptAcquireContextW(HCRYPTPROV*phProv,LPCWSTRszContainer,LPCWSTRszProvider,
DWORDdwProvType,DWORDdwFlags)
{
CAdAutoHookApiautoHook(&gHooks,my_CryptAcquireContextW);
BOOLret=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"
));
returnret;
}
BOOLWINAPImy_CryptImportKey(HCRYPTPROVhProv,CONSTBYTE*pbData,DWORDdwDataLen,HCRYPTKEYhPubKey,
DWORDdwFlags,HCRYPTKEY*phKey)
{
CAdAutoHookApiautoHook(&gHooks,my_CryptImportKey);
BOOLret=CryptImportKey(hProv,pbData,dwDataLen,hPubKey,dwFlags,phKey);
stringhexData=toHexString((constchar*)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"
));
returnret;
}
BOOLWINAPImy_CryptCreateHash(HCRYPTPROVhProv,ALG_IDAlgid,HCRYPTKEYhKey,DWORDdwFlags,HCRYPTHASH*phHash)
{
CAdAutoHookApiautoHook(&gHooks,my_CryptCreateHash);
BOOLret=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"
));
returnret;
}
BOOLWINAPImy_CryptHashData(HCRYPTHASHhHash,CONSTBYTE*pbData,DWORDdwDataLen,DWORDdwFlags)
{
CAdAutoHookApiautoHook(&gHooks,my_CryptHashData);
BOOLret=CryptHashData(hHash,pbData,dwDataLen,dwFlags);
stringhexData=toHexString((constchar*)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"
));
returnret;
}
BOOLWINAPImy_CryptDeriveKey(HCRYPTPROVhProv,ALG_IDAlgid,HCRYPTHASHhBaseData,DWORDdwFlags,HCRYPTKEY*phKey)
{
CAdAutoHookApiautoHook(&gHooks,my_CryptDeriveKey);
BOOLret=CryptDeriveKey(
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 源程序程序破解之 API HOOK技术 源程序 程序 破解 HOOK 技术