软编码寻找序列号(二)

使用工具

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

逆向思路

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

    开始

    有点意思,输入内容之后没有任何提示,看来只有输入正确的序列号才会有提示;

  2. 倒入OD开始分析:

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

    • 既然界面没有按钮,那就得先猜测它的工作原理了,如果它会在输入正确之后提示的话,那么它就是实时获取我们输入的内容并进行判断,这样的话,有用的应该就是内存断点了;

    • 说干就干,既然要去内存中找我们输入的内容,那输入就一定得个性一点,起码不是内存中常见的,不然一找一大堆就无从下手了:

      输入

    • 然后去内存窗口按下Ctrl + B或者右键选择查找,输入我们的序列号点击确定开始查找,记得勾选区分大小写

      查找

    • 输入的还算奇葩,只找到了两个,选择第二个吧,虽然第一个是独立的,但它在内存中的范围不太像:

      找到

    • 设置内存访问断点,长度随意,看心情,我就选 5 个字节了;

      内存断点

    • 然后去程序界面再输入一个字符后,发现程序中断了:

      中断1

      不过中断的地方好像没什么用,我们输入的内容在EDX中,下面的代码段并没有操作它,F9继续运行程序;

    • 刚一进程序领空就发现了亮点,这里好像就是比较了:

      亮点

    • 分析一番之后发现,逻辑很常规:

      • 首先,假码 和 可疑字符串 的前 4 个字节的机器码进行比较,不相等跳转到下一步;

      • 接着,假码 和 可疑字符串 的第 1 个字节的机器码进行比较,不相等跳转到下一步

      • 最后,恢复寄存器的内容并RETN,然后就杳无音讯了;

        比较

    • 至于正确的序列号,当然就是那串可疑字符串了,那这串字符是怎么来的呢,不能每次都这么找吧:

      • 我们都知道RETN返回的是调用位置的下一行,那跟着RETN去看看:

        调用位置

      • 按照程序的流程走,既然这里是判断,那上面的就是获取和处理了,上面有好几个CALL,哪一个才是我们想要的那个呢,按照先获取后处理的流程来看,离我们最近的那个CALL应该是处理用的,那么这个CALL上面那两行代码应该就是它的参数,在参数上设置断点,看看参数是什么就知道这个CALL的用途了:

        重点

        设置断点,重载程序并运行后,程序中断了,但重点不在反汇编窗口,而是在堆栈窗口,我们发现了可疑字符串也就是正确的序列号,如果我们设置断点的这个CALL是处理函数的话,那么它还没运行怎么会出现正确的序列号呢?取消断点,在它上面那个CALL的参数上设置断点;

      • 和上面的情况一样,程序中断后,在堆栈窗口,我们又发现了正确的序列号,再次取消断点,给它上面那个CALL设置断点:

        新断点

        这次就好多了,虽然没发现有用的数据但正确的序列号也没有出现,那这个CALL很可能就是生成序列号的函数了,F7跟进去看看,结果很丧气,它也不是;

      • 那就接着往上呗,终于,在设置好断点,重载并运行程序之后有了欣喜的收获,用户名当作参数传递了:

        真的重点

        虽然还没有运行代码,但信息窗口已经告诉我们它是什么了;

      • 果断F7跟进去看看,都失败那么多次了,不差这一次:
        进CALL

        进来只有四行代码,但内容非常令人兴奋:这几行代码在检测有没有输入用户名;这就是一个好的开始,因为要操作数据的时候才会检测数据有没有输入;

      • 从这个CALl里出来的窗口很熟悉,因为我们在这里已经操作了 3 次了,也失败了 3 次,这次将会是最后一次了:

        多次操作

      • 这手灯下黑玩儿的漂亮,它一直在眼皮子底下计算序列号:

        序列号运算

        这就完了?那序列号呢?嘿嘿,在这里:

        序列号

        是的,运算结束后,虽然数据保存到了EBP-C的位置,但我们并没有弹栈,所以EAX没有被覆盖,双击EAX试试,对啦,将运算结果转为 10 进制就是这个用户名对应的序列号;

    • 序列号的算法:

      • 用户名最少两位;
      • 用户名的下标从 1 开始;
      • 取用户名每个字节和它对应的下标进行异或运算;
      • 所有字节异或后的结果累加在一起;
      • 取最后一个字节的机器码,与对应下标进行异或运算后,循环左移 C 位;
      • 然后把左移的结果和所有字节异或后累加的结果相加;
      • 最后把最终结果转换成 10 进制就是这个用户名对应的注册码;
    • 无图无真相

      • 例如:用户名feng对应的机器码是66 65 6E 67

      • 用户名的下标从 1 开始;

      • 每个字节分别与它对应的下标进行异或运算xor 66, 1 >> 67xor 65, 2 >> 67xor 6E, 3 >> 6Dxor 67, 4 >> 63;

      • 异或后的结果累加67 + 67 + 6D + 63 >> 19E;

      • 取最后一个字节的机器码,与对应下标进行异或运算后,循环左移 C 位xor 67, 4 >> 63ROL 63, C >> 63000;

      • 然后把左移的结果和所有字节异或后累加的结果相加63000 + 19E >> 6319E

      • 转换成 10 进制6319E >> 405918

        真相