xiaoyu2032 发表于 2022-5-7 12:45

练习笔记之160Crackme-011

本帖最后由 xiaoyu2032 于 2022-5-7 12:48 编辑

# 160CM-011

## 1.破解过程

  先看看题,打开运行,界面如下图所示,要求输入正确的序列号,然后状态会成未注册变成已注册。序列号不正确时没有提示。

  先拖到PE看看,无壳,VB,那VB Decompiler先来一波。

  从左侧的函数列表可以看出,有一堆是事件响应函数,分别对应界面上的按键,另外还有4个定时处理函数。简单的浏览一下可以发现,注册算法应该在定时处理函数里面,按键处理函数只负责对按键进行响应和显示。
  这道题爆破太简单了,就不说了,主要分析一下算法。4个定时处理函数里面的内容基本相同,每个里面都是好几个重复的验证算法,取其中一个进行分析。

``` VB
For var_24 = 1 To Len(var_44) Step 1
loc_0040492F:
If var_F8 = 0 Then GoTo loc_00404A60
var_50 = CStr(Left(var_44, 1))      '取字符串的第一个字符
var_304 = Asc(Mid$(CStr(var_44), CLng(var_24), 1))    '依次取字符串中的第n个字符,并转换成ASCII码值
var_8C = Hex$((var_30C + var_CC))   '上面的ASCII码值+var_CC后转成16进制字符串
var_34 = var_34 & Hex$((var_30C + var_CC))      '16进制字符串加在字符串的后面
Next var_24
GoTo loc_0040492F
```

  上面代码理解起来不难,需要注意的是var_304和var_30C其实是同一个变量(一个是变量体的指针地址,一个是变量体中存储数据的指针地址),然后有个var_CC不知道是什么东西,VB Decompiler去掉“程序分析器和优化器”重新编译也查不出这个变量的来源,只好到OD里面跟跟看了。

  OD加载程序,然后运行,输入“12345*#”后,再找到函数入口地址404650,然后往下找到第一个段算法中for循环的起始位置和结束位置,在起始位置前一行和结束位置后一行设下断点。第一个断点断下后,F8单步跟踪一下,可以发现算法在取完第一个字符后,进行了一些浮点运算,然后就得到一个数字和ASCII码值相加了。

  浮点运算的汇编指令看不董,只好百度一下,备注好,基本可以看出是将取出的字符串转换成浮点数后与ASCII码值相加,也就是说var_CC就是取出字符串的浮点数值。
  跟完一遍for循环后再点运行,然后断在第二个断点(404A60)位置,我们查看一下ebp-0x34对应的地址(12FB44)的数据,根据变量体数据类型的结构,08表示字符串,“0015F2D4”为字符串的地址。

  查看一下“0015F2D4”地址的字符串内容,字符串为“032333435362B24”,除第一个0外,其余和字符串“12345*#”的ASCII码加“1”均吻合。

  再回去F8跟踪一下前面的代码,发现在4048CF位置对整型变量体var_34赋值为0,然后在404A01字符串连接时,var_34变成了字符串变量体,数值0自动转成成字符串“0”,放在了最前面。VB Decompiler对这部份的识别还是有些问题的。

  至此,核心的算法过程已经弄清楚了。可是程序里面为什么要整4个Timer?每个Timer里面还反复的进行计算,然后和一串字符进行比较。仔细看了看算法,发现不同的算法中中,唯一的区别就是“CStr(Left(var_44, 1))”这一行了,有的是取1个字符,有的是取2个、3个……
  但是除了这个区别,还是反反复复在重复啊,嗯,好奇怪。
  我们在看看算完以后要比较的字符串,“0817E747D7AFF7C7F82836D74RR7A7F7E7B7C7D826D81KE7B7C”。呃……不是16进制字符串吗?怎么字母“R”、“K”也有,那怎么可能相等呢?
  其实这些都是程序作者(机智的作者)的障眼法,看起来一大堆的字符串长的都一样,其实仔细比较一下可以发现有些串串还是不一样的。由于前面算法过程分析已经很明确了,计算后的字符串是16进制字符串,因此我们需要找一串不包含16进制字符以外的串串。最终只有Timer1中有一个串串符合要求。

``` VB
For var_24 = 1 To Len(var_44) Step 1
loc_004064A8:
If var_24 = 0 Then GoTo loc_004065D9
var_50 = CStr(Left(var_44, 2))
var_3A0 = Asc(Mid$(CStr(var_44), CLng(var_24), 1))
var_8C = Hex$((var_3A8 + var_CC))
var_34 = 0 & Hex$((var_3A8 + var_CC))
Next var_24
GoTo loc_004064A8
loc_004065D9:
If (var_34 = "0817E747D7A7D7C7F82836D74747A7F7E7B7C7D826D817E7B7C") = 0 Then GoTo loc_0040664F
Set var_54 = Me
Label3.Caption = "REGISTRIERT"
var_eax = %fobj
loc_0040664F:
```

  从这个代码中可以发现,字符的ASCII码需要加的是字符串前两位。由于ASCII码是个两位16进制数,两位10进制数换成16进制数也最多只有两位,而两个两位16进制数相加,其和即时是三位16进制数,其最高位也不可能超过1。因此这一长串字符串,去掉第一个“0”后,应该是每两个字符表示一个序列号数字。假设序列号第一个数字为a1,第二个数字为a2,则可以列出两个等式:
129(81的10进制表述)=a1的ASSII码+a1*10+a2;
126(7E的10进制表述)=a2的ASSII码+a1*10+a2;
  从上面的计算式可以看出,数字a1的ASCII码值比数字a2的ASCII码值大3,也就是说数字a1比数字a2大3,因此数字a1最小也应该是3,此时数字a2应该为0。3个ASCCII码为51(10进制),0的ASCII码为48(10进制),代入第一行,a1的ASSII码+a1*10+a2=51+30+0=81,与129相比还小48。假设正确的a1需要比3大n,则n+10*n+n=48,求解得到n=4。因此a1=3+4=7,a2=0+4=4。
  这样我们就可以将长串反向解析出来,公式为
字符串中取出的两个字符转换成10进制数-78,即为输入数字的ASCII码值。
  编写注册码计算程序如下:

``` Cpp
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>

int _tmain(int argc, _TCHAR* argv[])
{
    char key = "817E747D7A7D7C7F82836D74747A7F7E7B7C7D826D817E7B7C";
    char code = "";
    char a="",b="";
    int i,j,m,n;
    for (i=0;i<(strlen(key)/2);i++)
    {
      sprintf_s(a,"%c",key);
      sprintf_s(b,"%c",key);
      //printf("a=%s \n",a);
      //printf("b=%s \n",b);
      m=strtol(a,NULL,16);
      n=strtol(b,NULL,16);
      j=m*16+n-74;
      //printf("j=:%d \n",j);
      code=j;
    }
    printf("注册码为:%s \n",code);
    system("pause");
    return 0;
}
```

  输入计算得到的注册码,成功结果如下:

## 2.总结

- 再一次证明,VB Decompiler虽然好用,但也不可全信,有问题的地方还是得看汇编代码。
- 碰到一些浮点运算的指令,都不认识,这里整理一下。

|指令|说明|
|-|-|
|fld|将浮点数据压入协处理器的堆栈中|
|fild|将整型数据压入协处理器的堆栈中|
|fst|将协处理器堆栈栈顶的数据传送到目标操作数中|
|fstp|与FST相类似,所不同的是:指令FST执行完后,不进行堆栈的弹出操作,即:堆栈不发生变化,而指令FSTP执行完后,则需要进行堆栈的弹出操作,堆栈将发生变化|
|fstsw|取协处理器的状态字|
|fadd|浮点数相加|
|fclex|浮点检查错误清除|

- 野生菜鸟选手的悲哀,C语言的字符串函数都要现搜现用,怎么依次取字符串中的两个字符都尝试了各种方法之后才解决,怎么将16进制数字转换成16进制字符串又是各种折腾,动不动就是:==不能将参数 1 从“char”转换为“const char *”==
- VB逆向碰到的一些新函数:

|函数|功能|
|-|-|
|rtcR8ValFromBstr|将var1(字符串)转换成双精度浮点数存入ST0|
|vbaI4Var|将一个var1(变量体)转换为I4(长整数),结果存入eax|
|rtcHexBstrFromVar|将var1(变量体)转换为16进制字符串,结果存入eax|
|vbaVarCat|将var2(变量体)连接到var1(变量体)的后面,结果存入var3(变量体)中 |

tfrist 发表于 2022-5-7 13:29

分析的不错不错 VB的程序

hackerbob 发表于 2022-5-7 13:51

厉害,我只会爆破不会写注册机。。。。。

ehcapa 发表于 2022-5-7 14:23

很详细,学习一下

lgslegend 发表于 2022-5-7 15:28

&#128077;&#128077;

dinds 发表于 2022-5-7 16:27

学习了。。谢谢

snakenba580 发表于 2022-5-7 18:03

谢谢分享,正在学习中。

戰龍在野 发表于 2022-5-7 19:25

谢谢你的坚持我也来好好学习了

JohnWAPJ 发表于 2022-5-8 01:22

好厉害啊

stellarMC 发表于 2022-5-8 11:21

厉害了,学习了,感谢分享,11点21分2022年5月8日!
页: [1] 2
查看完整版本: 练习笔记之160Crackme-011