GFSJ0815-【Windows_Reverse1】
UPX+地址偏移间接操作
脱壳直接给一个假的flag
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[1024]; // [esp+4h] [ebp-804h] BYREF
_BYTE v5[1024]; // [esp+404h] [ebp-404h] BYREF
memset(v5, 0, sizeof(v5));
memset(v4, 0, sizeof(v4));
printf("please input code:");
scanf("%s", v5);
sub_401000(v5);
if ( !strcmp(v4, "DDCTF{reverseME}") )
printf("You've got it!!%s\n", v4);
else
printf("Try again later.\n");
return 0;
}
看看函数 发现是一个逐字节查表替换 那我们就应该找一下这个所替换的表在哪里
unsigned int __cdecl sub_401000(const char *a1)
{
_BYTE *v1; // ecx
unsigned int v2; // edi
unsigned int result; // eax
int v4; // ebx
v2 = 0;
result = strlen(a1);
if ( result )
{
v4 = a1 - v1;
do
{
*v1 = byte_402FF8[(char)v1[v4]];
++v2;
++v1;
result = strlen(a1);
}
while ( v2 < result );
}
return result;
}
- 看原理 定位表
程序使用了一个带偏移的可打印 ASCII 反转表 在sub_401000 中 关键汇编是
00401020 movsx eax, byte ptr [ebx+ecx]
00401024 mov dl, byte ptr [eax+402FF8h]
0040102A mov eax, ebp
0040102C mov byte ptr [ecx], dl
这还是容易看出来的 下面这个真不容易一眼看出来
.text:00401020 movsx eax, byte ptr [ebx+ecx]
.text:00401024 mov dl, ds:byte_402FF8[eax]
.text:0040102A mov eax, ebp
.text:0040102C mov [ecx], dl
但都是可以简化为:
output[i] = *(char *)(0x402FF8 + input[i]);
所以我们就知道输入字符被直接当作索引使用 同时我们还是不知道表在哪里 那我们输入的字符是不是都是可打印字符 那范围为
0x20 ~ 0x7E
所以真正会被访问的内存范围是
最小地址:0x402FF8 + 0x20 = 0x403018
最大地址:0x402FF8 + 0x7E = 0x403076
因此,真正的数据表位于
0x403018 ~ 0x403076
长度为
0x7E - 0x20 + 1 = 0x5F = 95
个字节
那我们定位还是定位到
.data:00403018 aZyxwvutsrqponm db '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>'
.data:00403059 db '=<;:9876543210/.-,+*)(',27h,'&%$#"! ',0
这个就是表
- 邪修方法
由于byte_402FF8点击进去发现是空的 调试也调试不行 索性下面就有一个看着像表的 拿过来一看 还真对了 哈哈哈哈
exp
table = b"~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"! "
target = b"DDCTF{reverseME}"
code = bytes(
table.index(char) + 0x20
for char in target
)
print('flag{',end='')
print(code.decode(),end = '')
print('}')
flag
flag{ZZ[JX#,9(9,+9QY!}
评论