找回密码
 注册
搜索
热搜: 回贴

补丁技术应用(10千字),补丁,逆向工程技术

2010-1-30 18:39| 发布者: admin| 查看: 537| 评论: 0|原作者: 潇潇雨


补丁技术应用(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下顺利运行了.

===========================================================================



第四部分:结束



本文目的只是给大家一个思路,当遇到困难时,换个思路问题就迎刃而解了。由于时间创促,文章写的比较凌



乱,请将就着看吧。;)


最新评论

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏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.

返回顶部