VB 调试之分析 N-Code(一)

使用工具

  • OllyDbg修改版,简称OD
  • OD 汉化插件均来自互联网;
  • CrackMe来自互联网,仅供学习使用;
  • 文中特殊数字均是HEX,为了书写方便采用DEC

分析思路

  1. 首先运行软件,了解一下程序:

    运行程序

    O, No!

    • 首先是一个NAG 窗口:

      NAG 窗口:软件未注册或软件的试用版经常会弹出一些提示窗口要求注册,这些窗口被称为 nag 窗口,也叫烦人的窗口;

    • 然后才是软件窗口,要求输入注册码;

    • 输入随机字符点击 OK,一个奇怪的弹窗,应该是 No? Thanks?

  2. CrackMe倒入OD,开始分析:

    • 首先,第一件事儿是去掉烦人的窗口

      既然是弹窗,那么它肯定调用了MessageBox这个 API,不过,VB 有专有的 API rtcMsgBox:调用一个消息框,类似于 Windows 里的 MessageBox/A/EXA,此之前一定有个 PUSH 命令将要在消息框中显示的数据压入椎栈;

      1. 既然已经知道了它调用的 API,那还等什么,给rtcMsgBox设置断点:

        设置断点

        按下Ctrl + G输入rtcMsgBox,然后点击确定,程序会跳转到rtcMsgBox函数的行首,然后双击该行设置断点,并做好备注;

      2. 设置好断点后,F9运行程序,程序会中断在断点位置,然后在堆栈窗口跟踪调用位置:

        反汇编窗口中跟随

      3. 来到反汇编窗口,这个时候函数rtcMsgBox已经调用完成,既然找到了调用位置,那怎么能去掉烦人的窗口呢?

        返回位置

        设置好断点,重载并运行程序后,程序中断在预先设置好的断点位置,而下面一行的CALL就是调用rtcMsgBox的位置,如果把这个调用NOP了,是不是就没有NAG 窗口了,听起来蛮靠谱,试一试:

        NOP

      4. NOP了调用rtcMsgBoxCALL后,继续运行程序,但并没有按照预期显示程序主窗口,而是程序结束了,这是怎么回事儿呢?

        程序终止了

        按下-减号键,返回上层位置,把修改的内容保存下来试试:

        保存到文件

        双击运行保存的程序,没有任何反应,em ~~ 看来程序被玩坏了,此路不通;

      5. 重载并运行程序,重新来到反汇编窗口,既然把直接调用函数NOP了不行,那就只能继续运行程序,看看它的上一级调用:

        上级调用

        来到上级调用的返回位置,上一行的CALl就是刚才整段代码的调用位置,如果把这个CALl干掉,那么弹窗也就没有了,试一试,但愿程序不被玩儿坏;

        在这个CALl的参数的上一行设置断点,重载并运行程序;

      6. 程序中断在预定位置,选择下面CALl和它的参数用NOP填充:

        用NOP填充

      7. 继续运行程序,啊哈,期待已久的程序主窗口终于出现了:

        程序主窗口

        主程序出现,说明这个方法可行;

      8. 为了后面找码方便,把修改后的程序另存为一个新的程序:终于要和烦人的窗口说拜拜了,至于保存的方法,上面已经说明,不再赘述;

        NoNAG

        运行修改的程序,嗯,没有NAG 窗口,直接出现程序主窗体,一切 OK;

    • 搞定NAG 窗口后,是时候寻找一下注册码了:

      1. 把修改后的程序倒入OD,然后运行程序,主窗体出现:

        运行修改程序

      2. 输入随机注册码,但不要点 OK,但不要点 OK,但不要点 OK,重要的事情说三遍:

        内存访问断点

        接着到内存窗口,在CODE 段设置内存访问断点,然后到程序中点击 OK;

      3. 点击 OK 后,程序中断:

        程序中断

        这里的代码看不大懂,不过没关系,也没有找到需要的信息;

      4. 继续运行程序,直到看到了随机输入的注册码和字符串比较函数:

        字符串比较

        随机输入的字符作为字符串比较函数的参数,那另一个参数毋庸置疑,肯定是真实的注册码;

      5. 不过遗憾的是,由于这个软件是西欧字符软件,好多字符无法正确显示,找到了真实的注册码也无法输入,复制也不行,因为系统字符集中没有某些字符:

        字符集

      6. 既然无法输入真实注册码,那就只能让任意字符成为注册码了:

        比较不成立

        首先,字符串比较函数的返回结果非 0,说明两个参数不相等,继续向下分析;

        向下分析

        把比较的结果从 EAX 拷贝到 EDI 中,然后进行了一系列的操作,然而,如果 EDI 为 0,则这些操作没有任何意义,至于中间夹杂的其他操作可以忽略了,因为没有涉及到 EDI:

        操作 EDI

        这里有一个 JE 跳转,上一行代码是TEST DI, DI,如果 EDI 自身为 0,则TEST DI, DI影响 ZF 标志位,ZF 置 1,反之为 0,而恰恰 JE 根据 ZF 标志位跳转,可以看到,程序运行到了这里,ZF 依然为 0,则间接说明了输入的注册码不正确;

        既然这个跳转根据比较的结果进行跳转,那就可以确定它是关键跳,如果将 JE 修改为 JMP,是否就能满足让任意字符成为注册码?值得一试:

        修改为 JMP

        修改 JE 为 JMP 后,取消内存访问断点,然后F9运行程序,It’s OK,终于看到了成功弹窗,不过由于字符集的原因,部分字符无法显示;

      7. 保存修改到文件,然后输入任意字符:

        成功

        成功的弹窗是 Yes? Thanks?