标签:
首先PEID查壳
Delphi,没有壳就好说了,OD起来语言关系并不太大。
首先,运行发现有一个nag,一个key,一个name/serial,一个一个来。具体瞎网上输入去看对话框什么的我就不扯了...
Nag:
OD运行起来,F8或者shift+F8,找到这几个CALL的作用
在第一个nag弹出的位置下断,F9过去,之后进入Call中。
不断利用F8去找弹出Nag的具体位置,这题挺坑,跳转比较多,耐心一点,最后找到这个MessageBoxA函数的位置。
把这个MessageBoxA全部nop掉,顺利爆破掉Nag。
Key:
key也是比较简单的,一般来说仅仅只输入一个字符串的都会硬编码在程序内部。
crtl+F2重新启动程序后,F9运行,输入
不要点击确定,F12将程序停下来,去Alk+k去堆栈找这时调用这个对话框的MessageBox函数
因为一开始在Crtl+N查找参考模块中发现函数实在有点多,我不能分辨出来每一个MessageBox函数的作用才这样做,如果只有一个MessageBox函数的话就可以直接Crtl+N CALL过去就好...
找到后Show Call,看到了这是一个函数
push ebp
move ebp,esp
这时太明显不过的建立栈帧的过程。
在函数头部push ebp出下断,我要找是从哪里调用的函数
注意要F9两次,重新输入,点击Check it Baby!,
在右下角堆栈处找到最近一条Return语句:
0019F810 0042F509 返回到 Acid_bur.0042F509 来自 Acid_bur.0042A170
右键 Follow in Disassm..(反汇编跟随),转到调用位置,向上向下翻一翻(这是个好习惯..)
看到了Key果然是硬编码在程序中的,12345是我们输入的key,那么Hello Dude就应该是正确的key,检验一下:
最后就是稍费劲一点的name/serial啦,之所以说费劲,是因为不难,因为连我都做出来了...
同样,用F9然后输入序列号去找MessageBox的调用:
show call,并且同样的结构在函数头部push ebq出下断点,找到调用这个MessageBox函数
(姑且这么称它吧,因为它的作用就是弹出一个对话框)
的call
找到了,首先可以爆破,把这个call直接nop掉:
爆破后我们怎么输入都会成功的,不过,这个太low了,于是我们去找算法:
不断的F7,F8,F9在加上下断点去分析上面的程序(你不知道作为一个新手我用了多长时间)对上面的汇编程序进行分析:
首先我们在、
0042FB32 |. E8 39A6FFFF call Acid_bur.0042A170
上下断点,在不弹出错误对话框的情况下去分析上面的汇编代码:
看到
于是,试一下这个serial,他应该是根据我输入的name生成的。
,成功了。
换一个name,发现,仅仅只是中间的字符串变了,左右的CW等等字母没变,从上面分析也可以看出来,这个正确的serial是拼接出来的。
show code:
0042FA52 |. E8 D96EFDFF call Acid_bur.00406930 0042FA57 |. 83F8 04 cmp eax,0x4 ; 比较字符串的长度是否大于等于4 0042FA5A |. 7D 1D jge short Acid_bur.0042FA79 0042FA5C |. 6A 00 push 0x0 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:[0x430A48] 0042FA6D |. 8B00 mov eax,dword ptr ds:[eax] ; Acid_bur.00424090 0042FA6F |. E8 FCA6FFFF call Acid_bur.0042A170 ; 字符串长度小于4回到0042A170位置,输错错误对话框 0042FA74 |. E9 BE000000 jmp Acid_bur.0042FB37 0042FA79 |> 8D55 F0 lea edx,[local.4] 0042FA7C |. 8B83 DC010000 mov eax,dword ptr ds:[ebx+0x1DC] 0042FA82 |. E8 D1AFFEFF call Acid_bur.0041AA58 0042FA87 |. 8B45 F0 mov eax,[local.4] 0042FA8A |. 0FB600 movzx eax,byte ptr ds:[eax] ; 取出name字符串中的第一个字符 0042FA8D |. F72D 50174300 imul dword ptr ds:[0x431750] ; 乘以0x29(地址431750) 0042FA93 |. A3 50174300 mov dword ptr ds:[0x431750],eax 0042FA98 |. A1 50174300 mov eax,dword ptr ds:[0x431750] 0042FA9D |. 0105 50174300 add dword ptr ds:[0x431750],eax ; 再乘以2存在0x431750 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] ; Acid_bur.0042FBAC 0042FAC0 |. 68 C8FB4200 push Acid_bur.0042FBC8 ; UNICODE "-" 0042FAC5 |. 8D55 E8 lea edx,[local.6] 0042FAC8 |. A1 50174300 mov eax,dword ptr ds:[0x431750] 0042FACD |. E8 466CFDFF call Acid_bur.00406718 ; 这个call函数中的内容就是生成serial中间数字串的部分 0042FAD2 |. FF75 E8 push [local.6] ; local.6是中间生成的password数字串 0042FAD5 |. 68 C8FB4200 push Acid_bur.0042FBC8 ; UNICODE "-" 0042FADA |. FF75 F8 push [local.2] ; Acid_bur.0042FBB8 0042FADD |. 8D45 F4 lea eax,[local.3] 0042FAE0 |. BA 05000000 mov edx,0x5 0042FAE5 |. E8 C23EFDFF call Acid_bur.004039AC ; 字符串拼接 0042FAEA |. 8D55 F0 lea edx,[local.4] 0042FAED |. 8B83 E0010000 mov eax,dword ptr ds:[ebx+0x1E0] 0042FAF3 |. E8 60AFFEFF call Acid_bur.0041AA58 ;得到正确的password 0042FAF8 |. 8B55 F0 mov edx,[local.4] ; 输入的Password:12345 0042FAFB |. 8B45 F4 mov eax,[local.3] ; 正确的password 0042FAFE |. E8 F93EFDFF call Acid_bur.004039FC ; 明显就是两个字符串比较喽 0042FB03 |. 75 1A jnz short Acid_bur.0042FB1F
每个call我们不需要进去,把他看作是黑箱,了解他的功能就可以了。
首先是判断字符串的长度 ,之后取第一个字符,乘以地址0x431750的值(0x431750在数据区Crtl+G一下找到)
之后乘2,就得出那一串数字啦,关键的生成代码在:
0042FAC5 |. 8D55 E8 lea edx,[local.6] 0042FAC8 |. A1 50174300 mov eax,dword ptr ds:[0x431750] 0042FACD |. E8 466CFDFF call Acid_bur.00406718 ; 这个call函数中的内容就是生成serial中间数字串的部分 0042FAD2 |. FF75 E8 push [local.6] ; local.6是中间生成的password数字串
同样我们下断点进去:
真正的坑来了,进去之后发现了很多的跳转,我只知道对每一个字符都进行了操作,但是跳转太多了,没明白他的实现过程(汇编学的渣...),但确定这里就是生成字符串的位置,于是我又把他当黑箱了。
输入name:12345之后,在0x431750内存操作后得到0x0FB2,之后生成的数字串:4018,好吧,我猜到了:
16进制转10进制数字串,这就是整个call的功能...
OK,算法就是name输入后先根据上面的结果生成数字字符串,然后再和其他的字母,符号拼接,成最后的serial。这就是这个name/serial算法的求算过程...
能够写注册机了:
#include <iostream> #include <cstdio> #include <cstring> #define N 10000 using namespace std; int main() { char buffer[N]; short int c; cout<<"please input name:\n"; gets(buffer); if(strlen(buffer)<4) //判断序列号长度 { cout<<"Error!"<<endl; } else { c=buffer[0]; c=c*0x29; c=c*2; cout<<"serial:"<<endl; printf("Serial: CW-%4d-CRACKED\r\n",c); } return 0; }
实话说,题目不难,但是nag,name/serial,key都涉及到了,作为新手的我也废了一些功夫...过程挺详细,比其他的详细多了(啰嗦..)。
标签:
原文地址:http://www.cnblogs.com/Space-of-Bruce/p/5521794.html