
1.jpg (20.9 KB, 下载次数: 0)
下载附件
2025-6-11 15:03 上传
根据描述,这是ISG2023的一道题,名字里有baby,难度是中,下载附件,发现是linux程序,拖入IDA,发现有UPX壳,

2.jpg (33.85 KB, 下载次数: 0)
下载附件
2025-6-11 15:03 上传
尝试upx -d,不出意外的发生了意外,解不开。010editer打开看一下,

3.jpg (187.65 KB, 下载次数: 0)
下载附件
2025-6-11 15:03 上传
果然,UPX的特征码[UPX!]被改了,而且文件类型的地方是03,动态库,这里导致IDA调试报错,将03修改为02,ISG!替换为UPX!,保存,在upx -d,不出意外又失败了,这次是bad seek 2,看了下源码,是写文件出错了,完全没思路继续解决,哪位大神如果知道原因,还请指点。
没办法,IDA远程调试,配置过程就不细说了,教程一大把。

4.jpg (142.57 KB, 下载次数: 0)
下载附件
2025-6-11 15:03 上传
跟进entry start函数,下断点

5.jpg (99.87 KB, 下载次数: 0)
下载附件
2025-6-11 15:03 上传
F8步过跟进,出现第一次寄存器跳转

6.jpg (44.88 KB, 下载次数: 0)
下载附件
2025-6-11 15:03 上传
继续F8,后面一大段带有循环的逻辑,建议找到ret,F2下断点,然后F9直接执行到断点,再F2取消断点(这里取消是因为不取消的话,后面还会再进来,要多按几次),之后再F8进入后续的逻辑,直到出现下一次寄存器跳转地址

7.jpg (61.16 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
继续F8,执行了syscall后再次出现寄存器跳转

8.jpg (20.7 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传

9.jpg (76.18 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
到这里,upx的解压缩工作已经完成了,后面就要进入libc_start_call_main了,尝试了dump内存,但没成功,还请大神指点一下,该如何dump完成脱壳。

10.jpg (52 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
下断点,F7进入,出现libc_start_call_main

11.jpg (113.82 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
下断点,F7进入,再次出现寄存器跳转

12.jpg (126.68 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
同样的,下断点后,F7进入

13.jpg (47.39 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
在call处下断点,F7进入

14.jpg (143.7 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
switch处下断点,F9执行后,F8继续,找到寄存器跳转

15.jpg (118.32 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
下断点并F7进入

16.jpg (31.72 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
下断点并F7进入

18.png (19.97 KB, 下载次数: 0)
下载附件
2025-6-11 15:09 上传
这个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] \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
由此、可以得到逆向计算逻辑如下
[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
得到flag
程序校验

17.png (27.14 KB, 下载次数: 0)
下载附件
2025-6-11 15:04 上传
总结一下,这个程序的逆向逻辑其实很简单,难点在于脱upx壳,不过在IDA下调试也不难。另一个难点是64位程序编译时用了大量的xmm寄存器及指令,不常见,但仔细看一下,并不难理解。