- UID
- 2198
注册时间2005-6-29
阅读权限255
最后登录1970-1-1
副坛主
该用户从未签到
|
——
今天分析的这款程序是X-NetStat Professional V5.52,明码比较的一个软件,我们通过这款程序来简单谈一下栈机制的应用和程序的自注册问题(明码比较的软件都可以实现程序的自注册)。 程序Borland Delphi 6.0 - 7.0写的,无壳,用DeDe可以分析出两个事件的地址:
unRegister2:
00563944 /. 55 PUSH EBP
unAbout2:
00564C70 /. 55 PUSH EBP
我们点注册按钮程序将自动打开官方注册页面,这一点很不爽,我们在程序中搜索该网页的ASCII找到两处并下断点,点注册时来到这里:
00564848 /. 55 PUSH EBP ; 将这里修改为retn
00564849 |. 8BEC MOV EBP,ESP
0056484B |. 6A 01 PUSH 1 ; /IsShown = 1
0056484D |. 6A 00 PUSH 0 ; |DefDir = NULL
0056484F |. 6A 00 PUSH 0 ; |Parameters = NULL
00564851 |. 68 68485600 PUSH xns5.00564868 ; |http://www.freshsoftware.com/xns/pro/register.html
00564856 |. 68 9C485600 PUSH xns5.0056489C ; |open
0056485B |. E8 EC33EAFF CALL <JMP.&user32.GetDesktopWindow> ; |[GetDesktopWindow
00564860 |. 50 PUSH EAX ; |hWnd
00564861 |. E8 52CEEDFF CALL <JMP.&shell32.ShellExecuteA> ; \ShellExecuteA
00564866 |. 5D POP EBP
00564867 \. C3 RETN
我们将该函数的首行修改为retn,使其不执行下方的指令以去掉弹出网页。我们在注册事件的地址00563944下断,随意输入假码点击注册:
00563944 /. 55 PUSH EBP ; 程序中断在这里
00563945 |. 8BEC MOV EBP,ESP
……
……
00563990 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
00563993 |. E8 D0020000 CALL xns5.00563C68 ; 很显然这里可以猜测到就是算法CALL
00563998 |. 84C0 TEST AL,AL ; 这里是对该函数返回的AL标志位进行判断
0056399A |. 0F84 DB000000 JE xns5.00563A7B ; 如果AL=0则代表注册失败
005639A0 |. E8 AFFDFFFF CALL xns5.00563754 ; 如果上方没有跳转 下边的指令将保存注册信息
005639A5 |. A1 E4F15C00 MOV EAX,DWORD PTR DS:[5CF1E4]
……
……
005639D5 |. 8B00 MOV EAX,DWORD PTR DS:[EAX]
005639D7 |. B9 E83A5600 MOV ECX,xns5.00563AE8 ; name
005639DC |. BA F83A5600 MOV EDX,xns5.00563AF8 ; Registration
……
……
00563A74 |. E8 17950400 CALL xns5.005ACF90
00563A79 |. EB 3D JMP SHORT xns5.00563AB8
00563A7B |> E8 F0FDFFFF CALL xns5.00563870 ; 上方跳转将跳到这里
我们F7跟进算法CALL,在数据窗口发现该函数有四处调用,通过现象我们可以大胆的猜测一下还有哪里调用该算法CALL,可能有:启动时的验证,或者过期后、功能验证时也将调用该函数来进行判断。
我们F7跟进算法CALL,F8单步到这里是发现真码:
00563CBB . 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
00563CBE . 8B55 F0 MOV EDX,DWORD PTR SS:[EBP-10] ; 到这里我们看到真码出现
00563CC1 . E8 6E0EEAFF CALL xns5.00404B34 ; 这时注意看堆栈中的数据
00563CC6 . 8D45 EC LEA EAX,DWORD PTR SS:[EBP-14]
00563CC9 . 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
00563CCC . E8 630EEAFF CALL xns5.00404B34 ; 我们在这里做文章
我们在EBP寄存器上点右键,跟随到堆栈窗口,向上翻一点就可以看到真码的地址保存在堆栈:
我们知道,程序在函数中是通过EBP来寻址的,在这个程序中,真码的保存位置为[EBP-10](EBP向上一行地址-4,两行-8,三行-CH,四行-10H),假码的地址为[EBP-C]。既然在栈中看到了真码和假码(真码的地址),那我们就可以利用栈来实现程序的自注册。我们现在程序代码段找到一个空白地址(一般在OD的反汇编窗口将滚动条拉到最下方就可以看到**的00 00 00 00 的空白数据,我们在这里写代码即可),我们找到一片空白数据,将 CALL 00404B34 修改为 CALL 005CABDC ,我们写一些代码来实现将假码替换为真码。
00563CCC . E8 630EEAFF CALL xns5.00404B34 ; 我们将这里修改为 CALL 005CABDC
Patch代码如下:
005CABDC 56 PUSH ESI ; 将使用到的寄存器压栈
005CABDD 57 PUSH EDI
005CABDE 51 PUSH ECX
005CABDF 53 PUSH EBX
005CABE0 B9 1E000000 MOV ECX,1E ; 真码的长度29+1=1DH
005CABE5 8B75 F0 MOV ESI,DWORD PTR SS:[EBP-10]
005CABE8 8B7D F4 MOV EDI,DWORD PTR SS:[EBP-C]
005CABEB 8BDF MOV EBX,EDI
005CABED F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] ; 实现假码的数据替换为真码
005CABEF C743 FC 1D000>MOV DWORD PTR DS:[EBX-4],1D ; 修改字符串的长度
005CABF6 5B POP EBX ; 将使用到的寄存器出栈
005CABF7 59 POP ECX
005CABF8 5F POP EDI
005CABF9 5E POP ESI
005CABFA E8 409FE3FF CALL xns5.00404B3F ; 还原我们覆盖的数据
005CABFF C3 RETN ; 返回
这里先介绍一个传送字符串的汇编指令:
MOV ECX,传送字串的长度
MOV ESI,源地址
MOV EDI,目的地址
CLD 设置DF=0,字符串正向传送
REP MOVSB 以字节形式传送 每次操作后ESI+1,EDI+1,直到ECX=0结束
我们这里还添加了这样两句代码:
MOV EBX,EDI // EDI这里保存的是假码的地址
MOV DWORD PTR DS:[EBX-4],1D // 在[EDI-4]的内存中写入数值1D(该程序的KEY格式固定长度为29=1DH)
在内存中,字符串有两个属性:字符串的地址和字符串的长度,字符串的长度数值就保存在【字符串地址-4】的地址
如下图中KEY的长度存放的地址就在该字符串首地址010F7964-4=010F7960,其数值为1DH。
在CALL中当使用到的寄存器出栈完毕后(注意:压栈和出栈修改的是ESP,当程序访问一个函数时,大多都先将ESP数值保存到EBP,该函数退出前再将EBP保存的数据送还ESP,函数的参数若保存到栈中,我们使用EBP寻址就可以方便的访问这些参数),我们再来还原我们在程序中覆盖的代码:CALL 00404B3F
我们写的函数执行完毕后加上RETN语句返回即可
通过跟踪,我们发现程序的注册信息保存在注册表中:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\X-NetStat Professional 5\Registration]
"name"="Nisy"
"email"="[email protected]"
"key"="11111-11111-11111-11111-11111"
通过上面我们写的函数,就可以实现程序将真码保存到注册表中。程序启动的时候也将调用这个算法CALL,所以即便我们注册表保存的是假码,程序也会将内存中存放的假码替换成真码来实现注册。
下面我们来看[PYG]解密小组成员 柳韵荷风 更巧妙的修改来实现程序的自注册:
在算法CALL中修改如下:
00563FDF . E8 DC0AEAFF CALL xns5.00404AC0 ; 这里修改为MOV EBX,DWORD PTR SS:[EBP-10]
00563FE4 . C3 RETN
00563FE5 .^ E9 3604EAFF JMP xns5.00404420
00563FEA .^ EB EB JMP SHORT xns5.00563FD7
00563FEC . 8BC3 MOV EAX,EBX
00563FEE . 5E POP ESI
00563FEF . 5B POP EBX
00563FF0 . 8BE5 MOV ESP,EBP
00563FF2 . 5D POP EBP
00563FF3 . C2 0400 RETN 4 ; 到这里算法CALL执行完毕返回
修改为:
00563FDF 8B5D F0 MOV EBX,DWORD PTR SS:[EBP-10]
00563FE2 90 NOP
00563FE3 90 NOP
00563FE4 . C3 RETN
00563FE5 .^ E9 3604EAFF JMP xns5.00404420
00563FEA .^ EB EB JMP SHORT xns5.00563FD7
00563FEC . 8BC3 MOV EAX,EBX ; 这里将EBX中真码的地址送到EAX中
注意:这样修改后,算法CALL的EAX将保存真码的地址,所以程序启动时调用算法CALL返回的数据AL!=0,仅修改这一处就相当于实现了爆破,我们关注的还是程序自注册,继续看下文:
程序返回算法CALL后:
00563993 |. E8 D0020000 CALL xns5.00563C68 ; 算法CALL
00563998 84C0 TEST AL,AL
0056399A 0F84 DB000000 JE xns5.00563A7B
修改为:
00563998 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX ; 算法CALL返回后 EAX保存真码的地址
0056399B 90 NOP ; [EBP-C]保存的是假码的地址 这里将假码的地址替换为真码的地址
0056399C 90 NOP
0056399D 90 NOP
0056399E 90 NOP
0056399F 90 NOP
我们知道在栈中保存的为字符串的地址,所以替换栈中保存的地址,将[EBP-C]保存的假码地址替换为真码的地址,使程序保存时保存真码,照样可以实现自注册。
以上两种方法和思路就是简单的对栈中数据的利用,第一种方法是替换栈中指针所指向的数据(字符串),第二种方法是替换栈中保存的地址。这两种思路,是我们处理栈中数据常用到的方法,希望通过本文的学习,大家不仅扩展了程序的爆破思路,同时也对栈有一个更深刻的认识。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?加入我们
x
|