战队要搞什么周练,用的360平台里自带题库的题(pwn和re难得一匹,虽然大部分是因为我太菜)
pwn的python沙盒打了个非预期解(实在想不明白预期解是怎么搞出来的,也没搜到原题的wp),没事就跑去隔壁re玩玩
脱壳
原程序拖进IDA只有一个start,说明加了壳,需要我们脱壳
用工具分析出来是upx壳,但是用脱壳工具没办法脱,只能手动脱壳了,正好见识下简易的upx手动脱壳到底是个什么流程(这里用的是ESP定律,完整的原理就不太清楚了)
从EntryPoint开始单步,直到ESP发生变化,然后ESP处右键进去它的Dump里,然后在Dump起始处下个硬件断点
![](http://www.asuka39.top/wp-content/uploads/2022/12/ESP0.png)
然后一路单步到一处跳转,可以发现此处有特征机器码55(听某re佬说的,我也不懂),所以从这里开始就是原程序了
![](http://www.asuka39.top/wp-content/uploads/2022/12/ESP1.png)
然后点工具栏里的Scylla插件,转储这段程序,重建PE文件,然后搜索并获取IAT(导入地址表,大概是PE文件用于装载Windows的DLL用的),修复PE文件,最后修复刚刚转储的程序,脱壳就完成了
![](http://www.asuka39.top/wp-content/uploads/2022/12/ESP2.png)
拖进IDA后就能看到反汇编出来的各种函数了
逻辑分析
main()
:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp-4h] [ebp-3Ch]
char v5; // [esp+4h] [ebp-34h] BYREF
char v6[51]; // [esp+5h] [ebp-33h] BYREF
v5 = 0;
memset(v6, 0, 0x30u);
v6[48] = 0;
sub_401000();
puts(aHiSdnisc2019);
sub_40188D(aSerialnumber, v4);
scanf("%s", &v5);
if ( sub_401150(&v5) == 1 )
puts(aCongratulation);
else
puts(aTryAgain);
return sub_40180D(Buffer);
}
sub_401000()
:读取data.bin
--> malloc
出一个大堆块存放文件 --> 文件头部地址传入指针*Buffer
int sub_401000()
{
FILE *v0; // eax
FILE *v1; // esi
void *v2; // edi
int i; // eax
v0 = fopen(FileName, Mode);
v1 = v0;
if ( !v0 )
exit(1);
fseek(v0, 0, 2);
ElementSize = ftell(v1);
rewind(v1);
v2 = malloc(ElementSize + 10);
Buffer = v2;
if ( !v2 )
exit(1);
for ( i = 0; i < 32; ++i )
byte_409064[i] ^= 0x66u;
memset(v2, 0, ElementSize + 10);
fread(Buffer, ElementSize, 1u, v1);
return fclose(v1);
}
sub_40188D()
:不知道干嘛用的,事实证明也不用管
sub_401150()
:加密
int __cdecl sub_401150(const char *a1)
{
unsigned int v1; // ebx
int i; // ecx
char *v3; // esi
char *v4; // ecx
char v5; // al
bool v6; // zf
signed int v7; // ecx
char v8; // al
char v9; // al
unsigned int v10; // edx
int result; // eax
char v12; // [esp+Ch] [ebp-34h] BYREF
char v13[51]; // [esp+Dh] [ebp-33h] BYREF
v12 = 0;
memset(v13, 0, 0x30u);
v13[48] = 0;
v1 = 0;
if ( strlen(a1) )
{
for ( i = 1 - (_DWORD)&v12; ; i = 1 - (_DWORD)&v12 )
{
v3 = &v12 + v1;
v4 = &v12 + v1 + i;
v5 = a1[(_DWORD)v4 - 1];
v7 = (unsigned int)v4 & 0x80000001;
v6 = v7 == 0;
*(&v12 + v1) = v5;
if ( v7 < 0 )
v6 = (((_BYTE)v7 - 1) | 0xFFFFFFFE) == -1;
v8 = v6 ? v5 ^ 0x19 : v5 ^ 0x20;
*v3 = v8;
v9 = sub_4010C0(v8);
*v3 = v9;
byte_40B9C0[v1++] = v9;
if ( v1 >= strlen(a1) )
break;
}
}
v10 = 0;
if ( !strlen(a1) )
goto LABEL_15;
do
{
if ( *(&v12 + v10) != byte_409064[v10] )
break;
++v10;
}
while ( v10 < strlen(a1) );
if ( v10 == 32 )
result = 1;
else
LABEL_15:
result = 0;
return result;
}
流程比较复杂,一步步分析:
*Buffer
指向存在堆里的data.bin
- 对用户输入的单数双数位的数据分别异或
- 光看反编译的代码看不懂,动态调试看
v6
的变化发现的
- 光看反编译的代码看不懂,动态调试看
- 到
sub_4010C0()
,遍历data.bin
按照逻辑对每个字符进行一个流程的加密 - 比对密文
byte_409064
main
函数里对这段密文进行了异或,所以在静态下是假密文,需要debug才能显现真密文,或者写EXP的时候异或一下- 注意小端序
- 最后比对加密后的用户输入长度是不是32
sub_4010C0()
:根据data.bin
(相当于一整个加密流程本),读取到特定值时就做特定加密
- 不知道用什么指令欺骗了反编译器导致反编译会缺失一段(所以没找到要怎么patch),总之需要看汇编找加密逻辑,IDA的逻辑流程图帮了大忙
- 具体逻辑太麻烦了这里就不写了,只用对着图里涉及到比较的数值一个个写function就行,不会很难
![](http://www.asuka39.top/wp-content/uploads/2022/12/IDA0.png)
用密文直接解密很难(加密过程应该有信息丢失),采用爆破比较合适
EXP
字节处理u32这里搞不太懂python的类型转换,所以干脆上通用的移位处理算了
from pwn import *
c = [0x5D, 0x2E, 0x4C, 0x35, 0x49, 0x30, 0x06, 0x6E, 0x77, 0x73, 0x64, 0x30, 0x67, 0x72, 0x68, 0x3C, 0x76, 0x55, 0x5A, 0x52, 0x58, 0x2F, 0x7A, 0x3A, 0x53, 0x35, 0x5C, 0x79, 0x2A, 0x71, 0x09, 0x1C]
data = open("./data.bin","rb").read()
list = []
i = 0
while i < len(data):
# tmp = u32(data[i] + data[i + 1] + data[i + 2] + data[i + 3]) # only for python2
tmp = data[i + 3] << 24 | data[i + 2] << 16 | data[i + 1] << 8 | data[i]
if tmp == 0xc8b5bdc6:
list.append(1)
elif tmp == 0xc5d0cfb3:
list.append(2)
elif tmp == 0xb5d2b4be:
list.append(3)
elif tmp == 0xbfc7bbb8:
list.append(4)
elif tmp == 0xc6c9d1d3:
list.append(5)
elif tmp == 0xc9d3d4d7:
list.append(6)
elif tmp == 0xced6a8b7:
list.append(7)
elif tmp == 0xfab9aeb0:
list.append(8)
i += 4
def f1(char):
return((char + 1) ^ 0x19) & 0xff
def f2(char):
return((char ^ 0x19) - 1) & 0xff
def f3(char):
return(char >> 1) & 0xff
def f4(char):
return(char ^ 0x66) & 0xff
def f5(char):
return(char << 1) & 0xff
def f6(char):
return(char - 1) & 0xff
def f7(char):
return(char << 2) & 0xff
def f8(char):
return(char + 1) & 0xff
flag = ""
count = 0
for t in c:
count += 1
for i in range(33,126):
tmp = i
if count % 2 == 0:
code = (i ^ 0x19)
else:
code = (i ^ 0x20)
for index in list:
if index == 1:
code = f1(code)
elif index == 2:
code = f2(code)
elif index == 3:
code = f3(code)
elif index == 4:
code = f4(code)
elif index == 5:
code = f5(code)
elif index == 6:
code = f6(code)
elif index == 7:
code = f7(code)
elif index == 8:
code = f8(code)
if code == t:
flag += chr(i)
break
print(flag)
print("length:", count, count==32)
$ python3 exp.py
w5z6s7duQtR7AyFSTVhYv0H1m6JZxb33
length: 32 True
验证一下
> ./re_HeXie_dump_SCY.exe
Hi, SDNISC 2019 ~~~
serialNumber: w5z6s7duQtR7AyFSTVhYv0H1m6JZxb33
--> Congratulations ~~~ <--
flag{w5z6s7duQtR7AyFSTVhYv0H1m6JZxb33}
参考
- 第八届山东省大学生网络安全技能大赛--“深思杯” WP(CataLpa大佬)
Comments NOTHING