硬编码寻找序列号(六)

使用工具

  • OllyDbg 1.10原版,简称OD
  • OD插件:中文搜索引擎ApiBreak
  • OD 汉化插件均来自互联网;
  • CrackMe【歪坑牌 CM2.2】来自互联网,仅供学习使用;
  • 文中特殊数字均是HEX,为了书写方便采用DEC

逆向思路

  1. 首先,打开软件到处点点,逆向一个软件起码要会用吧,不会用就没必要说逆向了;

    • 这个软件的操作有点不同寻常,填写内容之后会提示注册成功,并且通过重启来验证是否正确:

      流程

  2. 倒入OD开始分析:

    • 首先按下Ctrl + AOD分析一下代码;

    • 接着按下Ctrl + N查看使用了哪些API,翻了翻还真不少,搜索一下,GetDlgItemTextA没找到,那就试试GetWindowTextA,还真有,在GetWindowTextA上设置断点,然后去断点窗口双击断点进入反汇编窗口给断点设置备注,这是一个好的习惯,请保持;

    • F9运行程序,输入并点击登录之后,还是熟悉的弹窗,点击弹窗的确定按钮之后,嘿,又回到了解放前,这让我很困惑,难道它是在弹窗之后运行吗?毫无头绪;

    • TranslateMessage呢?没有 Buffer,无从下手;

    • MessageBox设置断点?皮,它都重启了,断点有什么*用,逆推呢?设置断点跟随之后发现没有什么有价值的内容;

    • 试一试内存断点,暂停的地方不重要而且在断点位置无限循环,根本连登录按钮都点不了,既然都没有登录肯定没有验证,断点的地方当然不重要了;

    • 试试消息断点,暂停了,但没有 Buffer,无从下手;

    • 试试中文搜索引擎

      • 中文搜索引擎不只是找关键字的,还可以找到其它有用的信息,而恰恰这才是重点:

        关注点

      • 如果看到software说明这个程序使用了注册表,而关键信息可能就存储在注册表中:

        注册表

      • 既然它将输入的内容存储在注册表中,那么验证的时候一定会去注册表取回内容,所以这个节点才是关键的环节,重载程序,删除之前设置的无用断点,接下来就是重点了;

      • 至于如何设置断点呢,那就要使用今天的主角儿了ApiBreak,点击菜单栏的插件选项并选择ApiBreak >> API断点,然后选择注册表以及RegQueryValueExA选项之后,点击确定;(翻译一下RegQueryValueExAReg是注册表,Query是查询,Value是值,ExA是 16 进制,连起来应该是设置查询注册表16进制值的断点,不知道对不对)

        设置断点

        当然,也可以直接使用系统 APIRegQueryValueExA 或 RegQueryValueExW来设置断点,这个插件就是简化了操作步骤,不用我们去寻找系统 API;

      • 设置好断点之后,运行程序,程序会中断多次,应该是查询了很多次,但我们的关注点只是两个:用户名密码,那就继续运行程序,直到在堆栈窗口看到需要的信息:

        用户名出现了两次,第一次Buffer中没有地址,第二次Buffer中才有地址,第二次才是我们关注的重点, 在Buffer上右键数据窗口中跟随,然后Ctrl + F9执行到返回,果然,它获取了我们输入的用户名:

        用户名

        同样的情况也会出现在密码身上,继续运行程序,获取密码:

        密码

      • 既然它已经获取了用户名和密码,那就上正菜吧:
        获取密码后,在Buffer上右键数据窗口中跟随,然后Ctrl + F9执行到返回,果然,它获取了我们输入的密码,在数据窗口选中密码数据右键断点 >> 内存访问设置内存访问断点

        内存访问断点

      • 然后继续运行程序,程序中断了,但看了看代码发现这里不是重点,而且继续运行还会中断,数一数,中断的次数刚好是密码的长度,并且每运行一次,ESI寄存器中存储的密码都会向后移动一位;

      • 继续运行程序,程序再次中断,重点来了,开始分析:

        分析完毕

        分析完毕,逻辑似曾相识的感觉,没错,和逆向分析之硬编码寻找序列号(五)中的逻辑一模一样:

        • 首先,假码 和 可疑字符串 的前 4 个字节的机器码进行比较,不相等跳转到下一步;
        • 接着,假码 和 可疑字符串 的第 1 个字节的机器码进行比较,不相等跳转到下一步;
        • 最后,恢复寄存器的内容并RETN,然后弹窗,最后回到解放前;
    • 分析完了,那真正的密码是什么呢?当然就是“可疑字符串”:一串看起来杂乱无章的字符,那密码的计算规则又是什么呢?

      RETN返回的地方永远是调用它的位置的下一行,那如果跟着RETN是不是就能找到调用它的地方,结果是肯定的:

      调用

      如果JNZ是返回的位置,那上面一行的CALL就是调用的地方,如何证明呢?很简单,JNZ根据ZF 标志位跳转,如果跳转肯定就注册失败了,那不跳转是不是就成功了?试一试,将ZF 标志位置 1,然后运行程序,正如所料,注册成功:

      注册成功

      这就说明,这个CALL很重要,给CALL的位置设置断点,重载程序,程序在CALL的位置暂停,既然CALL内部是判断,那上面很可能就是获取并处理了,既然正确的密码是一串杂乱无章的字符,那么它应该需要一个CALL去处理,所以,大胆的猜测一下,上面那个CALL可能就是处理程序,给上面那个CALL也设置断点:

      处理

      再次重载程序,程序中断,F8向下执行,查看中间那两行代码干了什么,很有可能就是判断CALL(也就是函数)的参数:

      参数

      • 两行代码执行后,EAX的数据在寄存器窗口看的很清楚,就是我们输入的假码;
      • EDX的数据是什么呢?选中EDX右键数据窗口中跟随,数据一目了然,正是比较时用的可疑字符串;
      • 这也就证明了,上面那个CALL就是处理数据用的;
      • 而密码的规则:在好多个CALL之间跳来跳去分析之后,发现是一串固定的字符串加上用户名的 16 进制表示;
      • 如:yang转换成 16 进制是79616E67,那么密码就是€队蠹易仿耄79616E67
    • 测试一下:

      测试