admin 管理员组文章数量: 1086019
目录
Windows Reverse1
Windows Reverse2
confused
obfuscating macros
黑盒破解2
北京地铁
MulTzor
[PWN] strike
Wireshark
联盟决策大会
滴~
Web签到题
Upload-IMG
大吉大利,今晚吃鸡~
Breaking LEM
Windows Reverse1
比较基础的一道逆向,加了UPX,直接用upx -d脱upx后,程序重定位会出问题,导致不好调试。解决方法是关闭地址随机化或带着壳调试。
主函数逻辑是将输入进行一次变换后与明文进行对比。变换如下:
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 )
return result;
v4 = a1 - v1;
do
{
*v1 = byte_402FF8[(char)v1[v4]];
++v2;
++v1;
result = strlen(a1);
}
while ( v2 < result );
return result;
}
这段代码无论是汇编形式还是反编译形式都有点诡异,还是得调试一下,发现这里就是将输入的每个字节作为偏移,以byte_402FF8为基址进行置换,置换表如下:
.data:00403018 aZyxwvutsrqponm db '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>'
.data:00403018 db '=<;:9876543210/.-,+*)(',27h,'&%$#"! ',0
由于byte_402FF8到这张表之间还有许多辣鸡数据,所以我们的输入需要先减去(0x403018-0x402FF8),再进行索引,即可完成置换。而这里我们需要将"DDCTF{reverseME}"还原为输入,这个逆过程很简单,随便写个脚本:
table = '''~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"! '''
s = 'DDCTF{reverseME}'
flag = ''
for c in s:
flag += chr(table.find(c) + 0x20)
print flag
解出来真是服了,没见过这么丑的flag
运行结果:
please input code:ZZ[JX#,9(9,+9QY!
You've got it!!DDCTF{reverseME}
Windows Reverse2
依旧是Win32程序,这次是有ASpack壳,和UPX类似,它也是压缩壳,这种东西用所谓的esp定律十分好找到入口点,不过为了速度,就不手脱了,随便去找个ASpack的脱壳机脱了就行,不过这个程序的ASpack版本较新,所以脱了后运行起来也有点问题,但这个题纯静态就可以解决了。
主程序逻辑和第1题相同,将输入进行变换与明文比较。而在这之前,有个对输入的判断
char __usercall sub_10611F0@<al>(const char *a1@<esi>)
{
signed int v1; // eax
signed int v2; // edx
int v3; // ecx
char v4; // al
v1 = strlen(a1);
v2 = v1;
if ( !v1 || v1 % 2 == 1 )
return 0;
v3 = 0;
if ( v1 <= 0 )
return 1;
while ( 1 )
{
v4 = a1[v3];
if ( (v4 < '0' || v4 > '9') && (v4 < 'A' || v4 > 'F') )
break;
if ( ++v3 >= v2 )
return 1;
}
return 0;
}
显然这就是说我们的输入必须为十六进制数,且为长度偶数。然后进到变换函数中
int __usercall sub_1061240@<eax>(const char *a1@<esi>, int a2)
{
signed int v2; // edi
signed int v3; // edx
char v4; // bl
char v5; // al
char v6; // al
unsigned int v7; // ecx
char v9; // [esp+Bh] [ebp-405h]
char v10; // [esp+Ch] [ebp-404h]
char Dst; // [esp+Dh] [ebp-403h]
v2 = strlen(a1);
v10 = 0;
memset(&Dst, 0, 0x3FFu);
v3 = 0;
if ( v2 <= 0 )
return sub_1061000(v2 / 2, a2);
v4 = v9;
do
{
v5 = a1[v3];
if ( (unsigned __int8)(a1[v3] - '0') > 9u )
{
if ( (unsigned __int8)(v5 - 'A') <= 5u )
v9 = v5 - '7';
}
else
{
v9 = a1[v3] - '0';
}
v6 = a1[v3 + 1];
if ( (unsigned __int8)(a1[v3 + 1] - '0') > 9u )
{
if ( (unsigned __int8)(v6 - 'A') <= 5u )
v4 = v6 - '7';
}
else
{
v4 = a1[v3 + 1] - '0';
}
v7 = (unsigned int)v3 >> 1;
v3 += 2;
*(&v10 + v7) = v4 | 16 * v9;
}
while ( v3 < v2 );
return sub_1061000(v2 / 2, a2);
}
这段代码将输入的每个字符变为一个数值,0~F分别对应十六进制数的值,并将两个字符作为一组生成新的数值,如s[0]*16 + s[1],乘16意味着左移4位,这完全就是将十六进制字符串转换为数值的函数。接着进入最后那个函数中,看几个关键点吧
v15[0] = Dst[0] >> 2;
v15[1] = (Dst[1] >> 4) + 16 * (Dst[0] & 3);
v15[2] = (Dst[2] >> 6) + 4 * (Dst[1] & 0xF);
v15[3] = Dst[2] & 0x3F;
i = 0;
do
std::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(
&v17,
(unsigned __int8)(byte_1063020[(unsigned __int8)v15[i++]] ^ 0x76));
while ( i < 4 );
可以看到这里将3个字符转换位了4个值,然后依次索引一张表,这不就是base64的编码方式嘛。我们去看那张表
'7452301>?<=:;89&\'$%"# !./,\x17\x14\x15\x12\x13\x10\x11\x1e\x1f\x1c\x1d\x1a\x1b\x18\x19\x06\x07\x04\x05\x02\x03\x00\x01\x0e\x0f\x0cFGDEBC@ANO]Y'
看起来很奇怪,但可以注意到查表时进行了xor 0x76的操作,如果我们将这个表xor 0x76会发现它正好就是标准base64的编码表
也就是说我们直接对"reverse+"进行base64解码就行了,得到的十六进制串就是flag,需要注意这里是大小字符
运行结果:
input code:ADEBDEAEC7BE
You've got it !!! DDCTF{reverse+}
confused
看到mac程序吓了跳,因为没有运行环境,不过这个题也是可以纯静态分析的。进去一堆看不懂的,但貌似是mac的某种GUI程序,IDA函数列表中有几个ViewController开头的函数,应该就是与控件有关的函数了,其中checkCode十分显眼,进入这个函数很快就能意识到它就是输入验证函数,很容易找到check点,进入check函数,其中一个函数对一个结构体进行了初始化,其中有许多成员是函数指针,看得不是很清楚,之后我们能找到一个关键函数
bool __fastcall run(vm_struc *a1)
{
bool result; // al
bool count; // [rsp+Fh] [rbp-11h]
signed int i; // [rsp+10h] [rbp-10h]
signed int k; // [rsp+14h] [rbp-Ch]
k = 0;
i = 0;
while ( 1 )
{
count = 0;
if ( !k )
count = i < 9;
result = count;
if ( !count )
break;
if ( *(unsigned __int8 *)a1->pc == *((unsigned __int8 *)&a1->code_F0 + 16 * i) )
{
k = 1;
(*((void (__fastcall **)(vm_struc *))&a1->mov_reg_imm + 2 * i))(a1);
}
else
{
++i;
}
}
return result;
}
虽然F5的代码很烂,但仔细分析下就能发现它是通过一串数据来对结构体中的函数表进行索引执行,很显然,这是个解释器,意识到它是虚拟机保护后,初始化结构体的逆向就很容易了,每个函数都是一个handler,但本题用到的指令不多
__int64 __fastcall init_vm(vm_struc *a1, char *input)
{
a1->reg0 = 0;
a1->reg1 = 0;
a1->reg2 = 0;
a1->reg3 = 0;
a1->flag = 0;
a1->buffer = 0;
LOBYTE(a1->code_F0) = -16;
a1->mov_reg_imm = (__int64)mov_reg_imm;
LOBYTE(a1->code_F1) = -15;
a1->xor_reg0_reg1 = (__int64)xor_reg0_reg0;
LOBYTE(a1->code_F2) = -14;
a1->cmp_reg0_imm = (__int64)cmp_reg0_imm;
LOBYTE(a1->code_F4) = -12;
a1->add_reg0_reg1 = (__int64)add_reg0_reg1;
LOBYTE(a1->code_F5) = -11;
a1->sub_reg0_reg1 = (__int64)sub_reg0_reg1;
LOBYTE(a1->code_F3) = -13;
a1->nop = (__int64)nop;
LOBYTE(a1->code_F6) = -10;
a1->jz_imm = (__int64)jz_imm;
LOBYTE(a1->code_F7) = -9;
a1->mov_buf_imm = (__int64)mov_buf_imm;
LOBYTE(a1->code_F8) = -8;
a1->enc_reg0_2 = (__int64)enc_reg0_2;
buffer = (char *)malloc(0x400uLL);
return __memcpy_chk((__int64)(buffer + 48), (__int64)input, 18LL, -1LL);
}
那么内存中那段数据就是就是指令了,我们将它dump下来,然后根据指令特征写个类似反汇编器的脚本就能还原出真正的执行过程,脚本如下,写得比较烂
code = [0xF0, 0x10, 0x66, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x30, 0xF6, 0xC1, 0xF0, 0x10, 0x63, 0x00, 0x00,
0x00, 0xF8, 0xF2, 0x31, 0xF6, 0xB6, 0xF0, 0x10, 0x6A, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x32, 0xF6,
0xAB, 0xF0, 0x10, 0x6A, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x33, 0xF6, 0xA0, 0xF0, 0x10, 0x6D, 0x00,
0x00, 0x00, 0xF8, 0xF2, 0x34, 0xF6, 0x95, 0xF0, 0x10, 0x57, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x35,
0xF6, 0x8A, 0xF0, 0x10, 0x6D, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x36, 0xF6, 0x7F, 0xF0, 0x10, 0x73,
0x00, 0x00, 0x00, 0xF8, 0xF2, 0x37, 0xF6, 0x74, 0xF0, 0x10, 0x45, 0x00, 0x00, 0x00, 0xF8, 0xF2,
0x38, 0xF6, 0x69, 0xF0, 0x10, 0x6D, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x39, 0xF6, 0x5E, 0xF0, 0x10,
0x72, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x3A, 0xF6, 0x53, 0xF0, 0x10, 0x52, 0x00, 0x00, 0x00, 0xF8,
0xF2, 0x3B, 0xF6, 0x48, 0xF0, 0x10, 0x66, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x3C, 0xF6, 0x3D, 0xF0,
0x10, 0x63, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x3D, 0xF6, 0x32, 0xF0, 0x10, 0x44, 0x00, 0x00, 0x00,
0xF8, 0xF2, 0x3E, 0xF6, 0x27, 0xF0, 0x10, 0x6A, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x3F, 0xF6, 0x1C,
0xF0, 0x10, 0x79, 0x00, 0x00, 0x00, 0xF8, 0xF2, 0x40, 0xF6, 0x11, 0xF0, 0x10, 0x65, 0x00, 0x00,
0x00, 0xF8, 0xF2, 0x41, 0xF6, 0x06, 0xF7, 0x01, 0x00, 0x00, 0x00, 0xF3, 0xF7, 0x00, 0x00, 0x00,
0x00, 0xF3, 0x5D, 0xC3, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]
table = {0xF0:(6, 'mov_reg_imm'), 0xF1:(2, 'xor_reg0_reg1'), 0xF2:(2, 'cmp_reg0_imm'), 0xF4:(2, 'add_reg0_reg1'),
0xF5:(2, 'sub_reg0_reg1'), 0xF3:(1, 'ret'), 0xF6:(2, 'jz_imm'), 0xF7:(5, 'mov_buf_imm'),
0xF8:(1, 'enc_reg0_2')}
pc = 0
while pc < len(code):
c = code[pc]
print hex(pc), '\t',
if c not in table.keys():
print 'nop'
pc += 1
continue
print table[c][1],
if table[c][0] == 1:
pc += 1
elif table[c][0] == 2:
if table[c][1] == 'jz_imm':
print hex(pc + code[pc+1]),
else:
print hex(code[pc+1]),
pc += 2
elif table[c][0] == 3:
print hex(code[pc+1]), hex(code[pc+2]),
pc += 3
elif table[c][0] == 5:
print code[pc+1],
pc += 5
elif table[c][0] == 6:
print 'eax', hex(code[pc+2]),
pc += 6
print ''
看一看得到的指令大概是这样
0x0 mov_reg_imm reg0 0x66
0x6 enc_reg0_2
0x7 cmp_reg0_imm 0x30
0x9 jz_imm 0xca
0xb mov_reg_imm reg0 0x63
0x11 enc_reg0_2
0x12 cmp_reg0_imm 0x31
0x14 jz_imm 0xca
0x16 mov_reg_imm reg0 0x6a
0x1c enc_reg0_2
0x1d cmp_reg0_imm 0x32
0x1f jz_imm 0xca
0x21 mov_reg_imm reg0 0x6a
0x27 enc_reg0_2
0x28 cmp_reg0_imm 0x33
0x2a jz_imm 0xca
0x2c mov_reg_imm reg0 0x6d
0x32 enc_reg0_2
0x33 cmp_reg0_imm 0x34
0x35 jz_imm 0xca
0x37 mov_reg_imm reg0 0x57
0x3d enc_reg0_2
0x3e cmp_reg0_imm 0x35
0x40 jz_imm 0xca
0x42 mov_reg_imm reg0 0x6d
0x48 enc_reg0_2
0x49 cmp_reg0_imm 0x36
0x4b jz_imm 0xca
0x4d mov_reg_imm reg0 0x73
0x53 enc_reg0_2
0x54 cmp_reg0_imm 0x37
0x56 jz_imm 0xca
0x58 mov_reg_imm reg0 0x45
0x5e enc_reg0_2
0x5f cmp_reg0_imm 0x38
0x61 jz_imm 0xca
0x63 mov_reg_imm reg0 0x6d
0x69 enc_reg0_2
0x6a cmp_reg0_imm 0x39
0x6c jz_imm 0xca
0x6e mov_reg_imm reg0 0x72
0x74
本文标签: WriteUp
版权声明:本文标题:DDCTF2019-Writeup 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1726379188a958443.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论