本帖最后由 公孙秒秒 于 2019-4-26 20:34 编辑
前言:
————————————————————————————————————
由于原来的帖子没有附件,这个我是百度搜索下载的一个为破解版本,解压即可用,不过与我是用户大大分析的原版本程序有所不同,少了一些文件,代码上虽然相同,不过思路上完全没办法了,记录一下我的思路吧
大大原帖传送门:https://www.52pojie.cn/thread-197598-1-1.html
视频传送门:
————————————————————————————————————
https://www.bilibili.com/video/av50351540/
https://www.bilibili.com/video/av50538005/
汇总帖传送门:
————————————————————————————————————
https://www.52pojie.cn/thread-932679-1-1.html
正文:
————————————————————————————————————
解压过后运行主程序:Courseware2.exe
发现弹出来一个注册界面:
打开任务管理器是看不到这个注册界面的进程的:
不过我们可以打开OD看一下,打开附件界面,然后按路径排序,看到了我们主程序旁边,还有一个EXE程序运行了,这个名字为注册的EXE程序其实就是注册的程序了:
先进行查壳,两个都查一下,发现无壳,所以就不上图了
那么我们就可以开始盘他了,我们先研究这个注册程序,用OD载入他
我们先把程序跑起来,然后输入好用户名和注册码,随便输入,确定看看,有一个弹窗
这里其实我们就可以有思路了,最简单的肯定是下按钮事件断点
其次下DialogBoxParamA,messagebox之类的断点断到这个弹窗出现的位置,向上回溯
或者下getwindowtext这些的获取文本的,因为要获取用户名和注册码很大可能会调用这些api,然后向下跟
这里本来打算下按钮事件断点的,不过试过了各种按钮事件,都断不下来,所以只好更改思路,同时下messagebox和dialogboxparam断点,然后再点确定尝试注册,成功的断在了DialogBoxParamA这里:
[Asm] 纯文本查看 复制代码 0012F7A8 00402553 /CALL 到 DialogBoxParamA 来自 AuthReg.0040254D
0012F7AC 00930000 |hInst = 00930000
0012F7B0 00000069 |pTemplate = 0x69
0012F7B4 003C0898 |hOwner = 003C0898 ('注册',class='#32770',parent=000100DA)
0012F7B8 004023E0 |DlgProc = AuthReg.004023E0
0012F7BC 0012F7C0 \lParam = 0012F7C0
直接进行回溯,返回到的地方上一句call就是调用dialogboxparam的地方了,那么我们往上找,找到这段的头部(00402480 处),下断,然后重新提交注册,F8跟一遍看看能不能有什么思路:
[Asm] 纯文本查看 复制代码 00402480 /$ 83EC 40 sub esp,0x40
00402483 |. 8B0D 38CC4000 mov ecx,dword ptr ds:[0x40CC38]
00402489 |. 56 push esi
0040248A |. 8B35 98914000 mov esi,dword ptr ds:[<&USER32.GetWindow>; user32.GetWindowTextA
00402490 |. 8D4424 04 lea eax,dword ptr ss:[esp+0x4]
00402494 |. 6A 08 push 0x8 ; /Count = 0x8
00402496 |. 50 push eax ; |Buffer = 0012F7C0
00402497 |. 51 push ecx ; |hWnd = 003C0898 ('注册',class='#32770',parent=000100DA)
00402498 |. FFD6 call esi ; \GetWindowTextA
0040249A |. A1 34CC4000 mov eax,dword ptr ds:[0x40CC34]
0040249F |. 8D5424 09 lea edx,dword ptr ss:[esp+0x9]
004024A3 |. 6A 08 push 0x8 ; /Count = 0x8
004024A5 |. 52 push edx ; |Buffer = offset Auth936.<ModuleEntryPoint>
004024A6 |. 50 push eax ; |hWnd = 0012F7C0
004024A7 |. FFD6 call esi ; \GetWindowTextA
004024A9 |. 8B15 40CC4000 mov edx,dword ptr ds:[0x40CC40]
004024AF |. 8D4C24 0E lea ecx,dword ptr ss:[esp+0xE]
004024B3 |. 6A 08 push 0x8 ; /Count = 0x8
004024B5 |. 51 push ecx ; |Buffer = 003C0898
004024B6 |. 52 push edx ; |hWnd = 00930000
004024B7 |. FFD6 call esi ; \GetWindowTextA
004024B9 |. 8B0D 3CCC4000 mov ecx,dword ptr ds:[0x40CC3C]
004024BF |. 8D4424 13 lea eax,dword ptr ss:[esp+0x13]
004024C3 |. 6A 08 push 0x8 ; /Count = 0x8
004024C5 |. 50 push eax ; |Buffer = 0012F7C0
004024C6 |. 51 push ecx ; |hWnd = 003C0898 ('注册',class='#32770',parent=000100DA)
004024C7 |. FFD6 call esi ; \GetWindowTextA
004024C9 |. 8B15 30CC4000 mov edx,dword ptr ds:[0x40CC30]
004024CF |. 68 00010000 push 0x100 ; /Count = 100 (256.)
004024D4 |. B0 2D mov al,0x2D ; |
004024D6 |. 68 80CD4000 push AuthReg.0040CD80 ; |Buffer = AuthReg.0040CD80
004024DB |. 52 push edx ; |hWnd = 00930000
004024DC |. 884424 1E mov byte ptr ss:[esp+0x1E],al ; |
004024E0 |. 884424 19 mov byte ptr ss:[esp+0x19],al ; |
004024E4 |. 884424 14 mov byte ptr ss:[esp+0x14],al ; |
004024E8 |. C64424 23 00 mov byte ptr ss:[esp+0x23],0x0 ; |
004024ED |. FFD6 call esi ; \GetWindowTextA
004024EF |. A1 58C74000 mov eax,dword ptr ds:[0x40C758]
004024F4 |. 5E pop esi ; AuthReg.00402553
004024F5 |. 85C0 test eax,eax
004024F7 |. 74 0E je short AuthReg.00402507
004024F9 |. 8D4C24 00 lea ecx,dword ptr ss:[esp]
004024FD |. 51 push ecx
004024FE |. 68 80CD4000 push AuthReg.0040CD80 ; ASCII "zhucema"
00402503 |. FFD0 call eax
00402505 |. EB 0F jmp short AuthReg.00402516
00402507 |> 8D5424 00 lea edx,dword ptr ss:[esp]
0040250B |. 52 push edx ; Auth936.<ModuleEntryPoint>
0040250C |. 68 80CD4000 push AuthReg.0040CD80 ; ASCII "zhucema"
00402511 |. E8 DA140000 call AuthReg.004039F0
00402516 |> 33C9 xor ecx,ecx
00402518 |. 8D5424 00 lea edx,dword ptr ss:[esp]
0040251C |. 85C0 test eax,eax
0040251E |. 0f95c1 setne cl
00402521 |. 52 push edx ; Auth936.<ModuleEntryPoint>
00402522 |. 68 80CD4000 push AuthReg.0040CD80 ; ASCII "zhucema"
00402527 |. 890D 40F84000 mov dword ptr ds:[0x40F840],ecx
0040252D |. E8 2E000000 call AuthReg.00402560
00402532 |. 8B4C24 4C mov ecx,dword ptr ss:[esp+0x4C]
00402536 |. 8B15 64CD4000 mov edx,dword ptr ds:[0x40CD64] ; Auth936.<ModuleEntryPoint>
0040253C |. 83C4 08 add esp,0x8
0040253F |. 8D4424 00 lea eax,dword ptr ss:[esp]
00402543 |. 50 push eax ; /lParam = 0012F7C0
00402544 |. 68 E0234000 push AuthReg.004023E0 ; |DlgProc = AuthReg.004023E0
00402549 |. 51 push ecx ; |hOwner = 003C0898 ('注册',class='#32770',parent=000100DA)
0040254A |. 6A 69 push 0x69 ; |pTemplate = 0x69
0040254C |. 52 push edx ; |hInst = 00930000
0040254D |. FF15 C4914000 call dword ptr ds:[<&USER32.DialogBoxPar>; \DialogBoxParamA
重新提交过后,程序断在了00402480 处,我们一路F8,这个时候注册观察堆栈会发现:
[Asm] 纯文本查看 复制代码 004024FD |. 51 push ecx
004024FE |. 68 80CD4000 push AuthReg.0040CD80 ; ASCII "zhucema"
00402503 |. FFD0 call eax ; SthClass.100010A0
这个位置,前两句分别把我们输入的用户名和假码推进栈了,然后立刻调用了一个call,那么这个call其实就非常的可疑了,我们跟进去看看,进入call过后,先F8走走看,走到下面这段代码的时候发现:
[Asm] 纯文本查看 复制代码 100010C5 0FBE4434 08 movsx eax,byte ptr ss:[esp+esi+0x8]
100010CA 83F8 41 cmp eax,0x41
100010CD 7C 08 jl short SthClass.100010D7
100010CF 83F8 5A cmp eax,0x5A
100010D2 7F 03 jg short SthClass.100010D7
100010D4 83C0 20 add eax,0x20
100010D7 50 push eax ; SthClass.100010A0
100010D8 E8 33020000 call SthClass.10001310
100010DD 83C4 04 add esp,0x4
100010E0 884434 08 mov byte ptr ss:[esp+esi+0x8],al
100010E4 46 inc esi
100010E5 83FE 04 cmp esi,0x4
100010E8 ^ 7C DB jl short SthClass.100010C5
发现下面这一句的位置一直在产生一些字符,而且刚好这个循环又执行了四次,那么就很可疑了,会不会是在计算真码的某四位呢?,我们在数据窗口中跟随[esp+esi+0x8]:
[Asm] 纯文本查看 复制代码 100010E0 884434 08 mov byte ptr ss:[esp+esi+0x8],al
然后我们继续往下走,当走到这一段的时候:
[Asm] 纯文本查看 复制代码 100010F8 8D4C14 08 lea ecx,dword ptr ss:[esp+edx+0x8]
100010FC 0FBE040E movsx eax,byte ptr ds:[esi+ecx]
10001100 83F8 41 cmp eax,0x41
10001103 7C 08 jl short SthClass.1000110D
10001105 83F8 5A cmp eax,0x5A
10001108 7F 03 jg short SthClass.1000110D
1000110A 83C0 20 add eax,0x20
1000110D 0FBE09 movsx ecx,byte ptr ds:[ecx]
10001110 3BC1 cmp eax,ecx
10001112 0F85 32010000 jnz SthClass.1000124A
10001118 42 inc edx
10001119 83FA 04 cmp edx,0x4
1000111C ^ 7C DA jl short SthClass.100010F8
发现又是一段循环,刚好四次,而且这里
[Asm] 纯文本查看 复制代码 100010FC 0FBE040E movsx eax,byte ptr ds:[esi+ecx]
这一句取了我们假码的第一位
[Asm] 纯文本查看 复制代码 1000110D 0FBE09 movsx ecx,byte ptr ds:[ecx]
又在上面这一句处取了刚才产生的四位字符的第一位,然后对他们进行比较,这个时候其实百分之99就可以确定这里是在比对注册码了,而上面产生的字符就是再计算真实的注册码
[Asm] 纯文本查看 复制代码 10001110 3BC1 cmp eax,ecx
10001112 0F85 32010000 jnz SthClass.1000124A
那么上面这个跳转的地方显然是要跳到不正确的处理结果处的的,我们顺着跳转的箭头找到这个位置的代码先保存下来,但是程序中我们不让这个跳转实现,因为这里循环比对了4次,改标志位太累了,直接nop吧
这是如果错误要跳转到的地方的代码:
[Asm] 纯文本查看 复制代码 1000124A 5F pop edi
1000124B 33C0 xor eax,eax
1000124D 5E pop esi
1000124E 83C4 20 add esp,0x20
10001251 C2 0800 retn 0x8
我们把跳转nop掉过后,继续跟,发现面又分别出现了三次同样的计算真码的代码,和三次比对的过程,然后就走到了刚刚那个跳转应该到达的上方:
[Asm] 纯文本查看 复制代码 1000123D 5F pop edi
1000123E B8 01000000 mov eax,0x1
10001243 5E pop esi
10001244 83C4 20 add esp,0x20
10001247 C2 0800 retn 0x8
发现如果没有经过跳转的话,那么这里会把eax的值赋值为1,如果错误的话,就会通过xor eax,eax将eax置零,也就是说其实这段函数最后判断代验证码正不正确靠的的是eax的值,这里我们爆破的思路就多了:
1、我们nop掉所有对比处的跳转
2、我们直接在这段函数的头部修改代码,对eax赋值1,然后直接返回就好了:
[Asm] 纯文本查看 复制代码 mov eax,0x1
retn 0x8
3、我们直接拿到真码注册
修改过后,我们保存好修改的文件替换掉原来的文件,发现这个时候果然无论我们输入什么都会显示注册成功了:
不过这个时候如果我们直接打开主程序,会发现又弹出来了注册的页面,但是如果我们是用真码去注册的话,重启就不会弹出来,那么说明主程序其实在启动的时候又重新进行了验证,重启验证,我们如果在刚刚那个比对函数返回过后,跟到下一个call中就会发现注册程序进行了大量的注册表写入操作,怀疑是将注册信息写入了注册表,并在每次启动程序的时候重新验证一遍,我们把主程序用OD载入,然后下regopenkey和regopenkeyex看看,能不能找到有用的信息,下断过后,运行:
[Asm] 纯文本查看 复制代码 0012F9D4 80000000 |hKey = HKEY_CLASSES_ROOT
0012F9D8 0012FA30 |Subkey = "CLSID\{C0483E67-A363-40C8-BC4C-22EC7419CD21}\Implemented Categories"
0012F9DC 00000000 |Reserved = 0x0
0012F9E0 00020019 |Access = KEY_READ
0012F9E4 0012F9EC \pHandle = 0012F9EC
堆栈当中看到的地址显然不是我们程序的调用,忽略,继续运行:
[Asm] 纯文本查看 复制代码 0012F9D0 00413886 /CALL 到 RegOpenKeyExA 来自 Coursewa.00413880
0012F9D4 80000000 |hKey = HKEY_CLASSES_ROOT
0012F9D8 0012FA30 |Subkey = "CLSID\{C0483E67-A363-40C8-BC4C-22EC7419CD21}\Implemented Categories"
0012F9DC 00000000 |Reserved = 0x0
0012F9E0 00020019 |Access = KEY_READ
0012F9E4 0012F9EC \pHandle = 0012F9EC
堆栈中的信息显示如上,发现是我们程序打开了一个键,我们直接返回用户代码,F8一路跟下去看看,程序访问了键过后,做了什么操作:
[Asm] 纯文本查看 复制代码 00413909 |. 8D4C24 10 lea ecx,dword ptr ss:[esp+0x10] ; 假码给到ecx
0041390D |. 8D5424 50 lea edx,dword ptr ss:[esp+0x50] ; 用户名给到EDX
00413911 |. 51 push ecx ; 假码进栈
00413912 |. 52 push edx ; 用户名进栈
00413913 |. E8 D8010000 call Coursewa.00413AF0 ; 所以这个call我们就要跟进去看看了,F7
走到上面这段的时候,发现出现了我们输入的假码和注册名,然后他们都被推进栈,我们跟进下面的call当中,发现里面的代码非常的熟悉,就是我们上面找到的产生真码和比对的地方一模一样,不过这次是写在主程序里面的,我们用相同的手法修改并且保存,执行我们修改过后的程序,发现没有了弹窗,同时查看注册信息发现,显示注册成功:
|