好友
阅读权限25
听众
最后登录1970-1-1
|
写在前面:一直很喜欢吾爱,,最近放假了来吾爱把爱盘里的教程看了一个遍,发现有好多大神都是之前先自己做一遍,结果后面讲解的时候容易先入为主的把自己已经知道的东西拿出来用,所以可能代码的分析上有点不足,导致小白很难以接受,所以我想写一篇没有做过任何准备,一边把自己思路写下来一边破解的文章.老实说这样挺容易坑自己的,毕竟我技术也不是特别的好,要是找了一个程序废了几个小时,分析到后面发现自己解决不了的话,那这篇文章就白打了,不过还好运气不错,这是一个算法并不是很复杂的程序.这篇文章写完后,我又假设自己是一个小白,然后自己跟着自己的步骤试了四边,直到让自己觉得没有什么阅读障碍才算完成,希望这篇文章能帮到更多想学习逆向的人.文章方向:小白教学向
软件名称:Acid burn
采用方式:爆破+算法注册机+堆栈注册机
逆向步骤:
用OD载入Acid burn,并F9运行.
转到Serial/Name界面,第一行输入111222,第二行输入333444
点击Check it Baby
弹出对话框提示:Sorry , The serial is incorect !
返回到OD,暂停程序,查看此时程序的堆栈(Alt+K)
找到最下面一行指令,点击右键-在堆栈中跟随地址,然后观察右下角堆栈区域,我们要关心的只有如下数据
[Asm] 纯文本查看 复制代码
0019F724 ]0019F75C
0019F728 |0042FB37 返回到 Acid_bur.0042FB37 来自 Acid_bur.0042A170 ;调用对话框后的返回地址
0019F72C |00000000
0019F730 |0019F84C 指针到下一个 SEH 记录
0019F734 |0042FB67 SE 句柄
0019F738 |0019F75C
0019F73C |021D1E8C
0019F740 |021D1E8C
0019F744 |021E251C ASCII "4018" ;对111222进行加密算法后得到的数据
0019F748 |021E2508 ASCII "111222" ;原始数据
0019F74C |021E254C ASCII "333444" ;原始数据
0019F750 |021E2530 ASCII "CW-4018-CRACKED" ;111222对应的密码数据
嗯,我对上面的信息进行了注释,也即是分号后的内容,大家可能会想,你怎么知道4018是对111222加密后的数据?
这个其实是靠猜的,并不一定正确,我是通过观察这四行ASCII文本,发现第二、三行数据是我们输入的原始数据,而第四行明显像是一个注册码一样的东西(Serial Key),且第四行是包含第一行的,而且还是"非完全无规律字符串"(因为它包含了cracked这个单词),所以我就先这么猜测:第四行数据是在第一行数据的基础上进行了某种拼接得到的,而第一行数据是否真的和用户输入的数据有关呢,我们还需要换一组数据进行测试.
于是我按下F9恢复程序运行,在注册界面重新输入了一组数据:222333 333444.
然后再次尝试刚才的步骤,观察栈,此时数据变成了
[Asm] 纯文本查看 复制代码
0019F728 |0042FB37 返回到 Acid_bur.0042FB37 来自 Acid_bur.0042A170;调用对话框后的返回地址
0019F72C |00000000
0019F730 |0019F84C 指针到下一个 SEH 记录
0019F734 |0042FB67 SE 句柄
0019F738 |0019F75C
0019F73C |021D1E8C
0019F740 |021D1E8C
0019F744 |021E251C ASCII "4100" ;这里变化了
0019F748 |021E2508 ASCII "222333" ;原始数据
0019F74C |021E254C ASCII "333444" ;原始数据
0019F750 |021E2530 ASCII "CW-4100-CRACKED" ;同第一行一样,发生变化了
好,到这里我们就可以得出一个相对靠谱的结论了:四行ASCII中,第一行数据会根据第二行数据发生变化
第二行是用户名(加密算法中的主Key)
第三行是注册码,不会影响加密算法,只用来判断注册码是否与算出来的码相同.
第四行是最终的注册码,且格式为:CW-第一行数据-CRACKED
好了以上,我们就可以通过OD观察堆栈区域得到真正序列号了,我们F9运行OD,尝试将刚才在注册界面输入的333444更改为CW-4100-CRACKED,点击Check按钮,提示Good job ,注册成功,证明我们刚才的所有猜测没有关键性的错误,接下来我们就可以写注册机了,这里可以分为两种,第一种是注入到主程序中,这种可能会被报毒,而且代码处理不好也会导致程序的栈溢出.
先说下思路:
我们在注册界面输入 111222 333444,按下Check按钮,会提示Sorry的错误,我们可以在CALL Sorry对话框的下面修改为JMP到自己的巢穴代码,弹个框取出当前ESP+20或者EBP-C都可以(这两个数是根据调试时观察右下角堆栈区域得到的,下面会讲),然后执行原来的代码最后再JMP回去,使用这种方法需要注意的就是你的巢穴代码必须有执行权限,这个需要有点PE基础的人做.
不过明显还有更简单的方法,直接修改Sorry字符串为注册码可以省去我们自己写MessageBox的步骤,但并不是直接修改就可以的,有一些细节需要注意,我们先在CALL函数下个断,重新点击Check,中断后F7步入到函数内部.
我在这里对思路2进行演示(为了避免一些初学的朋友找不到这个Call MessageBox的位置,我说一下怎么找这个CALL,并附上截图):
确保你的调试程序处于运行状态(也就是没有被OD中断暂停),在确定一下你输入的是不是111222 333444,按下Check按钮,然后回到OD中点击暂停,按下Alt+K(也就是"调用堆栈"窗口),双击最下面一行,此时你就会跳转到函数的头部,观察一下是不是和我下面的代码是类似的?好的我们可以开始了.
[Asm] 纯文本查看 复制代码
0042A170 /$ 55 PUSH EBP ;进入到函数里,压栈帧
0042A171 |. 8BEC MOV EBP, ESP ;也就是函数栈顶指针,之后取注册码的时候会用到,下面这段代码就没啥用了,直接看最下面的对话框函数的调用
0042A173 |. 83C4 F4 ADD ESP, -0C
0042A176 |. 53 PUSH EBX
0042A177 |. 56 PUSH ESI
0042A178 |. 57 PUSH EDI
0042A179 |. 8BF9 MOV EDI, ECX
0042A17B |. 8BF2 MOV ESI, EDX
0042A17D |. 8BD8 MOV EBX, EAX
0042A17F |. E8 7CB4FDFF CALL <JMP.&user32.GetActiveWindow> ; [GetActiveWindow
0042A184 |. 8945 F8 MOV [LOCAL.2], EAX
0042A187 |. 33C0 XOR EAX, EAX
0042A189 |. E8 12A0FFFF CALL Acid_bur.004241A0
0042A18E |. 8945 F4 MOV [LOCAL.3], EAX
0042A191 |. 33C0 XOR EAX, EAX
0042A193 |. 55 PUSH EBP
0042A194 |. 68 D0A14200 PUSH Acid_bur.0042A1D0
0042A199 |. 64:FF30 PUSH DWORD PTR FS:[EAX]
0042A19C |. 64:8920 MOV DWORD PTR FS:[EAX], ESP
0042A19F |. 8B45 08 MOV EAX, [ARG.1]
0042A1A2 |. 50 PUSH EAX ; /Style
0042A1A3 |. 57 PUSH EDI ; |Title
0042A1A4 |. 56 PUSH ESI ; |Text
0042A1A5 |. 8B43 24 MOV EAX, DWORD PTR DS:[EBX+24] ; |
0042A1A8 |. 50 PUSH EAX ; |hOwner
0042A1A9 |. E8 FAB5FDFF CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA ,没错就是这里了,接下来的下面的代码跟这部分没啥关系,我就不粘贴过来了
注意看这个CALL MessageBoxA,里面有四个PUSH,也就是对应着4个参数,大家写过Win32编程的应该很熟悉了,没见过的我在这里简单解释一下,详细的去查MSDN.
它的调用代码类似于下面这样:
[C] 纯文本查看 复制代码 MessageBox(父窗口句柄,对话框内容,对话框标题,对话框样式)
在PUSH EAX这行,也就是我们看到的四个PUSH中的第一个,按下F2下断点,然后运行程序,再回到程序中点一下Check按钮,紧接着应该会中断到刚才你下断点的这一行.
我们再来看一下此时各寄存器的值:
EAX 00000000
ECX 0019F704 ASCII "小B"
EDX 0019F730
EBX 021D7FE8
ESP 0019F700
EBP 0019F724
ESI 0042FB80 ASCII 53,"orry , The serial is incorect !"
EDI 0042FB74 ASCII 54,"ry Again!"
EIP 0042A1A2 Acid_bur.0042A1A2
对应着上面传过来的参数,就变成了:
[C] 纯文本查看 复制代码 MessageBox([EBX+24],"Sorry, The serial is incorect !" ,"Try Again!",0)
这里的EBX+24是多少我们没必要知道,因为完全可以用0代替,也就是父窗口是桌面
我们需要做的,就是把Text这段"Sorry, The serial is incorect !" 改为我们要的注册码,隔得上面有点远了 我再粘贴一下关键代码
[Asm] 纯文本查看 复制代码
0042A1A2 |. 50 PUSH EAX ; /Style
0042A1A3 |. 57 PUSH EDI ; |Title
0042A1A4 |. 56 PUSH ESI ; |Text
0042A1A5 |. 8B43 24 MOV EAX, DWORD PTR DS:[EBX+24] ; |
0042A1A8 |. 50 PUSH EAX ; |hOwner
0042A1A9 |. E8 FAB5FDFF CALL <JMP.&user32.MessageBoxA> ;
修改这里有一个需要注意的地方,就是你如果不做巢穴代码跳转,打算直接在这里修改的话,修改的字节数大小一定不能超过它原来的大小,否则会影响到下面的代码,我们来算一下:
PUSH EAX -> 对应的16进制机器码 50(仔细看上面汇编代码的左边,那个就是),也就是1字节
同理 PUSH EAX + PUSH EDI + PUSH ESI + PUSH EAX(在CALL上面),这是4个字节
MOV EAX, DWORD PTR DS:[EBX+24] 这是3个字节
我们要修改的部分只有PUSH部分,所以现在我们就需要在7字节的范围内对TEXT的数据进行替换,可以看到 PUSH 这种代码都是占用1个字节的,想优化成更小的话就别想了,还是考虑在MOV这行上刀吧,刚才说过了,HWND句柄即使是0也是可以弹出的,我们在这里其实没必要去找主程序的句柄是多少,所以,删(NOP)掉这行代码
这样的话,EAX的值不会改变,等同于Style的值,还是0(忘记了的话,去找找上面寄存器的值自己看看)
好了,我们节省了3个字节的代码,算上要修改的PUSH ESI 一共有四个字节供我们使用了,完全可以满足我们的要求,接下来把原先的PUSH ESI修改为PUSH [EBP+某个十六进制数]
那么这个十六进制数到底是多少呢?观察右下角堆栈区域,先找到我们的EBP地址(同样在寄存器值里可以找到),然后在堆栈窗口双击EBP的值,可以看到原先的地址变成了一个偏移符号,接下来我们就可以去找注册码了,找到后看一下前面的偏移:"$+2C" .OK我们要的值有了
修改PUSH ESI为 PUSH [EBP+2C],F9运行程序,再看看.
CW-4100-CRACKED
搞定,把它输到注册码区域,可以看到标题已经由原来的Try Again变为了Congratulations~
除了这种读堆栈的方法,我们还可以自己去写一个注册机出来,这样就不算是修改程序了,而是分析程序算法.
通过利用刚才修改好的程序反复输入Name取得Serial,我发现,其实只有Name的前四位决定Serial的值,所以Name的长度必须大于等于4,否则是没有注册码的.
也就是说,这个程序的加密逻辑其实很简单:判断Name是否大于3,若是,则取第前四位字符进行加密,我们现在要做的就是找到他的加密算法.
我们看看他是怎么算出来的(自我认为这个过程非常头痛,求知欲强的筒子们GOGOGO).
首先我们要返回Call MessageBoxA所在函数的上一级,我们再让程序可以在之前下过的断点中断一次(之前下过PUSH EAX的断点,如果你没删除的话运行程序再点击一次Check就能中断了),然后Ctrl+F9跳到他的上级函数.你可能会发现当你按下Ctrl+F9的时候OD的汇编窗口并没有任何跳转,那是因为你现在被调试程序还弹了一个框你没关闭呢,回到程序,按下弹出对话框的OK,此时OD再次暂停了程序的运行,我们发现他返回到了一个奇怪的位置,代码很短
明显这个函数过程中不会有算法了..我们再按一次Ctrl+F9,再往上走一层
很明显,这才是我们要的部分,观察左侧黑色的范围线,我们找到他的函数头
[Asm] 纯文本查看 复制代码
0042F9A9 |. 55 PUSH EBP ;从这里开始看,上面的省略
0042F9AA |. 68 67FB4200 PUSH Acid_bur.0042FB67
0042F9AF |. 64:FF30 PUSH DWORD PTR FS:[EAX]
0042F9B2 |. 64:8920 MOV DWORD PTR FS:[EAX], ESP
0042F9B5 |. C705 50174300 >MOV DWORD PTR DS:[431750], 29 ; 将内存地址为431750的值改为29,这里要记住,下面会用到
0042F9BF |. 8D55 F0 LEA EDX, [LOCAL.4]
0042F9C2 |. 8B83 DC010000 MOV EAX, DWORD PTR DS:[EBX+1DC]
0042F9C8 |. E8 8BB0FEFF CALL Acid_bur.0041AA58 ; 获取用户输入的Name,我这里输入的是111222
0042F9CD |. 8B45 F0 MOV EAX, [LOCAL.4]
0042F9D0 |. E8 DB40FDFF CALL Acid_bur.00403AB0 ; 取出前四位,放入EAX中
0042F9D5 |. A3 6C174300 MOV DWORD PTR DS:[43176C], EAX ; 将前四位写到内存43176C
0042F9DA |. 8D55 F0 LEA EDX, [LOCAL.4]
0042F9DD |. 8B83 DC010000 MOV EAX, DWORD PTR DS:[EBX+1DC]
0042F9E3 |. E8 70B0FEFF CALL Acid_bur.0041AA58
0042F9E8 |. 8B45 F0 MOV EAX, [LOCAL.4] ; 将用户名111222放到EAX中
0042F9EB |. 0FB600 MOVZX EAX, BYTE PTR DS:[EAX] ; 按位取EAX值,也就是把用户输入的第1位取出来,也就是我输入的字符'1',移动到EAX中
0042F9EE |. 8BF0 MOV ESI, EAX ; 字符'1'的ASCII码是49,转成16进制就是31,把它移动到ESI中
0042F9F0 |. C1E6 03 SHL ESI, 3 ; 将16进制数31左移三位,变成十六进制的188
0042F9F3 |. 2BF0 SUB ESI, EAX ; 再用188-31,得到157
0042F9F5 |. 8D55 EC LEA EDX, [LOCAL.5]
0042F9F8 |. 8B83 DC010000 MOV EAX, DWORD PTR DS:[EBX+1DC]
0042F9FE |. E8 55B0FEFF CALL Acid_bur.0041AA58 ; 在这个CALL里,把用户名复制了一份,放到了一个新变量(栈)中
0042FA03 |. 8B45 EC MOV EAX, [LOCAL.5] ; 把新变量的值(还是111222),放到EAX中
0042FA06 |. 0FB640 01 MOVZX EAX, BYTE PTR DS:[EAX+1] ; 取用户名的第2位,还是字符'1'
0042FA0A |. C1E0 04 SHL EAX, 4 ; 这次是左移四位,变成了310
0042FA0D |. 03F0 ADD ESI, EAX ; 将两个加密过的用户名相加,也就是310+157=467,放到ESI中
0042FA0F |. 8935 54174300 MOV DWORD PTR DS:[431754], ESI ; 把467存到内存里
0042FA15 |. 8D55 F0 LEA EDX, [LOCAL.4]
0042FA18 |. 8B83 DC010000 MOV EAX, DWORD PTR DS:[EBX+1DC]
0042FA1E |. E8 35B0FEFF CALL Acid_bur.0041AA58 ; 再把用户名复制一份,替换到栈中
0042FA23 |. 8B45 F0 MOV EAX, [LOCAL.4] ; 111222放到EAX
0042FA26 |. 0FB640 03 MOVZX EAX, BYTE PTR DS:[EAX+3] ; 取用户名第四位,也就是字符'2',16进制的32
0042FA2A |. 6BF0 0B IMUL ESI, EAX, 0B ; 让EAX乘以0B,把结果放到ESI中,也就是十进制的 50x11 = 550,十六进制的226
0042FA2D |. 8D55 EC LEA EDX, [LOCAL.5]
0042FA30 |. 8B83 DC010000 MOV EAX, DWORD PTR DS:[EBX+1DC]
0042FA36 |. E8 1DB0FEFF CALL Acid_bur.0041AA58 ; 继续取用户名替换栈
0042FA3B |. 8B45 EC MOV EAX, [LOCAL.5] ; 移到EAX中
0042FA3E |. 0FB640 02 MOVZX EAX, BYTE PTR DS:[EAX+2] ; 这次是第三位,'1',31
0042FA42 |. 6BC0 0E IMUL EAX, EAX, 0E ; 乘16进制的0E,结果2AE
0042FA45 |. 03F0 ADD ESI, EAX ; 2AE+226=4D4
0042FA47 |. 8935 58174300 MOV DWORD PTR DS:[431758], ESI ; 结果存内存
0042FA4D |. A1 6C174300 MOV EAX, DWORD PTR DS:[43176C] ; 从内存中读前四位,一开始就存的,忘记了的看看上面
0042FA52 |. E8 D96EFDFF CALL Acid_bur.00406930 ; 求字符长度,用于判断是否大于4
0042FA57 |. 83F8 04 CMP EAX, 4 ; 跟4比较
0042FA5A |. 7D 1D JGE SHORT Acid_bur.0042FA79 ; 大于就跳
0042FA5C |. 6A 00 PUSH 0
0042FA5E |. B9 74FB4200 MOV ECX, Acid_bur.0042FB74 ; ASCII 54,"ry Again!"
0042FA63 |. BA 80FB4200 MOV EDX, Acid_bur.0042FB80 ; ASCII 53,"orry , The serial is incorect !"
0042FA68 |. A1 480A4300 MOV EAX, DWORD PTR DS:[430A48]
0042FA6D |. 8B00 MOV EAX, DWORD PTR DS:[EAX]
0042FA6F |. E8 FCA6FFFF CALL Acid_bur.0042A170
0042FA74 |. E9 BE000000 JMP Acid_bur.0042FB37
0042FA79 |> 8D55 F0 LEA EDX, [LOCAL.4]
0042FA7C |. 8B83 DC010000 MOV EAX, DWORD PTR DS:[EBX+1DC]
0042FA82 |. E8 D1AFFEFF CALL Acid_bur.0041AA58
0042FA87 |. 8B45 F0 MOV EAX, [LOCAL.4] ; 依然是取用户名放到EAX中
0042FA8A |. 0FB600 MOVZX EAX, BYTE PTR DS:[EAX] ; 这回还是第一位,31
0042FA8D |. F72D 50174300 IMUL DWORD PTR DS:[431750] ; EAX乘431750的值(最上面我有让注意过),也就是10进制的49x41=2009,十六进制的7D9
0042FA93 |. A3 50174300 MOV DWORD PTR DS:[431750], EAX ; 7D9存回内存
0042FA98 |. A1 50174300 MOV EAX, DWORD PTR DS:[431750] ; 感觉这行代码是多余的...
0042FA9D |. 0105 50174300 ADD DWORD PTR DS:[431750], EAX ; 十进制的2009x2=十六进制的FB2
0042FAA3 |. 8D45 FC LEA EAX, [LOCAL.1]
0042FAA6 |. BA ACFB4200 MOV EDX, Acid_bur.0042FBAC
0042FAAB |. E8 583CFDFF CALL Acid_bur.00403708
0042FAB0 |. 8D45 F8 LEA EAX, [LOCAL.2]
0042FAB3 |. BA B8FB4200 MOV EDX, Acid_bur.0042FBB8
0042FAB8 |. E8 4B3CFDFF CALL Acid_bur.00403708
0042FABD |. FF75 FC PUSH [LOCAL.1] ; 取固定字符串'CW'并压入栈中,为拼接字符串做准备
0042FAC0 |. 68 C8FB4200 PUSH Acid_bur.0042FBC8 ; UNICODE "-"
0042FAC5 |. 8D55 E8 LEA EDX, [LOCAL.6]
0042FAC8 |. A1 50174300 MOV EAX, DWORD PTR DS:[431750]
0042FACD |. E8 466CFDFF CALL Acid_bur.00406718 ; 16进制转为10进制,FB2,转为4018
0042FAD2 |. FF75 E8 PUSH [LOCAL.6] ; 将4018压入栈
0042FAD5 |. 68 C8FB4200 PUSH Acid_bur.0042FBC8 ; UNICODE "-"
0042FADA |. FF75 F8 PUSH [LOCAL.2] ; 固定字符串'CRACKED'压入栈
0042FADD |. 8D45 F4 LEA EAX, [LOCAL.3]
0042FAE0 |. BA 05000000 MOV EDX, 5
0042FAE5 |. E8 C23EFDFF CALL Acid_bur.004039AC ; 得到最终注册码,写入栈中,准备判断
0042FAEA |. 8D55 F0 LEA EDX, [LOCAL.4]
0042FAED |. 8B83 E0010000 MOV EAX, DWORD PTR DS:[EBX+1E0]
0042FAF3 |. E8 60AFFEFF CALL Acid_bur.0041AA58
0042FAF8 |. 8B55 F0 MOV EDX, [LOCAL.4] ; 取我们输入的注册码
0042FAFB |. 8B45 F4 MOV EAX, [LOCAL.3] ; 取算法算出的注册码
0042FAFE |. E8 F93EFDFF CALL Acid_bur.004039FC ; 在这个调用里完成了比较,控制Z标志位
0042FB03 |. 75 1A JNZ SHORT Acid_bur.0042FB1F ; 若Z不等于0就跳到注册失败的位置,需要爆破就改这里了,或者直接改上面的两个比较数也可以
0042FB05 |. 6A 00 PUSH 0 ; 以下就是压各种成功注册的数据了
0042FB07 |. B9 CCFB4200 MOV ECX, Acid_bur.0042FBCC
0042FB0C |. BA D8FB4200 MOV EDX, Acid_bur.0042FBD8
0042FB11 |. A1 480A4300 MOV EAX, DWORD PTR DS:[430A48]
0042FB16 |. 8B00 MOV EAX, DWORD PTR DS:[EAX]
0042FB18 |. E8 53A6FFFF CALL Acid_bur.0042A170 ; 调用对话框事件
0042FB1D |. EB 18 JMP SHORT Acid_bur.0042FB37
0042FB1F |> 6A 00 PUSH 0
0042FB21 |. B9 74FB4200 MOV ECX, Acid_bur.0042FB74 ; ASCII 54,"ry Again!"
0042FB26 |. BA 80FB4200 MOV EDX, Acid_bur.0042FB80 ; ASCII 53,"orry , The serial is incorect !"
0042FB2B |. A1 480A4300 MOV EAX, DWORD PTR DS:[430A48]
0042FB30 |. 8B00 MOV EAX, DWORD PTR DS:[EAX]
0042FB32 |. E8 39A6FFFF CALL Acid_bur.0042A170
0042FB37 |> 33C0 XOR EAX, EAX
0042FB39 |. 5A POP EDX
0042FB3A |. 59 POP ECX
0042FB3B |. 59 POP ECX
0042FB3C |. 64:8910 MOV DWORD PTR FS:[EAX], EDX
0042FB3F |. 68 6EFB4200 PUSH Acid_bur.0042FB6E
0042FB44 |> 8D45 E8 LEA EAX, [LOCAL.6]
0042FB47 |. E8 243BFDFF CALL Acid_bur.00403670
0042FB4C |. 8D45 EC LEA EAX, [LOCAL.5]
0042FB4F |. BA 02000000 MOV EDX, 2
0042FB54 |. E8 3B3BFDFF CALL Acid_bur.00403694
0042FB59 |. 8D45 F4 LEA EAX, [LOCAL.3]
0042FB5C |. BA 03000000 MOV EDX, 3
0042FB61 |. E8 2E3BFDFF CALL Acid_bur.00403694
0042FB66 \. C3 RETN
有了算法,写注册机就不难了,随便用个脚本轻轻松算出,这个算法不是很复杂,但却费了我几个小时,希望大神们轻拍.
Acid burn.zip
(341.14 KB, 下载次数: 38)
|
免费评分
-
查看全部评分
|