补丁技术应用(10千字),补丁,逆向工程技术 2008年06月24日 星期二 下午 04:05 标 题: 补丁技术应用 关于本文:本文主要目的在于教学,让初学者如何应用补丁技术修补程序,只从技术角度探讨一下 请勿将此教程用于商业目的。在这首先感谢dreamtheater在Win32Asm编程方面的帮助! =========================================================================== 第一部分:程序分析 该程序是一网络客户端程序,每台机子可免费试用10天,如需继续使用则要购买正式帐号。程序拦截过程就不 写了,主要验证代码如下: 0167:004ABEC0 MOV ECX,EAX 0167:004ABEC2 CALL 00547B69 0167:004ABEC7 JMP 004AC228 0167:004ABECC MOV DWORD [0067B9C8],01 0167:004ABED6 MOV EAX,[EBX 64] 0167:004ABED9 PUSH EAX //机器号入栈(一机一号,并在其服务器登记,只能用10天) 0167:004ABEDA CALL `CHANNEL!CheckTryUserWhenLogOn`//在服务上验证用户合法性 0167:004ABEDF CMP EAX,BYTE -01 //EAX=0用户试用期己到;EAX=FFFFFFFF,网络连接超时; //EAX为其它值时可以继续试用(若强行修改其值以欺骗服务器,无效)。 0167:004ABEE2 MOV [ESP 30],EAX 0167:004ABEE6 JZ NEAR 004AC1F4//当EAX=FFFFFFFF时跳,报“网络连接超时”; 0167:004ABEEC CMP EAX,ESI 0167:004ABEEE JNZ 004ABF51 0167:004ABEF0 CALL `CHANNEL!CloseChannel`//如上述EAX==0,则断开与服务器连接 0167:004ABEF5 PUSH ESI 所以该程序破解相当简单,当过期后,只要将0167:004ABED9 PUSH EAX 入栈的机器号换一数又可使用十天, 但问题来了,每到十天要修改程序一次,实在是让人烦脑,能不能让程序自动修改机器号呢? =========================================================================== 第二部分:确定方案 对这个问题,我采取的是这个方案:在该程序目录建一配制文件(如1212.ini),有如下内容: ///////////////////////////////////////////////////////////////////// [kanxue] count=0x00000003 //记数器,以十六进制表示(字符0x表示十六进制) ///////////////////////////////////////////////////////////////////// 程序上服务器验证时,从1212.ini文件读取count值作为机器号(0167:004ABED9 PUSH EAX)入栈,十天试用 期用完后(也就是`CHANNEL!CheckTryUserWhenLogOn`反回值为FFFFFFFF),将1212.ini文件中的count值加1, 如此流程,该软件就可长时间使用了。 实现这个功能,需要如下win32函数: 1、GetPrivateProfileIntA函数(用来读取1212.ini文件的count值) UINT GetPrivateProfileInt( LPCTSTR lpAppName, // address of section name ("kanxue") LPCTSTR lpKeyName, // address of key name ("count") INT nDefault, // return value if key name is not found(默认值) LPCTSTR lpFileName // address of initialization filename(初始化文件的名字1212.ini) ); 2、WritePrivateProfileString函数(用来将数据写入1212.ini文件里) BOOL WritePrivateProfileString( LPCTSTR lpAppName, // pointer to section name ("kanxue") LPCTSTR lpKeyName, // pointer to key name ("count") LPCTSTR lpString, // pointer to string to add (要写的字串) LPCTSTR lpFileName // pointer to initialization filename (文件1212.ini) ); 3、wsprintf函数(将字串用十六进制形式放入1212.ini,你也可不用该函数,结果就以十进制表示了) int wsprintf( LPTSTR lpOut, // 指向一个接收格式化文本的buffer LPCTSTR lpFmt, // 指向一个带有格式控制的零终止字符串 ... // 参数选项 ); 其中:lpFmt指向的格式字符串中每一个格式设定必须是以下形式: %[-][#][0][width][.precision]type 由于我们希望1212.ini中的count是以十六进制(0x0000000)表示,所以在这定义: Fmt db "%#08lx",0 它含义输出的数值是带有0x(#)8位(8)小写的hex(lx),如果输出结果不足8位,补零(0) 为了更好地理解,我们先以一Win32ASM程序来实现这个功能: ///////////////////////////////////////////////////////////////////// .386 .model flat,stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib .const Default equ 1 .data iniFile db ".\1212.ini",0 AppName db "kanxue",0 KeyName db "count",0 Fmt db "%#08lx",0 Opt db 10 dup(0) .code ; --------------------------------------------------------------------------- start: invoke GetPrivateProfileInt, addr AppName, addr KeyName, Default, addr iniFile mov eax, 5 invoke wsprintf, addr Opt, addr Fmt, eax invoke WritePrivateProfileString, addr AppName, addr KeyName, addr Opt, addr iniFile invoke ExitProcess, NULL end start ///////////////////////////////////////////////////////////////////// 将上面程序编译后的EXE文件,反汇编后,关键代码如下: ///////////////////////////////////////////////////////////////////// * Possible StringData Ref from Data Obj ->".\1212.ini" | //******************** Program Entry Point ******** :00401000 6800304000 push 00403000 :00401005 6A01 push 00000001 * Possible StringData Ref from Data Obj ->"count" | :00401007 6812304000 push 00403012 * Possible StringData Ref from Data Obj ->"kanxue" | :0040100C 680B304000 push 0040300B * Reference To: KERNEL32.GetPrivateProfileIntA, Ord:011Fh //从1212.ini文件读取count的值 | :00401011 E844000000 Call 0040105A :00401016 B805000000 mov eax, 00000005 :0040101B 50 push eax //将EAX的值以十六进制格式写入1212.ini文件中(count) * Possible StringData Ref from Data Obj ->"%#08lx" | :0040101C 6818304000 push 00403018 :00401021 681F304000 push 0040301F * Reference To: USER32.wsprintfA, Ord:02A5h //字串格式转换为0x???????的形式 | :00401026 E823000000 Call 0040104E :0040102B 83C40C add esp, 0000000C * Possible StringData Ref from Data Obj ->".\1212.ini" | :0040102E 6800304000 push 00403000 :00401033 681F304000 push 0040301F * Possible StringData Ref from Data Obj ->"count" | :00401038 6812304000 push 00403012 * Possible StringData Ref from Data Obj ->"kanxue" | :0040103D 680B304000 push 0040300B * Reference To: KERNEL32.WritePrivateProfileStringA, Ord:02BFh//写入1212.ini文件 | :00401042 E819000000 Call 00401060 :00401047 6A00 push 00000000 * Reference To: KERNEL32.ExitProcess, Ord:0075h | :00401049 E806000000 Call 00401054 ///////////////////////////////////////////////////////////////////// =========================================================================== 第三部分:补丁程序 方案基本确定,现在就需要在原程序将上述三个函数功能加进去,打开程序Import表查看,所幸的是 GetPrivateProfileInt、wsprintf、WritePrivateProfileString三个函数原程序皆有,因此我们就省了构造 Import表这一块了。只需要bpx GetPrivateProfileInt等三个API设断拦截后,查看相应的机器码就可,如: FF153CC35700 CALL `KERNEL32!GetPrivateProfileIntA` 因此调用GetPrivateProfileInt时的机器码就是FF153CC35700 。 用十六进制工具打开主程序,我们先找一空白地方放入三个函数的参数: --------------------------------------------------------------------------------------- 17B65E 90909090909090902E5C313231322E696E69006B616E .........\1212.ini.kan 17B674 78756500636F756E7400252330386C78000000000000 xue.count.%#08lx...... --------------------------------------------------------------------------------------- \1212.INI:对应的文件地址:0017B650,内存地址为0057B650 kanxue:对应的文件地址:0017B65B,内存地址为0057B65B count :对应的文件地址:0017B662,内存地址为0057B662 %#08lx:对应的文件地址:0017B668,内存地址为0057B668 最后一步只需在程序找段空白处,将如下代码插入即可: ///////////////////////////////////////////////////////////////////// 修改后代码: ................. ................. jmp Lable01 Lable00: MOV EAX,[EBX 64] PUSH EAX //机器号入栈(一机一号,并在其服务器登记,只能用10天) CALL `CHANNEL!CheckTryUserWhenLogOn`//在服务上验证用户合法性 CMP EAX,BYTE -01 MOV [ESP 30],EAX JZ Lable03//当EAX=FFFFFFFF时跳到Lable03,报“网络连接超时” CMP EAX,ESI JNZ 004ABF51 JMP Lable02//如EAX返回值为0,则跳到 Lable02处,将count加1. ................. ................. Lable03: Callxxxxxx 网络连接超时 补丁代码一:(读取1212.ini文件count值) Lable01: 6850B65700 PUSH DWORD 0057B650 ->".\1212.ini" 6A01 PUSH BYTE 01 6862B65700 PUSH DWORD 0057B662 ->"count" 685BB65700 PUSH DWORD 0057B65B ->"kanxue" FF153CC35700 CALL `KERNEL32!GetPrivateProfileIntA`//读取count的值 E93008F3FF JMP Lable00 补丁代码二:(将1212.ini文件count值加1) Lable02: 6850B65700 PUSH DWORD 0057B650->".\1212.ini" 6A01 PUSH BYTE 01 6862B65700 PUSH DWORD 0057B662->"count" 685BB65700 PUSH DWORD 0057B65B ->"kanxue" FF153CC35700 CALL `KERNEL32!GetPrivateProfileIntA`//读取count值到EAX 40 INC EAX//count值加1 50 PUSH EAX 6868B65700 PUSH DWORD 0057B668->"%#08lx" 686FB65700 PUSH DWORD 0057B66F->"0000000000" FF158CC65700 CALL `USER32!wsprintfA`//将count的值转换成0x00000004之类形式 83C40C ADD ESP,BYTE 0C //调整堆栈平衡(wsprintfA需手动调整堆栈) (其它函数自动平衡堆栈) 6850B65700 PUSH DWORD 0057B650->".\1212.ini" 686FB65700 PUSH DWORD 0057B66F 6862B65700 PUSH DWORD 0057B662->"count" 685BB65700 PUSH DWORD 0057B65B"kanxue" FF151CC35700 CALL `KERNEL32!WritePrivateProfileStringA`//将计算过的count //写入1212.ini文件 E9F10AF3FF JMP Lable03 将程序修改后,在Win98下顺利运行,但在XP下报错(内存写超出范围),用工具PEditor打开程序,将文件检验和纠正(Checksum),此时程序就可在XP下顺利运行了. =========================================================================== 第四部分:结束 本文目的只是给大家一个思路,当遇到困难时,换个思路问题就迎刃而解了。由于时间创促,文章写的比较凌 乱,请将就着看吧。;) |
|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )
GMT+8, 2024-9-29 09:31 , Processed in 0.203261 second(s), 12 queries , Gzip On, MemCache On.
Powered by Discuz! X3.5
© 2001-2023 Discuz! Team.