吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2007|回复: 4
收起左侧

[CTF] [KCTF]rev_babyrev

[复制链接]
geesehoward 发表于 2025-6-11 15:12
1.jpg
根据描述,这是ISG2023的一道题,名字里有baby,难度是中,下载附件,发现是linux程序,拖入IDA,发现有UPX壳,
2.jpg
尝试upx -d,不出意外的发生了意外,解不开。010editer打开看一下,
3.jpg
果然,UPX的特征码[UPX!]被改了,而且文件类型的地方是03,动态库,这里导致IDA调试报错,将03修改为02,ISG!替换为UPX!,保存,在upx -d,不出意外又失败了,这次是bad seek 2,看了下源码,是写文件出错了,完全没思路继续解决,哪位大神如果知道原因,还请指点。

没办法,IDA远程调试,配置过程就不细说了,教程一大把。
4.jpg
跟进entry start函数,下断点
5.jpg
F8步过跟进,出现第一次寄存器跳转
6.jpg
继续F8,后面一大段带有循环的逻辑,建议找到ret,F2下断点,然后F9直接执行到断点,再F2取消断点(这里取消是因为不取消的话,后面还会再进来,要多按几次),之后再F8进入后续的逻辑,直到出现下一次寄存器跳转地址
7.jpg
继续F8,执行了syscall后再次出现寄存器跳转
8.jpg
9.jpg
到这里,upx的解压缩工作已经完成了,后面就要进入libc_start_call_main了,尝试了dump内存,但没成功,还请大神指点一下,该如何dump完成脱壳
10.jpg
下断点,F7进入,出现libc_start_call_main
11.jpg
下断点,F7进入,再次出现寄存器跳转
12.jpg
同样的,下断点后,F7进入
13.jpg
在call处下断点,F7进入
14.jpg
switch处下断点,F9执行后,F8继续,找到寄存器跳转
15.jpg
下断点并F7进入
16.jpg
下断点并F7进入
18.png
这个rdi便是真正的主函数地址,F7进入,按p生成函数,按F5生成伪代码
[C] 纯文本查看 复制代码
void __noreturn sub_7F4573309980()
{
  unsigned __int16 *v0; // rbx
  unsigned __int64 v1; // rbx
  __int64 *v2; // r14
  __int64 v3; // r15
  __m128i v4; // xmm0
  int v5; // ebp
  unsigned int v6; // eax
  __m128i v7; // xmm0
  __m128i v8; // xmm1
  __m128i v9; // xmm3
  __m128i si128; // xmm2
  __m128i v11; // xmm4
  unsigned int v12; // ecx
  __m128i v13; // xmm3
  unsigned __int8 v14; // al
  __m128i v15; // xmm0
  __m128i v16; // xmm1
  __int64 v17[3]; // [rsp+8h] [rbp-690h] BYREF
  __int128 v18[2]; // [rsp+20h] [rbp-678h] BYREF
  __m128i v19; // [rsp+40h] [rbp-658h] BYREF
  __m256 v20; // [rsp+50h] [rbp-648h]
  __int64 v21; // [rsp+70h] [rbp-628h]
  __m128i v22; // [rsp+360h] [rbp-338h] BYREF
  __m128i v23; // [rsp+370h] [rbp-328h] BYREF
  __m128i v24; // [rsp+380h] [rbp-318h] BYREF
  __int64 v25; // [rsp+390h] [rbp-308h]
  int v26; // [rsp+64Ch] [rbp-4Ch]
 
  sub_7EFF194BB200((__int128 *)v19.m128i_i8);
  sub_7EFF194BB470(&v22, (__int64)&v19);
  if ( v22.m128i_i64[0] )
  {
    if ( v22.m128i_i64[1] )
      sub_7EFF19498E50(v22.m128i_i64[0], v22.m128i_i64[1], 1LL);
    sub_7EFF194BB470(v17, (__int64)&v19);
  }
  else
  {
    v17[0] = 0LL;
  }
  if ( *(_QWORD *)&v20.m256_f32[2] != *(_QWORD *)v20.m256_f32 )
  {
    v1 = (*(_QWORD *)&v20.m256_f32[2] - *(_QWORD *)v20.m256_f32) / 0x18uLL;
    v2 = (__int64 *)(*(_QWORD *)v20.m256_f32 + 8LL);
    do
    {
      if ( *v2 )
        sub_7EFF19498E50(*(v2 - 1), *v2, 1LL);
      v2 += 3;
      --v1;
    }
    while ( v1 );
  }
  if ( v19.m128i_i64[1] )
    sub_7EFF19498E50(v19.m128i_i64[0], 24 * v19.m128i_i64[1], 8LL);
  v0 = (unsigned __int16 *)v17[0];
  if ( !v17[0] )                                // 检查是否有参数,且不为空字符串
  {
    v19.m128i_i64[0] = (__int64)&off_7EFF194F35A0;
    v19.m128i_i64[1] = 1LL;
    *(_QWORD *)v20.m256_f32 = "src/main.rsWrong!\nCorrect!\n";
    *(_OWORD *)&v20.m256_f32[2] = 0LL;
    sub_7EFF194BCBD0((__int128 *)v19.m128i_i8);
    ((void (__fastcall *)(_QWORD))unk_7EFF194BF530)(0LL);
    BUG();
  }
  v3 = v17[2];                                  // v17应该是std::string,v3就是v17.length();
  sub_7EFF19499660((__int64)&v22);
  if ( v22.m128i_i32[0] == 6 )
  {
    v4 = _mm_loadu_si128((const __m128i *)&v22.m128i_i8[8]);
    v18[1] = (__int128)_mm_loadu_si128((const __m128i *)&v23.m128i_i8[8]);
    v18[0] = (__int128)v4;
    ((void (__fastcall *)(__m128i *, __int128 *))sub_7EFF194996A0)(&v19, v18);
    ((void (__fastcall *)(__m128i *, __m128i *))sub_7EFF194988E0)(&v22, &v19);
    v5 = v26;
    sub_7EFF19498700(&v22);
    sub_7EFF194986B0((__int64)v18);
    if ( v5 )
      goto LABEL_27;
    if ( v3 != 0x20 )
      goto LABEL_27;
    v6 = *v0;
    v7 = _mm_cvtsi32_si128(v6);                 // int转int128,4字节扩展到16字节
    v8 = _mm_srli_epi16(v7, 8u);                // 128位(16字节)数据,没2字节内右移8位,相当于
                                                // unsigned int a[8];
                                                // a[0] << 8;
                                                // a[1] << 8;
                                                // ……
                                                // a[7] << 8;
    v9 = _mm_loadu_si128((const __m128i *)(v0 + 1));// 读取flag16字节
    si128 = _mm_load_si128((const __m128i *)&xmmword_7EFF194E0020);
    if ( _mm_movemask_epi8(
           _mm_cmpeq_epi8(
             _mm_xor_si128(_mm_or_si128(_mm_slli_si128(v9, 1), _mm_andnot_si128(si128, v8)), v9),// memcmp(v9 ^ ((v9 << 8) | (~si128 & v8)), xmmword_7EFF194E0030) == 0
                                                // xmmword_7EFF194E0030 = 0x192A30261B6261204D49152F22133C14
             (__m128i)xmmword_7EFF194E0030)) != 0xFFFF )
      goto LABEL_27;
    v11 = _mm_loadl_epi64((const __m128i *)(v0 + 9));// 从第19字节读到26字节
    if ( _mm_xor_si128(_mm_or_si128(_mm_slli_epi64(v11, 8u), _mm_srli_si128(v9, 15)), v11).m128i_u64[0] == 0x7140901160D0507LL
      && (v12 = *(_DWORD *)(v0 + 13),
          v13 = _mm_cvtsi32_si128(v12),
          _mm_cvtsi128_si32(
            _mm_xor_si128(
              _mm_or_si128(_mm_slli_epi32(v13, 8u), _mm_andnot_si128(si128, _mm_srli_epi64(v11, 0x38u))),
              v13)) == 0x84C1D02)
      && (unsigned __int8)_mm_cvtsi128_si32(_mm_xor_si128(v8, v7)) == 0x1A
      && (_BYTE)v6 == 0x49
      && (v14 = *((_BYTE *)v0 + 30), (v14 ^ HIBYTE(v12)) == 0x78)
      && (*((_BYTE *)v0 + 31) ^ v14) == 0x35 )
    {
      v19.m128i_i64[0] = (__int64)&off_7EFF194F35F0;
      v19.m128i_i64[1] = 1LL;
      *(_QWORD *)v20.m256_f32 = "src/main.rsWrong!\nCorrect!\n";
      *(_OWORD *)&v20.m256_f32[2] = 0LL;
      sub_7EFF194BCBD0((__int128 *)v19.m128i_i8);
    }
    else
    {
LABEL_27:
      v19.m128i_i64[0] = (__int64)&off_7EFF194F35E0;
      v19.m128i_i64[1] = 1LL;
      *(_QWORD *)v20.m256_f32 = "src/main.rsWrong!\nCorrect!\n";
      *(_OWORD *)&v20.m256_f32[2] = 0LL;
      sub_7EFF194BCBD0((__int128 *)v19.m128i_i8);
    }
    ((void (__fastcall *)(_QWORD))unk_7EFF194BF530)(0LL);
  }
  else
  {
    v21 = v25;
    v15 = _mm_loadu_si128(&v22);
    v16 = _mm_loadu_si128(&v23);
    *(__m128i *)&v20.m256_f32[4] = _mm_loadu_si128(&v24);
    *(__m128i *)v20.m256_f32 = v16;
    v19 = v15;
    ((void (__fastcall *)(const char *, __int64, __m128i *, void **, char **))unk_7EFF194980E0)(
      "called `Result::unwrap()` on an `Err` valueUsage : BabyRev <flag>\n",
      43LL,
      &v19,
      &off_7EFF194F3580,
      &off_7EFF194F35B0);
  }
  BUG();
}

主函数里面有一个反debug,比较简单,就是v5的地方,判断了traceid是否为0,调试的时候,直接改ZF值,让跳转不生效即可。

这个函数的主逻辑比较简单,大致如下:

1、检查是否有参数,且不为空字符串。

2、检查是否有traceid

3、检查入参长度是否为32

4、flag[0]=0x49,后面每一字节与前一字节进行异或,比对结果是否为目标值。逻辑可以简化为如下代码
[C] 纯文本查看 复制代码
    unsigned char checkflag[] = {
        0x1A, 0x14, 0x3C, 0x13, 0x22, 0x2F, 0x15, 0x49,
        0x4D, 0x20, 0x61, 0x62, 0x1B, 0x26, 0x30, 0x2A, 
        0x19, 0x07, 0x05, 0x0D, 0x16, 0x01, 0x09, 0x14, 
        0x07, 0x02, 0x1D, 0x4C, 0x08, 0x78, 0x35
    };
    char flag[33] = { 0 };
    flag[0] = 'I';
    unsigned char Targetflag[31] = { 0 };
    for (int i = 1; i < 32; i++)
    {
        Targetflag[i - 1] = flag[i] ^ flag[i - 1];
    }
    if(memcmp(flag, checkflag, 31))
    {
        printf("Wrong!");
    }
    else
    {
        printf("Conrect!");
    }

由此、可以得到逆向计算逻辑如下
[C] 纯文本查看 复制代码
    char flag[33] = { 0 };
    flag[0] = 'I';
    unsigned char checkflag[] = {
        0x1A, 0x14, 0x3C, 0x13, 0x22, 0x2F, 0x15, 0x49,
        0x4D, 0x20, 0x61, 0x62, 0x1B, 0x26, 0x30, 0x2A, 
        0x19, 0x07, 0x05, 0x0D, 0x16, 0x01, 0x09, 0x14, 
        0x07, 0x02, 0x1D, 0x4C, 0x08, 0x78, 0x35
    };
 
    for (int i = 1; i < 32; i++)
    {
        flag[i] = checkflag[i - 1] ^ flag[i - 1];
    }
    printf("%s\n", flag);

得到flag
程序校验
17.png
总结一下,这个程序的逆向逻辑其实很简单,难点在于脱upx壳,不过在IDA下调试也不难。另一个难点是64位程序编译时用了大量的xmm寄存器及指令,不常见,但仔细看一下,并不难理解。

免费评分

参与人数 1威望 +1 吾爱币 +20 热心值 +1 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Sound 发表于 2025-6-13 01:22
本帖最后由 Sound 于 2025-6-13 01:23 编辑

报错badseek2.大概就是修改了某个地方的特征码 ,是魔改的UPX,
一般建议看下最后的四个byte,改成0xf4 0 0 0 试试。

免费评分

参与人数 1吾爱币 +2 热心值 +1 收起 理由
geesehoward + 2 + 1 感谢指点

查看全部评分

Sound 发表于 2025-6-14 02:02
geesehoward 发表于 2025-6-13 09:59
感谢大神指点,果然是这里改了upx -d直接脱壳成功了


打Ctf的话,UPX很常见 ,为了防止被工具一键脱壳,它的改法有很多,我给你列出来我打CTF碰到的UPX的改动问题,

1.文件的类型
2.Upx区段名   
3.Upx的版本号- 一般为未知版本
4.在Upx区段后插入干扰代码  一般4 byte
5.在Upx区段后插入 跳转指令 一般eb 90 90 90
6.末尾overlay_offset 修改特征码   改0xf4 00 00 00
7.入口点一些代码nop掉

PS:1 2  6 是文章里这个elf的改法,这里推荐使用Detect it easy (Die)进行查壳,它可以帮助你获取到可能修改的地方。

免费评分

参与人数 2吾爱币 +3 热心值 +2 收起 理由
zaoshang123 + 1 + 1 热心回复!
geesehoward + 2 + 1 感谢指点

查看全部评分

 楼主| geesehoward 发表于 2025-6-13 09:59
Sound 发表于 2025-6-13 01:22
报错badseek2.大概就是修改了某个地方的特征码 ,是魔改的UPX,
一般建议看下最后的四个byte,改成0xf4 0  ...

感谢大神指点,果然是这里改了upx -d直接脱壳成功了

点评

打Ctf的话,UPX很常见 ,为了防止被工具一键脱壳,它的改法有很多,我给你列出来我打CTF的碰到的UPX的改动问题, 1.文件的类型 2.Upx区段名 3.Upx的版本号- 一般为未知版本 4.在Upx区段后插入干扰代码  详情 回复 发表于 2025-6-14 02:02
 楼主| geesehoward 发表于 2025-6-14 15:30
Sound 发表于 2025-6-14 02:02
打Ctf的话,UPX很常见 ,为了防止被工具一键脱壳,它的改法有很多,我给你列出来我打CTF碰到的UPX的改 ...

感谢指点
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-3 02:10

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表