UltraTagger逆向算法分析
POJIE提交于:2007-10-18 | 本文目前专长值:文/图 TC-XB
UltraTagger是一款非常不错的MP3处理软件,可以批量修改MP3文件名和强大的文件格式转换功能。在使用的过程中,我体会到了它的强大功能,但也发现没有注册的版本每一次运行都要等待10秒钟,而且还有使用时间的限制。于是我尝试着分析了一下这款软件,发现它的算法十分奇特,很具有代表性。它究竟采用了一种什么样的算法呢?让我们一点点的分析,一层层揭开它的面纱吧!
首先用PEiD检查这款软件,发现没有加壳。一运行该软件就出现了要求我们注册的提示窗口,如图1所示,并且可以看出我们必须等待10秒钟才可以使用,且试用期只有30天。这时我们不要急着关掉它,点击“Register”按钮,打开注册窗口,如图2所示。任意输入注册名和注册码,呵呵,弹出了一个错误提示对话框,如图3所示,
这个提示十分有用。因为我们从这个对话框中得到了重要的线索,一定要记住这个对话框的内容哦:“invalid registration code!”。
现在该关掉软件打开我们的武器OD了,用OD载入这个软件,打开字符串查找窗口,几秒后OD就为我们找到了好大一堆字符串。面对这一片字符串的海洋,我们该从哪里下手呢?不要慌,还记得刚才那个对话框的内容吗?没错,现在就是要在所有的字符串中找到这句提示。经过一番努力,总算发现了它的身影,如图4所示,双击它就可以直接来到对应的代码处了,具体代码如下所示。
00548DE8 |. 6A 00 push 0
; /Arg1 = 00000000
00548DEA |. 66:8B0D 3C8E5>mov cx, [548E3C]
; |
00548DF1 |. B2 02 mov dl, 2 ; |
00548DF3 |. B8 488E5400 mov eax, 00548E48
; |thanks for registering ultratagger!
00548DF8 |. E8 0FF5EEFF call 0043830C
; \UltraTag.0043830C
00548DFD |. EB 15 jmp short 00548E14
00548DFF |> 6A 00 push 0
; /Arg1 = 00000000
00548E01 |. 66:8B0D 3C8E5>mov cx, [548E3C]
; |
00548E08 |. B2 01 mov dl, 1 ; |
00548E0A |. B8 748E5400 mov eax, 00548E74
; |invalid registration code!
00548E0F |. E8 F8F4EEFF call 0043830C
; \UltraTag.0043830C
仔细观察这段代码,发现它不仅包含了注册失败时的提示“invalid registration code!”,也包含了注册成功的提示“thanks for registering ultratagger!”。结合这两点我们可以确定,这里就是软件整个注册流程的最后一个地方。所有的关键部分,例如注册信息验证部分、算法部分、注册码有效性验证部分等,都在这段代码的前面,所以现在我们就要顺藤摸瓜地找到最关键的算法。当我们把鼠标定位在地址00548DFF处时,OD窗口给了我们一个重要的提示线索。呵呵,看到了吗?如图5所示。OD告诉我们这个跳转来自00548DAD这个地方。不要犹豫,直接跟踪来到00548DAD这里,代码如下所示。
00548D9C |. E8 2FAEF1FF call 00463BD0
;取输入的注册码
00548DA1 |. 8B55 FC mov edx, [ebp-4]
;将输入的注册码保存在寄存器EDX中
00548DA4 |. 8BC3 mov eax, ebx
00548DA6 |. E8 7982FFFF call 00541024
;算法关键CALL
00548DAB |. 84C0 test al, al ;检查标志位
00548DAD |. 74 50 je short 00548DFF
;标志位等于0就跳向失败
看到了吗?这就是标准的注册码检验流程:记录输入的注册信息,将输入的注册码与经过算法计算得到的注册码进行比较,相等就注册成功,不相等就失败。
现在我们有两条路可以选择:直接在关键跳转处爆破,或是进一步跟踪寻找算法。如果选择爆破,那就把爆破点定位在“00548DAD |. 74 50 je short 00548DFF”这个关键跳转处,将它改为“00548DAD |. 74 50 je short 00548DE8”就可以爆破了。但我们有更高一层的目标,所以就不能止步在爆破这一层上,我们的目标是分析出软件的算法。
注意上面的算法关键CALL,我们跟进后会来到如下的代码处。
00541024 /$ 55 push ebp ;来到这里
00541025 |. 8BEC mov ebp, esp
……省略一些代码……
00541047 |. E8 8040ECFF call 004050CC
;取输入注册码的位数
0054104C |. 83F8 0E cmp eax, 0E
;注册码位数与14相比较
0054104F |. 0F8C C0000000 jl 00541115
;小于14位就跳走
00541055 |. 8B45 FC mov eax, [ebp-4]
;将输入的注册码保存在EAX中
00541058 |. BA 40115400 mov edx, 00541140
;7413-6386-5454
这时OD提示我们,内存中出现了一个固定的字符串:7413-6386-5454,这个字符串有什么用呢?现在我们还不能确定,继续分析代码。
0054105D |. E8 B641ECFF call 00405218
;将输入的注册码与固定字符串比较
00541062 |. 0F84 AD000000 je 00541115
;相等就跳走,不相等就继续比较
当跟踪经过这一段代码时会发现,我们任意输入的注册码在内存中出现了,而且程序还将这个任意输入的注册码与一个固定的字符串作比较,比较完毕后还有一处明显的跳转。这时我们就可以肯定上文中出现的那个固定字符串“7413-6386-5454”恰恰就是软件的一个固定注册码。这一收获真是让人意外!不过不要满足哦,我们的目标可是分析出它的算法呢。
通过上面的一小段代码的分析,我们可以得到两个很重要的信息:一是注册码不能小于14位;二是注册码的格式应该是“P1-P2-P3”,即分成三段,每段4位字符。记住这两个结论哦!我们继续分析,来到下面的代码处。
00541068 |. 8B45 FC mov eax, [ebp-4]
0054106B |. 8A00 mov al, [eax]
;取注册码的第一位
0054106D |. 3C 36 cmp al, 36
;注册码第一位与0x36(十进制的6)比较
0054106F |. 0F86 A0000000 jbe 00541115
;小于6就跳走,大于等于6继续比较
这段代码检验的是我们输入的注册码的第一位。通过分析,我们可以看出程序要求注册码的第一位必须大于6。
00541075 |. 8B 45 FC mov eax, [ebp-4]
00541078 |. 8A 40 01 mov al, [eax+1]
;取输入注册码的第二位
0054107B |. 3C 35 cmp al, 35
;注册码第二位与5比较
0054107D |. 0F83 92000000 jnb 00541115
;小于5就继续比较下一位
检查完第一位后,紧接着检查了注册码的第二位,要求注册码的第二位必须小于5。
00541083 |. 8B45 FC mov eax, [ebp-4]
00541086 |. 8A40 02 mov al, [eax+2]
;取注册码的第三位
00541089 |. 3C 31 cmp al, 31
;第三位是否等于1
0054108B |. 0F85 84000000 jnz 00541115
;不相等就跳走
00541091 |. 8B45 FC mov eax, [ebp-4]
00541094 |. 8A40 03 mov al, [eax+3]
;取注册码的第四位
00541097 |. 3C 32 cmp al, 32
; 大于2,第四位是否大于2
00541099 |. 76 7A jbe short 00541115
;大于2才能继续比较
0054109B |. 8B45FC mov eax, [ebp-4]
0054109E |. 8A40 04 mov al, [eax+4]
;取注册码的第五位
005410A1 |. 3C 2D cmp al, 2D
;第五位是否等于“2D”
005410A3 |. 75 70 jnz short 00541115
;不相等就跳向失败
上面的代码对注册码的第三到第五位做了检验。我们可以看出程序要求注册码的第三位必须等于1,第四位必须大于2,第五位必须等于“2D”。呵呵,有的朋友可能会问了,这个“2D”是什么呢?其实这里的“2D”就是连接符号“-”的HEX值,所以“2D”对应的就是符号“-”。到这里我们就能进一步的确定我们刚才得到的结论了,也就是注册码的格式应该是“P1-P2-P3”,即分成三段,每段4位字符。
005410A5 |. 8B45 FC mov eax, [ebp-4]
005410A8 |. 8A40 05 mov al, [eax+5]
;取注册码的第六位
005410AB |. 3C 35 cmp al, 35
;第六位大于5吗?
005410AD |. 76 66 jbe short 00541115
;大于5就继续
005410AF |. 8B45 FC mov eax, [ebp-4]
005410B2 |. 8A40 06 mov al, [eax+6]
;取注册码的第七位
005410B5 |. 3C 34 cmp al, 34
;小于4,第七位小于4吗?
005410B7 |. 73 5C jnb short 00541115
;小于4就满足条件了
005410B9 |. 8B45 FC mov eax, [ebp-4]
005410BC |. 8A40 07 mov al, [eax+7]
;取注册码的第八位
005410BF |. 3C 38 cmp al, 38
;等于8,第八位等于8吗?
005410C1 |. 75 52 jnz short 00541115
;等于就继续
005410C3 |. 8B45 FC mov eax, [ebp-4]
005410C6 |. 8A40 08 mov al, [eax+8]
;取注册码的第九位
005410C9 |. 3C 37 cmp al, 37
;小于7,第九位小于7吗?
005410CB |. 73 48 jnb short 00541115
;小于7就继续
005410CD |. 8B45 FC mov eax, [ebp-4]
005410D0 |. 8A40 09 mov al, [eax+9]
;取注册码的第十位
005410D3 |. 3C 2D cmp al, 2D
;第十位等于“2D”吗?
005410D5 |. 75 3E jnz short 00541115
;不等于就跳向失败
检查完了第一部分的注册码,程序又对注册码的第二部分(第六位到第十位)进行了检验。注册码的第六位必须大于5,第七位必须小于4,第八位必须是8,第九位必须小于7,第十位必须是连接符“-”。那么注册码的第三部分又必须满足什么样的条件呢?
005410D7 |. 8B45 FC mov eax, [ebp-4]
005410DA |. 0FB640 0A movzx eax, byte ptr [eax+A];
005410DE |. 83C0 D0 add eax, -30
005410E1 |. 83E8 0A sub eax, 0A
当我们继续向下分析时发现,对注册码第三部分的每一位进行检验时都用到了上面这段代码。这段代码的含义就是要求当前某一位注册码必须是数字。所以我们就可以推断出注册码的第三部分可以是任意一个四位数。
对注册码的三个部分进行检验以后,算法的核心部分也就结束了。现在根据我们得到的各个结论来推导出一组注册码,并在软件中实践一下,看看我们的结论是否正确。我们一部分一部分地进行推导。
第一部分(第一到第五位)需要满足的条件是:第一位大于6,第二位小于5,第三位必须是1,第四位大于2,第五位连接符“-”。我们很容易得到一组满足条件的数字:7413。
第二部分(第六到第十位)需要满足的条件是:第六位大于5,第七位小于4,第八位必须是8,第九位小于7,第十位连接符“-”。同样可以得到一组数字:6386。
第三部分可以是任意四位数:1234。
所以,我们得到的一组注册码是:7413-6385-1234。检验一下,看到了吗?注册成功了。最后别忘了,在前面我们还得到了一组固定注册码:7413-6386-5454哦。至此,这个软件就算彻底地被我们成功破解了!
本文发布者:POJIE (在本营发布了条专长内容) 专长值: | 汗水: |
POJIE在本栏的其他内容查看全部


专长值: |
汗水: