逆向学习crackme160题-002-abexcm5的write up

简介: 逆向学习crackme160题-002-abexcm5的write up

声明

本笔记都是自己在2022年学逆向的时候的练习笔记,其中所有代码和图片都是自己操作所截图,之前一直保存在本地,终于博客网站搭建好了所以就搬上来了,希望对学习的人有所帮助。

002-abexcm5 的 write up

1. 执行程序测试

可以看到这是一个输入注册码然后进行检测的问题,可见我们并不知道正确的注册码是合适是什么呢?但是根据它的提示信息要敏感,可以知道后面可以用来查找字符串。

1. 程序查壳

可见程序Not packed,这是没有壳的,所以可以直接进行调试。(TASM是Borland公司推出的汇编编译器。MASM宏汇编程序。FASM是一个x86体系处理器下的汇编语言编译器。)

2. 静态调试

  1. 首先将程序拖入32位IDA后先点击View(查看)——>Open subviews——>Strings,结果如下图:

解释:在这些关键字符串中既可以看到刚才的提示信息,还可以看到两个硬编码,可以猜测这很可能和注册码有关系。

  1. 查看输入表,点击View——>Open subviews——>Imports,结果如下:

解释:函数wsprintf [1] ()将一系列的字符和数值输入到缓冲区。

GetVolumeInformationA获取磁盘卷标
GetDlgItemText是C++中的函数,调用这个函数以获得与对话框中的控件相关的标题或文本。
GetModuleHandle功能是获取一个应用程序或动态链接库的模块句柄。
lstrcmpiA 对比字符串
lstrcatA 拼接字符串


  1. 跟踪关键字符串,地址sub_401056,可以看到关键的代码:如下图:


解释:如注释,这个程序的主要注册码的思路就是让用户输入和拼接后的字符出串对比,一致则跳转输出“Well Done!”的窗口,否则就是“Error!”弹窗。

注意:根据上面观察到的的输入表可以知道,程序大多数用到的都是WInAPI,所以它的函数调用约定应该是_stdcall的形式,复习函数调用约定我们知道:__cdecl:    C/C++默认方式,参数从右向左入栈,主调函数负责栈平衡。

__stdcall:          windows API默认方式,参数从右向左入栈,被调函数负责栈平衡。
也因此在回顾上面的拼接函数的时候,前面压入的参数是从后面(也就是右边)往前压的(也即是左边),函数最后得到的拼接结果也是将右边的拼接到左边的字符串后面。

3. 动态调试

  1. 寻找main函数

还记得运行时弹出的提示框也即是提示字符串,所以我们可以用OD查找参考文本字符串,然后直接双击进入,如下图:

前面静态调试已经看过注释了,所以上图的原理也是一样的。所以这里我们重点关注的是他的动态执行过程。


我们现在在0040106c加入了一个断点F2,然后直接执行程序F9,直到弹出提示框让我们输入注册码,我们假设输入“123456”,


然后点击Check检查,单步F8执行完00401078,也即执行过我们的GetDlgItemTextA获取我们输入的内容,这时候观察堆栈可以看到我们输入的内容就被压入进来了,如下图:


此时再单步执行完获取到卷标名,可见卷标名存到了地址为0040225c的地方,由于我的程序放在了C盘,默认是空的卷标名(其他盘或者卷标改名了就会或得卷标名字符串),如下图:


然后004010A8的函数就是拼接字符串到卷标名后面了,接下来就是给0040225C的地址储存的字符串前四个元素加1,并且循环了两次,也就是总共加了2,得到如下图:!

image-20220925153550833

004010D9最后再进行拼接,最后对比。。。

4. 暴力破解

验证逻辑是:生成序列号,通过与用户输入的比对来进行验证

暴力破解的思路是:修改跳转条件,修改标志寄存器

5. 程序复现

  1. 注册码的检验思路是:
    将一串硬编码拼接到获取到的卷标字符串后面储存为str1,然后通过循环将str1的前4个字节总共自加2,得到str2,最后再将str2拼接到另一串硬编码后面,就得到注册码。
#include <stdio.h>
#include <windows.h>
#include <string.h>
#define Max 100

int main()
{
  char str[Max] = "4562-ABEX";//初始化关键字符串
  char Str[Max] = "L2C-5781";
  char VolumeNameBuffer[Max] = { 0 };//初始化参数
  DWORD VolumeSerialNumber = 0;
  DWORD MaximumComponentLength = 0;
  DWORD FileSystemFlags = 0;

  GetVolumeInformationA(
    0,
    VolumeNameBuffer,
    0x32u,
    &VolumeSerialNumber,
    &MaximumComponentLength,
    &FileSystemFlags,
    0,
    0);
  strcat(VolumeNameBuffer, str);
    for (int i = 0; i < 4; i++)
  {
    VolumeNameBuffer[i] += 2;
  }

  strcat(Str, VolumeNameBuffer);
  puts(Str);
  system("pause");
  return 0;
}


相关文章
|
2月前
|
存储
逆向学习crackme160题-015-Brad Soblesky.1 的 write up
逆向学习crackme160题-015-Brad Soblesky.1 的 write up
41 1
|
2月前
|
算法 数据安全/隐私保护
逆向学习crackme160题-011-wocy.1 的 write up
逆向学习crackme160题-011-wocy.1 的 write up
19 1
|
2月前
|
算法 数据安全/隐私保护
逆向学习crackme160题-008-Afkayas.1 的 write up
逆向学习crackme160题-008-Afkayas.1 的 write up
25 1
|
2月前
|
存储 API
逆向学习crackme160题-003-Cruehead-CrackMe-3的 write up
逆向学习crackme160题-003-Cruehead-CrackMe-3的 write up
25 1
|
2月前
|
算法 API 数据安全/隐私保护
逆向学习crackme160题-007-reg 的 write up
逆向学习crackme160题-007-reg 的 write up
45 2
|
2月前
|
算法
逆向学习crackme160题-012-ACG-crcme1 的 write up
逆向学习crackme160题-012-ACG-crcme1 的 write up
20 0
|
2月前
逆向学习crackme160题-005-Andrnalin.1 的 write up
逆向学习crackme160题-005-Andrnalin.1 的 write up
14 0
|
2月前
|
数据安全/隐私保护
逆向学习crackme160题-010-ceycey 的 write up
逆向学习crackme160题-010-ceycey 的 write up
17 0
|
2月前
|
算法 API
逆向学习crackme160题-014-Splish 的 write up
逆向学习crackme160题-014-Splish 的 write up
39 0
|
2月前
|
算法 数据安全/隐私保护
逆向学习crackme160题-009-Boonz-KeygenMe#1 的 write up
逆向学习crackme160题-009-Boonz-KeygenMe#1 的 write up
17 0