【ZCTF】easy reverse 详解

本文介绍了一种针对特定DLL文件的逆向分析方法。通过对DLL进行静态与动态分析,揭示了其加密算法的工作原理,并提出了有效的暴力破解策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0x01  前言
    团队逆向牛的解题思路,分享出来~



0x02  内容

0. 样本
bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll

 

1. 静态分析
使用IDAPro逆向分析样本,样本较小,得到方法列表:

 

调用关系:

 

PS: 开始受调用关系误导,分析DLL入口函数调用的几个方法,浪费了时间。

 

查看内部字符串:

 

根据yes!定位到方法 get_string

 

get_string: 调用关系图,有解密过程调用。

 

使用IDAPro F5功能,生成伪码:

 

大致过程:
1) 获取unk_6E282000处存储的0x19长字节流
2) 用户输入字符串
3) 使用encrypt_str加密用户输入字符串
4) 加密结果与1)处0x19字节相同,则输入的串为可能的Flag

 

encrypt_str伪码:
大致过程:
1) v4基本上为固定值0,原因自己看
2) 输入串长度大于2, 并且只有偶数个字节参与运算, v7为终止哨兵,其值为 a1+2*result,  a1为串首地址,result为串长度的1/2取整。
3) v6从首地址开始,每循环一次,跳2个字节。

 

encrypt伪码:
        大致过程:
1) 伪码中参数列表,重点看a3参数,从encrypt_str调用处可看出,a3为指向用户输入串中的某个位置,调用一次,前进2
2) 重点看:
此处取了 *a3 *(a3 + 1)的值

 

此处回写了 *a3 *(a3 + 1)的值
这块是暴力破解的关键。

 

        PS 可以看出,加密过程是通过一系列复杂的运算来实现,一般算法很难从结果逆推出原始串,先从暴力破解入手。

 

2. 动态分析
样本为DLL,导出方法get_string,需要编写Load程序。

 

为简化调试过程,Load程序模拟get_string流程,不需scanf控制台输入,主要代码如下:
typedef int(*encrypt_str)(uint8_t*, uint32_t, int*);
auto h = LoadLibrary("bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll");
encrypt_str f = (encrypt_str)((uint8_t*)h + 0x67C + 0xC00);
        int v1 = 0xEFBEADDE;
        char testStr[] = "0123456789abcdefgh";
        f((uint8_t *)testStr, strlen(testStr)+1, &v1);

 

 

注意:v1的值问题,IDA翻译的伪码未识别出v1的类型,从汇编可以看出,v14字节Int0xEFBEADDE对应伪码中 -34.-83,-66.-17

 

OD调试分析步骤:
1) 使用OD启动编译完的程序
2) bp encrypt_str 下断点
OD断在

 

查看寄存器信息:

 

可知 0073FDD0处存储待加密的字符串,OD数据跟随:

 

单步步过一次encrypt调用后,数据区:

 

前两个字节数据变化,基本肯定,加密方法每次处理2字节数据,且加密后数据长度不发生变化。

 

PS上述过程原理上只能得出每次只生成两字节加密数据,不能证明加密过程也只有2字节参与,证明方法:可以在数据区按字节下内存读断点,看在encrypt过程中,是否只命中两个字节的读断点。


3. 破解方法
1) 取到dll中加密后的串,即get_stringunk_6E2820000x19 Bytes数据。有效数据长度24
2) 每两字节一组,穷举0x0-0xFF,共256*256种可能,调用encrypt,结果与unk_6E282000对应位置数据比较,一致时找到2Bytes
3) 重复过程2) 12次。
4) 由于算法中,每两字节一组独立运行,暴力枚举量从: 256^24 缩小到 256^2*12 = 786432,运算时间10秒以内。

 

核心代码如下:
uint8_t str[25] = { 0 };[/align]        for (int n = 0; n < 25; n+=2)
      {
              for (int i = 0; i <= 0xFF; i++)
                      for (int j = 0; j <= 0xFF; j++)
                      {
                              uint8_t *data = new uint8_t[25];
                              memcpy(data, str, 25);
                              *(data + n) = (uint8_t)i;
                              *(data + n + 1) = (uint8_t)j;
                              f(data, 25, &v1);
                              if (data[n] == pstr[n] && data[n+1] == pstr[n+1]) {
                                      str[n] = i;
                                      str[n + 1] = j;
                                      break;
                              }
                      }
      }
      printf("%s\n", str);
      f(str, 25, &v1);
      if (memcmp(str, pstr, 25) == 0)
      {
              printf("It's OK!\n");
      }

 

 

  结果:
0x01  前言
    团队逆向牛的解题思路,分享出来~



0x02  内容

0. 样本
bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll

1. 静态分析
使用IDAPro逆向分析样本,样本较小,得到方法列表:

调用关系:

PS: 开始受调用关系误导,分析DLL入口函数调用的几个方法,浪费了时间。

查看内部字符串:

根据yes!定位到方法 get_string

get_string: 调用关系图,有解密过程调用。

使用IDAPro F5功能,生成伪码:

大致过程:
1) 获取unk_6E282000处存储的0x19长字节流
2) 用户输入字符串
3) 使用encrypt_str加密用户输入字符串
4) 加密结果与1)处0x19字节相同,则输入的串为可能的Flag

encrypt_str伪码:
大致过程:
1) v4基本上为固定值0,原因自己看
2) 输入串长度大于2, 并且只有偶数个字节参与运算, v7为终止哨兵,其值为 a1+2*result,  a1为串首地址,result为串长度的1/2取整。
3) v6从首地址开始,每循环一次,跳2个字节。

encrypt伪码:
        大致过程:
1) 伪码中参数列表,重点看a3参数,从encrypt_str调用处可看出,a3为指向用户输入串中的某个位置,调用一次,前进2
2) 重点看:
此处取了 *a3 *(a3 + 1)的值

此处回写了 *a3 *(a3 + 1)的值
这块是暴力破解的关键。

        PS 可以看出,加密过程是通过一系列复杂的运算来实现,一般算法很难从结果逆推出原始串,先从暴力破解入手。

2. 动态分析
样本为DLL,导出方法get_string,需要编写Load程序。

为简化调试过程,Load程序模拟get_string流程,不需scanf控制台输入,主要代码如下:
typedef int(*encrypt_str)(uint8_t*, uint32_t, int*);
auto h = LoadLibrary("bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll");
encrypt_str f = (encrypt_str)((uint8_t*)h + 0x67C + 0xC00);
        int v1 = 0xEFBEADDE;
        char testStr[] = "0123456789abcdefgh";
        f((uint8_t *)testStr, strlen(testStr)+1, &v1);

 


注意:v1的值问题,IDA翻译的伪码未识别出v1的类型,从汇编可以看出,v14字节Int0xEFBEADDE对应伪码中 -34.-83,-66.-17

OD调试分析步骤:
1) 使用OD启动编译完的程序
2) bp encrypt_str 下断点
OD断在

查看寄存器信息:

可知 0073FDD0处存储待加密的字符串,OD数据跟随:

单步步过一次encrypt调用后,数据区:

前两个字节数据变化,基本肯定,加密方法每次处理2字节数据,且加密后数据长度不发生变化。

PS上述过程原理上只能得出每次只生成两字节加密数据,不能证明加密过程也只有2字节参与,证明方法:可以在数据区按字节下内存读断点,看在encrypt过程中,是否只命中两个字节的读断点。


3. 破解方法
1) 取到dll中加密后的串,即get_stringunk_6E2820000x19 Bytes数据。有效数据长度24
2) 每两字节一组,穷举0x0-0xFF,共256*256种可能,调用encrypt,结果与unk_6E282000对应位置数据比较,一致时找到2Bytes
3) 重复过程2) 12次。
4) 由于算法中,每两字节一组独立运行,暴力枚举量从: 256^24 缩小到 256^2*12 = 786432,运算时间10秒以内。

核心代码如下:
uint8_t str[25] = { 0 };[/align]        for (int n = 0; n < 25; n+=2)
      {
              for (int i = 0; i <= 0xFF; i++)
                      for (int j = 0; j <= 0xFF; j++)
                      {
                              uint8_t *data = new uint8_t[25];
                              memcpy(data, str, 25);
                              *(data + n) = (uint8_t)i;
                              *(data + n + 1) = (uint8_t)j;
                              f(data, 25, &v1);
                              if (data[n] == pstr[n] && data[n+1] == pstr[n+1]) {
                                      str[n] = i;
                                      str[n + 1] = j;
                                      break;
                              }
                      }
      }
      printf("%s\n", str);
      f(str, 25, &v1);
      if (memcmp(str, pstr, 25) == 0)
      {
              printf("It's OK!\n");
      }

 


结果:
<think>好的,我现在需要回答用户关于在Ubuntu 24.04上检查GCC版本的问题。首先,我应该回忆一下常用的Linux命令。用户提到的是gcc version,所以直接使用gcc --version应该就能显示版本信息。这是最直接的方法,可能用户只需要这个命令。 不过,可能用户对系统其他信息也感兴趣,比如是否安装了多个版本的GCC或者如何切换版本。但根据问题,用户明确询问的是当前系统的GCC版本,所以先给出最简洁的答案比较好。此外,Ubuntu 24.04默认的GCC版本可能较新,比如GCC 13或更高,但具体需要实际查看。 另外,可能需要提到其他相关命令,比如使用dpkg来查询安装的软件包版本,或者查看/usr/bin/gcc的符号链接。但为了不过于复杂,先提供主要的方法,再在相关问题中扩展。 用户提供的引用中有关于Ubuntu安装CUDA和配置的内容,虽然与GCC版本无直接关联,但可能用户的环境涉及开发工具链,因此可以建议检查开发工具是否完整安装,比如build-essential包。不过这可能超出当前问题范围,但作为相关问题可以提到。 最后,生成的相关问题需要围绕GCC版本管理、安装其他版本、切换版本以及编译依赖等。确保问题有针对性,帮助用户进一步探索。</think>### 检查Ubuntu 24.04的GCC版本 #### 方法1:直接查看版本 在终端执行以下命令,可直接显示当前默认GCC版本: ```bash gcc --version ``` 输出示例: ``` gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0 Copyright (C) 2023 Free Software Foundation, Inc. ``` 此命令会显示GCC的主版本号(如13.2.0)和编译时间戳[^1]。 --- #### 方法2:查询软件包状态 通过`dpkg`工具查看已安装的GCC软件包详细信息: ```bash dpkg -l gcc ``` 输出示例: ``` ||/ 名称 版本 架构 描述 +++-==============-============-======-================================= ii gcc 4:13.2.0-1 amd64 GNU C编译器 ``` --- #### 方法3:检查符号链接 查看`/usr/bin/gcc`指向的实际二进制文件: ```bash ls -l /usr/bin/gcc ``` 输出示例: ``` lrwxrwxrwx 1 root root 5 Mar 30 14:22 /usr/bin/gcc -> gcc-13 ``` 此结果显示默认GCC指向`gcc-13`,即版本号为13[^2]。 --- #### 系统开发环境建议 如果输出提示`command not found`,说明未安装GCC。可通过以下命令安装: ```bash sudo apt update && sudo apt install build-essential ``` 该命令会安装GCC、G++、make等基础开发工具链[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值