硬编码寻找序列号(三)

使用工具

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

逆向思路

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

    • 这个 CrackMe 不一般,有两个部分,两个按钮,那就分两部分分析喽(这句话绕不绕):

      CrackMe

  2. 倒入OD开始分析:

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

    • 接着按下Ctrl + N查看使用了哪些API,咦,发现可疑函数:

      第一部分

      很好的开始,在GetWindowTextA函数上设置断点,然后去断点窗口双击断点进入反汇编窗口给断点设置备注,这是一个好的习惯,请保持;

    • 然后F9运行程序,“随便输入” 一些字符后点击验证,可以随便输入,但请记住它;

    • 程序断在了设置断点的 API 的行首,在堆栈窗口可以看到 API 的各种参数,而参数 Buffer 里存放着函数返回的数据,选中 Buffer 右键数据窗口中跟随,可以看到,由于断点中断在了函数的第一行,所以这个地址里什么也没有:

      Buffer

    • 接着按下 Ctrl + F9 或点击菜单栏的调试选项并选择执行到返回,然后发现 Buffer 对应的地址里有数据了,并且是我们输入的字符串;

    • 在上一步中,我们执行到返回,那就意味着接下来登场的将会是RETN了,按下 F8,返回到调用 API 的程序,多么令人兴奋的内容,这么简单吗,会不会有诈?

      第一部分找到了

    • 那就分析一下,看看到底是啥:

      第一部分结束

      看来第一部分很简单,逻辑简单粗暴,只要不对直接弹出错误;
      第一部分到此结束。
      既然第一部分这么简单,那难点肯定在第二部分,去看看!

第二部分
  • 倒入OD开始分析:

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

    • 接着按下Ctrl + N查看使用了哪些API,还是那个可疑的函数:

      第二部分

      很好的开始,在GetWindowTextA函数上设置断点,然后去断点窗口双击断点进入反汇编窗口给断点设置备注,这是一个好的习惯,请保持;

    • 然后F9运行程序,“随便输入” 一些字符后点击验证,可以随便输入,但请记住它;

    • 程序断在了设置断点的 API 的行首,在堆栈窗口可以看到 API 的各种参数,而参数 Buffer 里存放着函数返回的数据,选中 Buffer 右键数据窗口中跟随,可以看到,由于断点中断在了函数的第一行,所以这个地址里什么也没有:

      Buffer

    • 接着按下 Ctrl + F9 或点击菜单栏的调试选项并选择执行到返回,然后发现 Buffer 对应的地址里有数据了,并且是我们输入的字符串;

    • 在上一步中,我们执行到返回,那就意味着接下来登场的将会是RETN了,按下 F8,返回到调用 API 的程序,哦豁,好像没啥有用的内容,等等,下面还有个GetWindowTextA,对对,我们输入了用户名和密码,那就得获取两次,而第一次获取的是密码,那就F9再运行程序;

    • 是的,又获取了一次,而这次获取的是用户名,执行到用户代码,果然是下面这个GetWindowTextA,那下面就是比较的代码了呗,毕竟它位于获取和弹窗之间;

    • 那就分析一下它的流程:

      开始

      • 第一行TEST EAX, EAX不迷茫,TEST EAX, EAX表示检查EAX自身是否为 0,而第二行的JE SHORT 硬编码寻.0040167E指向了一个MessageBoxA,那就说明EAX很重要,既然弹窗提示的是必须输入用户名才可以,那它不是用户名的长度是什么,况且EAX里的数据刚好和用户名长度相同;

      • 接下来,它对用户名做了一系列的运算,并把运算的结果放到了指定的内存中,暂时还不知道它想干嘛:

        用户名

      • 同样的,又对输入的假序列号进行了一系列运算,并把运算结果放到了指定的内存中,不过相对于用户名,序列号的处理比较简单,只是进行了除法运算,并存储了余数:

        假码

      • 直到序列号运算完毕,一个JMP跳下来,分析之后,终于知道了它要干什么:

        比较

        原来,它上面进行的一系列运算,是为这一步的比较做准备;

      • 至此,终于分析出了结果:这里没有硬编码,而是软编码,序列号是根据用户名计算出来的

      • 至于计算的规则:

        • 用户名的运算规则:用户名的下标从零开始,每个字节依次和A进行除法运算,然后用余数和下标异或之后的值加 2;
        • 序列号的运算规则:序列号的每个字节和A进行除法运算,然后用余数和用户名运算的结果进行比较;
        • 序列号的算法:任意数字和A进行乘法运算,并加上用户名每个字符运算的结果就是这个用户名对应的序列号,运算的结果必须可以转为ASCII码中的数字;
        • 例如:
          • 用户名: yang

          • 运算的结果:03 08 04 02

          • 序列号算法:5 * A + 3 = 35 4 * A + 8 = 30 5 * A + 4 = 36 5 * A + 2 = 34

          • 转成 ASCII:5064

            成功