Delphi,没有壳就好说了,OD起来语言关系并不太大。
首先,运行发现有一个nag,一个key,一个name/serial,一个一个来。具体往name,serial的位置上瞎输入字符串看情况什么的我就不扯了...
Nag:
OD运行起来,F8或者shift+F8,找到这几个CALL的作用
在第一个nag弹出的位置下断,F9过去,之后进入Call中。
不断利用F8去找弹出Nag的具体位置,这题挺坑,跳转比较多,耐心一点,最后找到这个MessageBoxA函数的位置。
把这个MessageBoxA全部nop掉,顺利爆破掉Nag。
Key:
key也是比较简单的,一般来说仅仅只输入一个字符串的都会硬编码在程序内部。
不要点击确定,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啦,之所以说费劲,是因为不难,因为连我都做出来了...
show call,并且同样的结构在函数头部push ebq出下断点,找到调用这个MessageBox函数
(姑且这么称它吧,因为它的作用就是弹出一个对话框)
的call
找到了,首先可以爆破,把这个call直接nop掉:
爆破后我们怎么输入都会成功的,不过,这个太low了,于是我们去找name/serial算法:
不断的F7,F8,F9在加上下断点去分析上面的程序(你不知道作为一个新手我用了多长时间)对上面的汇编程序进行分析:
首先我们在、
0042FB32 |. E8 39A6FFFF call Acid_bur.0042A170
上下断点,之后输入name:12345,seial=12345,带着这两个字符串再去分析汇编代码,才能得到有用的结果。
看到
于是,试一下这个serial,他应该是根据我输入的name:12345生成的。
换一个name:54321,发现,仅仅只是中间的数字字符串变了,左右的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
#include
#include
#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!"<
实话说,题目不难,但是nag,name/serial,key都涉及到了,作为新手的我也废了一些功夫...过程挺详细,比其他的详细多了(啰嗦..)。