首页
社区
课程
招聘
toshimi's serialme的逆向兼修正
发表于: 2006-9-1 20:47 6205

toshimi's serialme的逆向兼修正

2006-9-1 20:47
6205

下载地址:http://www.crackmes.de/users/toshimi/serialme
使用工具:

反编译及调试工具:OllyDbg,IDA,Resource Hacker,LordPE,ImportREC,FixRes
程序开发包:MASM32

1. 脱壳及修复资源

  这个Crackme加了UPX的壳,如果只是为了破解,本来可以带壳调试,但是不脱壳则没有
办法提取它的资源,也就无法构建源代码。

  脱壳的基本过程可以参考脱壳版FAQ:

          http://bbs.pediy.com/showthread.php?s=&threadid=20366

这里就不再详解。脱了壳之后的程序虽然能正常运行,但是用ResHacker无法解析资源,必
须再用FixRes修复一遍以后才行。

2. 逆向

  用IDA载入可执行文件。这个程序的入口点有些模仿C语言的WinMain()函数,这里把它
命名为_CMain。这个子程序中进行注册窗口类、创建窗口、显示窗口和建立消息循环的工
作,根据窗口类成员lpfnWndProc的值可以找到主窗口过程。

  进入主窗口程序可以发现,主窗口上的编辑框以及三个按钮都是用CreateWindowEx即时
创建的,这也是资源脚本中没有定义控件的一个原因。在创建控件的时候需要记录hMenu也
就是CreateWindowEx的第十个参数,这个参数相当于对话框程序中的所谓控件ID,在处理
WM_COMMAND消息的时候要用到。WM_PAINT分支的代码看起来比较多,但其功能说白了只有一
个,那就是把资源中唯一的图片贴到窗口的背景上去。

  我们最为关注的部分是WM_COMMAND分支,这个分支中主要对付的就是由三个按钮发送的
消息,而“About”和“Clean”两个按钮引发的事件又相对比较简单,主要的是“Enter”
按钮引发的事件。在“Enter”按钮事件分支里,程序在GetDlgItemText函数取得序列号输
入框的内容后调用一个过程sub_401373(这里把它重命名为_CheckSerial),其内容如下:

=====================  以下是代码  =========================
UPX0:00401373 _CheckSerial    proc near               ; CODE XREF: _ProcWinMain+176p
UPX0:00401373                 push    eax
UPX0:00401374                 xor     eax, eax
UPX0:00401376                 xor     edx, edx
UPX0:00401378                 lea     esi, szSerial
UPX0:0040137E
UPX0:0040137E loc_40137E:                             ; CODE XREF: _CheckSerial+1Ej
UPX0:0040137E                                         ; _CheckSerial+25j
UPX0:0040137E                 mov     al, [edx+esi]   ; 取用户名的第i个字符
UPX0:00401381                 test    al, al
UPX0:00401383                 jz      short loc_40139A
UPX0:00401385                 mov     cl, 1
UPX0:00401387                 inc     edx
UPX0:00401388                 inc     cl
UPX0:0040138A                 div     cl              ; 除以2
UPX0:0040138C                 cmp     ah, 0
UPX0:0040138F                 jz      short loc_401393 ; bh = 序列号中偶字符的个数
UPX0:00401391                 jmp     short loc_40137E
UPX0:00401393 loc_401393:                             ; CODE XREF: _CheckSerial+1Cj
UPX0:00401393
UPX0:00401393                 inc     bh              ; bh = 序列号中偶字符的个数
UPX0:00401395                 sub     cl, 2
UPX0:00401398                 jmp     short loc_40137E
UPX0:0040139A
UPX0:0040139A loc_40139A:                             ; CODE XREF: _CheckSerial+10j
UPX0:0040139A                 push    eax
UPX0:0040139B                 rol     ebx, 8          ; 将上面得到的bh值存入ebx的高16位
UPX0:0040139E                 push    edx             ; 现dl=序列号串长
UPX0:0040139F                 mov     ah, 0
UPX0:004013A1                 mov     al, 1           ; mov eax, 1
UPX0:004013A3                 mov     cl, 2
UPX0:004013A5
UPX0:004013A5 loc_4013A5:                             ; CODE XREF: _CheckSerial+3Fj
UPX0:004013A5                 mov     bl, [eax+esi]   ; 取szSerial的第2, 5, 9, 14等字符累加

到dl
UPX0:004013A8                 test    bl, bl
UPX0:004013AA                 jz      short loc_4013B4
UPX0:004013AC                 add     dl, bl
UPX0:004013AE                 inc     cl
UPX0:004013B0                 add     al, cl          ; 1+3+4+5+...
UPX0:004013B2                 jmp     short loc_4013A5 ; 取szSerial的第2, 5, 9, 14等字符累加

到dl
UPX0:004013B4
UPX0:004013B4 loc_4013B4:                             ; CODE XREF: _CheckSerial+37j
UPX0:004013B4                 mov     ax, 0FFFFh
UPX0:004013B8                 cmp     dl, 0ABh        ; 如相等,下面调用sub_40145D,否则调用

sub_4013D9
UPX0:004013BB                 jnz     short loc_4013C8
UPX0:004013BD                 inc     ax
UPX0:004013BF                 jmp     short loc_4013CC
UPX0:004013C1
UPX0:004013C1 loc_4013C1:                             ; CODE XREF: _CheckSerial:loc_4013CCj
UPX0:004013C1                                         ; _CheckSerial+60j
UPX0:004013C1                 call    sub_4013D9
UPX0:004013C6                 jmp     short loc_4013D5
UPX0:004013C8
UPX0:004013C8 loc_4013C8:                             ; CODE XREF: _CheckSerial+48j
UPX0:004013C8                 add     ax, 1
UPX0:004013CC
UPX0:004013CC loc_4013CC:                             ; CODE XREF: _CheckSerial+4Cj
UPX0:004013CC                 jb      short loc_4013C1
UPX0:004013CE                 call    sub_40145D      ; 关键是有否把ecx清零
UPX0:004013D3                 jmp     short loc_4013C1
UPX0:004013D5
UPX0:004013D5 loc_4013D5:                             ; CODE XREF: _CheckSerial+53j
UPX0:004013D5                 pop     edx
UPX0:004013D6                 pop     eax
UPX0:004013D7                 pop     eax
UPX0:004013D8                 retn
UPX0:004013D8 _CheckSerial    endp
=====================  以上是代码  =========================

这段代码中有一些无用的语句,关键的地方是从loc_4013A5开始的,在这个标号之前dl的值
也就是edx的值等于序列号的串长,而loc_4013A5开始的循环是取序列号中的一些字符累加
到dl上,具体计算一下可以知道它取的字符依次是第2,5,9,14……个(由于下文会提到
合法字符串有长度上的要求,实际取的仅仅是第2,5,9个字符)。然后看dl是否等于
0ABh,如果等则先调用sub_40145D再调用sub_4013D9,否则直接调用sub_4013D9(注意inc
指令和加1的add指令在标志影响方面的区别)。那么sub_40145D有什么特别之处呢?跟进去
看一下:

=====================  以下是代码  =========================
UPX0:0040145D sub_40145D      proc near               ; CODE XREF: _CheckSerial+5Bp
UPX0:0040145D                 push    ebx
UPX0:0040145E                 cmp     byte ptr [esi+5], '3' ; 第6个字符必须是'3'
UPX0:00401462                 jnz     short loc_401496
UPX0:00401464                 cmp     byte ptr [esi+2], '-' ; 第3个字符必须是'-'
UPX0:00401468                 jnz     short loc_401496
UPX0:0040146A                 cmp     byte ptr [esi+0Ah], '0'
UPX0:0040146E                 jbe     short loc_401496
UPX0:00401470                 cmp     byte ptr [esi+0Bh], '1' ; 第12个字符必须是'1'
UPX0:00401474                 jnz     short loc_401496
UPX0:00401476                 cmp     byte ptr [esi+3], '1' ; 第4个字符必须是'1'
UPX0:0040147A                 jnz     short loc_401496
UPX0:0040147C                 cmp     byte ptr [esi+9], '0' ; 第10个字符必须是'0'
UPX0:00401480                 jnz     short loc_401496
UPX0:00401482                 cmp     byte ptr [esi+6], '9' ; 第7个字符必须是'9'
UPX0:00401486                 jnz     short loc_401496
UPX0:00401488                 cmp     byte ptr [esi+0Ah], '9' ; 第11个字符必须是数字且不为0
UPX0:0040148C                 ja      short loc_401496
UPX0:0040148E                 cmp     byte ptr [esi+7], '3' ; 第8个字符必须是'3'
UPX0:00401492                 jnz     short loc_401496
UPX0:00401494                 xor     ecx, ecx
UPX0:00401496
UPX0:00401496 loc_401496:                             ; CODE XREF: sub_40145D+5j
UPX0:00401496                                         ; sub_40145D+Bj
UPX0:00401496                                         ; sub_40145D+11j
UPX0:00401496                                         ; sub_40145D+17j
UPX0:00401496                                         ; sub_40145D+1Dj
UPX0:00401496                                         ; sub_40145D+23j
UPX0:00401496                                         ; sub_40145D+29j
UPX0:00401496                                         ; sub_40145D+2Fj
UPX0:00401496                                         ; sub_40145D+35j
UPX0:00401496                 pop     ebx
UPX0:00401497                 retn
UPX0:00401497 sub_40145D      endp
=====================  以上是代码  =========================

这个过程归结起来就是根据一些条件决定是否将ecx清零,当然现在我们暂时还不知道其目
的是什么。跟进另外一个过程sub_4013D9看看:

=====================  以下是代码  =========================
UPX0:004013D9 sub_4013D9      proc near               ; CODE XREF: _CheckSerial:loc_4013C1p
UPX0:004013D9                 mov     dh, bh          ; bh = 0
UPX0:004013DB                 lea     edi, szInfo
UPX0:004013E1                 xor     ebx, ebx
UPX0:004013E3                 mov     dl, al
UPX0:004013E5                 add     esp, 4          ; 废除返回地址,现[esp] = 序列号长度+1
UPX0:004013E8                 jnb     short loc_401426 ; 绝对跳转
UPX0:004013EA
UPX0:004013EA loc_4013EA:                             ; CODE XREF: sub_4013D9+2Cj
UPX0:004013EA                                         ; sub_4013D9+31j
UPX0:004013EA                                         ; sub_4013D9+6Fj
UPX0:004013EA                                         ; sub_4013D9+7Aj
UPX0:004013EA                                         ; sub_4013D9+7Ej
UPX0:004013EA                 mov     [ebx+edi], dl
UPX0:004013ED                 inc     ebx
UPX0:004013EE                 cmp     dl, 'o'
UPX0:004013F1                 jz      short loc_401407
UPX0:004013F3                 cmp     dl, 'r'
UPX0:004013F6                 jz      short loc_40140C
UPX0:004013F8                 cmp     dl, 'I'
UPX0:004013FB                 jz      short loc_401455
UPX0:004013FD                 cmp     dl, 'n'
UPX0:00401400                 jz      short loc_401446
UPX0:00401402                 add     dl, 2Ch
UPX0:00401405                 jmp     short loc_4013EA
UPX0:00401407
UPX0:00401407 loc_401407:                             ; CODE XREF: sub_4013D9+18j
UPX0:00401407                 add     dl, 3
UPX0:0040140A                 jmp     short loc_4013EA
UPX0:0040140C
UPX0:0040140C loc_40140C:                             ; CODE XREF: sub_4013D9+1Dj
UPX0:0040140C                 mov     byte ptr [ebx+edi], 72h
UPX0:00401410                 inc     ebx
UPX0:00401411                 mov     byte ptr [ebx+edi], 65h
UPX0:00401415                 inc     ebx
UPX0:00401416                 mov     byte ptr [ebx+edi], 63h
UPX0:0040141A                 inc     ebx
UPX0:0040141B                 mov     byte ptr [ebx+edi], 74h
UPX0:0040141F                 inc     ebx
UPX0:00401420                 mov     byte ptr [ebx+edi], 21h
UPX0:00401424                 jmp     short loc_401459
UPX0:00401426
UPX0:00401426 loc_401426:                             ; CODE XREF: sub_4013D9+Fj
UPX0:00401426                 pop     eax
UPX0:00401427                 xor     eax, 0Bh
UPX0:0040142A                 cmp     eax, 7          ; 7 xor B = C
UPX0:0040142D                 jnz     short loc_40144A ; 注册失败
UPX0:0040142F                 cmp     ecx, 0          ; 关键在于前面有否把ecx清零
UPX0:00401432                 jnz     short loc_40144A ; 注册失败
UPX0:00401434                 xor     dh, 0Ch
UPX0:00401437                 cmp     dh, 48h         ; 48 xor C = 44('D')
UPX0:0040143A                 jnz     short loc_40144A ; 注册失败
UPX0:0040143C                 mov     byte ptr [eax+edi+1], 0
UPX0:00401441                 mov     byte ptr [eax+edi+2], 0
UPX0:00401446
UPX0:00401446 loc_401446:                             ; CODE XREF: sub_4013D9+27j
UPX0:00401446                 mov     dl, 43h
UPX0:00401448                 jmp     short loc_4013EA
UPX0:0040144A
UPX0:0040144A loc_40144A:                             ; CODE XREF: sub_4013D9+54j
UPX0:0040144A                                         ; sub_4013D9+59j
UPX0:0040144A                                         ; sub_4013D9+61j
UPX0:0040144A                 xor     ebx, ebx        ; 注册失败
UPX0:0040144C                 mov     dl, 49h
UPX0:0040144E                 mov     eax, 7
UPX0:00401453                 jmp     short loc_4013EA
UPX0:00401455
UPX0:00401455 loc_401455:                             ; CODE XREF: sub_4013D9+22j
UPX0:00401455                 mov     dl, 6Eh
UPX0:00401457                 jmp     short loc_4013EA
UPX0:00401459
UPX0:00401459 loc_401459:                             ; CODE XREF: sub_4013D9+4Bj
UPX0:00401459                 sub     esp, 8
UPX0:0040145C                 retn
UPX0:0040145C sub_4013D9      endp ; sp = -4
=====================  以上是代码  =========================

从loc_401426这里开始,前面的答案就一一揭晓了:首先弹出到eax的序列号串长(前面在
40139E处推入的edx)与0Bh相xor后的值必须是7,换言之串长必须是7 xor 0Bh = 0Ch,否
则注册失败;其次ecx的值必须是0,而上面知道只有执行过sub_40145D并且满足其中所有
条件后,ecx才会被清零。

  但第三个条件就令人困惑了,它要求的是dh的值等于48h xor 0Ch = 44h,而4013D9处
的指令表明dh的值来自bh,而bh的值可以追溯到40139B处的rol ebx,8指令,此句后的bh值
等于此句前的bl值,但此句前就再也找不到可见的语句修改ebx的值了,用调试器跟踪可以
发现这时的bl值是零,也就是说,这个条件根本不可能达成!这一来,无论输什么号都不会
注册成功。所谓Bug就在这里!

  在逆向出的代码中我放弃了这个判断条件,这一来,合法序列号的要求应该是:

  (1)长为12字符;
  (2)第3、4、6、7、8、10、12个字符分别为'-'、'1'、'3'、'9'、'3'、'0'、'1',
       第11个字符在'1'到'9'之间;
  (3)第2、5、9个字符之和应等于(0ABh - 0Ch = )9Fh。

  另外在WM_COMMAND分支里不论是按下哪个按钮最终都会调用一个LoadBitmap,多次按下
就会多次调用,显得多余。个人认为这应该放到WM_CREATE分支里完成。

  上传内容(在MASMv9下编译通过)
     1.asm 汇编源文件
     1.rc 资源脚本
     Icon_2.ico 图标
     Bitmap_1.bmp 位图


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (7)
雪    币: 338
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
强!真是好文章!
2006-9-1 23:16
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
3
有点强,支持+学习
2006-9-2 00:06
0
雪    币: 333
活跃值: (11)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
4
支持一下
2006-9-2 11:45
0
雪    币: 256
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
5
果然好文章,支持支持!!!
2006-9-2 11:50
0
雪    币: 112
活跃值: (16)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
6
兄弟,我顶你一下!你是否愿意!
2006-9-3 04:06
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
7
感谢大家的支持。今天心情巨不爽……
2006-9-3 09:30
0
雪    币: 47147
活跃值: (20450)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
8
转份本地,因为原链接时间长了有失效可能性。
上传的附件:
2006-9-3 09:49
0
游客
登录 | 注册 方可回帖
返回
//