运行 exe 提示输入 flag,直接使用 GDB 查看文件信息,判断是否加壳。
使用 GDB 加载程序,查看文件信息:
gdb ./moe.exe info file
回显发现 UPX 加壳:
Symbols from "/workspaces/untrammeled0107_writeups/test/moe.exe".
Local exec file:
`/workspaces/untrammeled0107_writeups/test/moe.exe', file type pei-x86-64.
Entry point: 0x14000a260
0x0000000140001000 - 0x0000000140009000 is UPX0
0x0000000140009000 - 0x000000014000a600 is UPX1
0x000000014000b000 - 0x000000014000b600 is .rsrc
退出 GDB,使用 UPX 命令脱壳:
exit upx -d moe.exe -o moe_attack.exe
脱壳成功回显:
untrammeled@laptop-fpsmp3t:/mnt/c/Users/HP/Downloads/moe$ upx -d moe.exe -o moe_attack.exe
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2024
UPX 4.2.2 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 3rd 2024
File size Ratio Format Name
-------------------- ------ ----------- -----------
12288 <- 8192 66.67% win64/pe moe_attack.exe
Unpacked 1 file.
脱壳后丢入 IDA,直接查看 main 函数核心逻辑:
int __fastcall main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rax
__int64 v4; // rcx
__int64 v5; // rax
int v6; // r9d
__int64 v7; // r8
char v8; // dl
_OWORD v10[8]; // [rsp+20h] [rbp-148h]
int v11; // [rsp+A0h] [rbp-C8h]
int v12; // [rsp+A4h] [rbp-C4h]
int v13; // [rsp+A8h] [rbp-C0h]
char v14[48]; // [rsp+B0h] [rbp-B8h]
char Buffer[112]; // [rsp+E0h] [rbp-88h] BYREF
v10[0] = _mm_load_si128((const __m128i *)&xmmword_1400032E0);
v10[1] = _mm_load_si128((const __m128i *)&xmmword_140003310);
v10[2] = _mm_load_si128((const __m128i *)&xmmword_140003320);
v10[3] = _mm_load_si128((const __m128i *)&xmmword_1400032F0);
v10[4] = _mm_load_si128((const __m128i *)&xmmword_1400032D0);
v10[5] = _mm_load_si128((const __m128i *)&xmmword_1400032B0);
v10[6] = _mm_load_si128((const __m128i *)&xmmword_140003300);
v10[7] = _mm_load_si128((const __m128i *)&xmmword_1400032C0);
v11 = 41;
v12 = 36;
v13 = 86;
sub_140001010("please input your flag: ");
v3 = _acrt_iob_func(0);
fgets(Buffer, 100, v3);
v4 = -1;
do
++v4;
while ( Buffer[v4] );
v5 = 0;
v6 = 0;
if ( (int)v4 > 0 )
{
v7 = 0;
do
{
v8 = Buffer[v7] ^ 0x21;
if ( v6 < (int)v4 - 1 )
v8 ^= Buffer[v7 + 1];
v14[v7] = v8;
++v6;
++v7;
}
while ( v7 < (int)v4 );
}
while ( v14[v5] == *((_DWORD *)v10 + v5) )
{
if ( ++v5 >= 35 )
return 0;
}
sub_140001010("you will never get the flag!!!!\n");
return 0;
}
核心加密逻辑:
if ( (int)v4 > 0 )
{
v7 = 0;
do
{
v8 = Buffer[v7] ^ 0x21;
if ( v6 < (int)v4 - 1 )
v8 ^= Buffer[v7 + 1];
v14[v7] = v8;
++v6;
++v7;
}
while ( v7 < (int)v4 );
}
规则:
第 i 位:out[i] = in[i] ^ 0x21 ^ in[i+1]
最后一位:out[i] = in[i] ^ 0x21
处理后与硬编码数组 v10 比较,完全相等输出 flag。
从 IDA 数据段提取全部 35 字节密文,整理为数组:
enc=[
0x23, 0x2b, 0x27, 0x36, 0x33, 0x3c, 0x03, 0x48,
0x64, 0x0b, 0x1d, 0x76, 0x7b, 0x10, 0x0b, 0x3a,
0x3f, 0x65, 0x76, 0x29, 0x15, 0x37, 0x1c, 0x0a,
0x08, 0x21, 0x3e, 0x3c, 0x3d, 0x16, 0x0b, 0x24,
0x29, 0x24, 0x56, 43
]
先全体异或 0x21,再从后往前逆异或:
enc=[
0x23, 0x2b, 0x27, 0x36, 0x33, 0x3c, 0x03, 0x48,
0x64, 0x0b, 0x1d, 0x76, 0x7b, 0x10, 0x0b, 0x3a,
0x3f, 0x65, 0x76, 0x29, 0x15, 0x37, 0x1c, 0x0a,
0x08, 0x21, 0x3e, 0x3c, 0x3d, 0x16, 0x0b, 0x24,
0x29, 0x24, 0x56, 43
]
xored=[]
for i in range(len(enc)):
xored.append(enc[i]^0x21)
for i in range(len(xored)-2,-1,-1):
xored[i]=xored[i]^xored[i+1]
flag=''.join(chr(i) for i in xored)
print(flag)
moectf{Y0u_c4n_unp4ck_It_vvith_upx}