Jcxp's blog

巅峰极客逆向之flodb分析

Word count: 1.3kReading time: 6 min
2019/11/07 Share

前言

最近看群里有人在讨论今年的巅峰极客,说有道逆向题考的linux的反调试出的挺好的,借着这道题学习一下linux反调试

分析

  1. 第一个反调试ptrace函数,代码如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     if ( ptrace(0, 0LL, 1LL, 0LL) >= 0 )
    '''
    else
    v6 = 10000000;
    do
    {
    sleep(1u);
    --v6;
    }
    while ( v6 );
    exit(-1);
    }

ptrace函数的相关介绍可以参考官方介绍ptrace,这道题的ptrace绕过的方法很简单,直接nop相关跳转即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 修改前
.text:000000000040096A call _ptrace
.text:000000000040096F test rax, rax
.text:0000000000400972 js loc_400C6C
//修改后
.text:000000000040096A call _ptrace
.text:000000000040096F test rax, rax
.text:0000000000400972 nop
.text:0000000000400973 nop
.text:0000000000400974 nop
.text:0000000000400975 nop
.text:0000000000400976 nop
.text:0000000000400977 nop
  1. 第二个反调试是监控程序执行的时间:
1
2
3
4
5
6
7
8
 v4 = time(0LL);
v5 = (unsigned __int64)time(0LL) - v4;
if ( v5 <= 1 )
{
func3(&v10, 2LL, 7LL, 14LL);
v7 = 4196782LL;
JUMPOUT(__CS__, v8 + 10);
}

同样nop掉即可

1
2
3
4
5
6
7
8
9
10
11
12
//修改前
.text:0000000000400981 sub edi, r12d ; status
.text:0000000000400984 cmp edi, 1
.text:0000000000400987 jg loc_400C51

//修改后
.text:0000000000400981 sub edi, r12d ; status
.text:0000000000400984 cmp edi, 1
.text:0000000000400987 nop
.text:0000000000400988 nop
.text:0000000000400989 nop
.text:000000000040098A nop
  1. 后面的反调试可以通过使用ida把如下的数据转化为代码(程序有部分花指令,导ida初始化会反编译出错)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.text:00000000004009C4                 dd 48EBEBEBh
.text:00000000004009C8 dq 11B908247C8Dh, 4BE00000006BA00h, 440E8000000h, 0EBB866C8FFC0FFEBh
.text:00000000004009C8 dq 0EB90EBFA74C03105h, 0FFC0FFEBC8FFC0FFh, 8D48C8FFC0FFEBC8h
.text:00000000004009C8 dq 0AB924247Ch, 3BE00000005BAh, 0E800000409E80000h, 24B48D48FFFFFE04h
.text:00000000004009C8 dq 75E8C789000000A0h, 0FC589C085000004h, 5C8D480000020B85h
.text:00000000004009C8 dq 0BA0000000FB92C24h, 6BE00000009h, 3CFE8E7894800h, 6BA00000008B900h
.text:00000000004009C8 dq 3BE000000h, 3B8E8DF8948h, 89FFFFFD71E8FF31h, 0F02FF83E72944C7h
.text:00000000004009C8 dq 0EB9000001D48Fh, 0BE00000005BA0000h, 0E8EF894C00000001h
.text:00000000004009C8 dq 0FFFCF7E80000038Ch, 0FFFFFD10E8C789FFh, 0FFFFFD78E8C58941h
.text:00000000004009C8 dq 1AA850FC53941h, 13B9F63100h, 0E7894800000002BAh, 356E840246C8D4Ch
.text:00000000004009C8 dq 0C03105EBB8660000h, 0FFC0FFEB90EBFA74h, 0C03105EBB86690C8h
.text:00000000004009C8 dq 0C8FFC0FFEBEBFA74h, 10B90C247C8D48h, 0BE00000005BA0000h
.text:00000000004009C8 dq 31FE800000003h, 4B93C247C8D4800h, 3BA000000h, 306E800000001BEh
.text:00000000004009C8 dq 0C8FFC0FFEB900000h, 0C03105EBB8669090h, 5EBB86690EBFA74h
.text:00000000004009C8 dq 909090EBFA74C031h, 0BB920247C8D48h, 0BE00000006BA0000h
.text:00000000004009C8 dq 2CFE800000003h, 13B9F63100h, 0E7894800000007BAh, 0E89090000002BBE8h
.text:00000000004009C8 dq 0C083485800000000h, 0EBEBEBEBEBE0FF0Ch, 74C03105EBB86690h
.text:00000000004009C8 dq 3B9F63190EBFAh, 4C00000002BA0000h, 4800000289E8EF89h
.text:00000000004009C8 dq 9B9F63128247C8Dh, 2BA000000h, 0E8FF3100000273E8h, 2944C789FFFFFC2Ch
.text:00000000004009C8 dq 8F8F0F03FF83E7h, 8B9DF89480000h, 0BE00000003BA0000h
.text:00000000004009C8 dq 45E8DB3100000001h, 3B9000002h, 4CF63100000002BAh, 0B800000231E8EF89h
.text:00000000004009C8 dq 2E660EEB00000053h, 841F0Fh, 759C043B509C448Bh, 0FB834801C383483Ch
.text:00000000004009C8 dq 400FFDBFED7513h, 948B48FFFFFB63E8h, 334864000004A824h
.text:00000000004009C8 dq 0E889000000282514h, 4B8C481482475h, 0C35D415C415D5B00h
.text:00000000004009C8 dq 0FF2BFD8EBFFCD83h, 89FFFFFB31E80040h
.text:0000000000400C50 db 0DFh

同样是监控代码执行的反调试,直接patch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//修改前
.text:0000000000400A6A call _time
.text:0000000000400A6F mov edi, eax
.text:0000000000400A71 sub edi, r12d
.text:0000000000400A74 cmp edi, 2
.text:0000000000400A77 jg loc_400C51
//修改后
text:0000000000400A6A call _time
.text:0000000000400A6F mov edi, eax
.text:0000000000400A71 sub edi, r12d ; status
.text:0000000000400A74 cmp edi, 2
.text:0000000000400A77 nop
.text:0000000000400A78 nop
.text:0000000000400A79 nop
.text:0000000000400A7A nop
.text:0000000000400A7B nop
.text:0000000000400A7C nop
  1. 第四个反调试是通过getppidgetsid实现的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //修改前
    .text:0000000000400A94 call _getpid
    .text:0000000000400A99 mov edi, eax ; pid
    .text:0000000000400A9B call _getsid
    .text:0000000000400AA0 mov r13d, eax
    .text:0000000000400AA3 call _getppid
    .text:0000000000400AA8 cmp r13d, eax
    .text:0000000000400AAB jnz loc_400C5B

    //修改后
    .text:0000000000400A94 call _getpid
    .text:0000000000400A99 mov edi, eax ; pid
    .text:0000000000400A9B call _getsid
    .text:0000000000400AA0 mov r13d, eax
    .text:0000000000400AA3 call _getppid
    .text:0000000000400AA8 cmp r13d, eax
    .text:0000000000400AAB nop
    .text:0000000000400AAC nop
    .text:0000000000400AAD nop
    .text:0000000000400AAE nop
    .text:0000000000400AAF nop
    .text:0000000000400AB0 nop
  2. 最后一个反调试同样是通过监控代码执行时间实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//修改前
.text:0000000000400BAF call _time
.text:0000000000400BB4 mov edi, eax
.text:0000000000400BB6 sub edi, r12d
.text:0000000000400BB9 cmp edi, 3
//修改后
.text:0000000000400BAF call _time
.text:0000000000400BB4 mov edi, eax
.text:0000000000400BB6 sub edi, r12d
.text:0000000000400BB9 cmp edi, 3
.text:0000000000400BBC nop
.text:0000000000400BBD nop
.text:0000000000400BBE nop
.text:0000000000400BBF nop
.text:0000000000400BC0 nop
.text:0000000000400BC1 nop

有几段花指令,特征就是:

  • call $+5
  • dec eax;inc eax
  • jmp short near

比如第一段call $+5,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.text:00000000004009A9                 call    $+5
.text:00000000004009AE pop rax
.text:00000000004009AF
.text:00000000004009AF loc_4009AF: ; CODE XREF: main+164↓j
.text:00000000004009AF add rax, 0Ah
.text:00000000004009B3 jmp rax
.text:00000000004009B3 ; ---------------------------------------------------------------------------
.text:00000000004009B5 db 3 dup(0EBh)
.text:00000000004009B8 ; ---------------------------------------------------------------------------
.text:00000000004009B8 call $+5
.text:00000000004009BD pop rbx
.text:00000000004009BE add rbx, 0Ah
.text:00000000004009C2 jmp rbx
.text:00000000004009C4 ; ---------------------------------------------------------------------------
.text:00000000004009C4 jmp short near ptr loc_4009AF+2
.text:00000000004009C6 ; ---------------------------------------------------------------------------
.text:00000000004009C6 jmp short near ptr loc_400A0D+3
.text:00000000004009C8 ; ---------------------------------------------------------------------------
.text:000000

可以通过分析或者调试定位到跳转地址,直接jmp就好

调试

处理了反调试之后,gdb可以直接调试,字符处理的主要逻辑通过func3,func2,func1,三个函数实现,过程相对复杂,不过通过对0x400C04下断点,可以清晰的获得我们输入的数据加密后的结果,以及要比较的对象

如下所示,我们知道字符的长度为19

1
2
3
4
5
6
7
8
   0x400bf4 <main+916>:	jmp    0x400c04 <main+932>
0x400bf6 <main+918>: nop WORD PTR cs:[rax+rax*1+0x0]
0x400c00 <main+928>: mov eax,DWORD PTR [rsp+rbx*4+0x50]//要比较的字符
=> 0x400c04 <main+932>: cmp eax,DWORD PTR [rsp+rbx*4]//用户处理后的字符串
0x400c07 <main+935>: jne 0x400c45 <main+997>
0x400c09 <main+937>: add rbx,0x1
0x400c0d <main+941>: cmp rbx,0x13
0x400c11 <main+945>: jne 0x400c00 <main+928>

用来比较的字符串如下

1
2
3
4
5
6
7
gdb-peda$ x /20wx $rsp+0x50
0x7fffffffdfb0: 0x00000053 0x00000040 0x00000079 0x00000052
0x7fffffffdfc0: 0x00000074 0x00000066 0x00000054 0x0000006c
0x7fffffffdfd0: 0x00000030 0x0000002b 0x00000061 0x00000067
0x7fffffffdfe0: 0x0000002d 0x0000004c 0x0000005f 0x00000033
0x7fffffffdff0: 0x0000004d 0x0000007d 0x0000007b 0x00007fff
//S@yRtfTl0+ag-L_3M}{

我们输入abcdefghijklmnopqrs生成的数据如下

1
2
3
4
5
6
7
gdb-peda$ x /20wx 0x7fffffffdf60
0x7fffffffdf60: 0x00000069 0x00000070 0x00000067 0x0000006d
0x7fffffffdf70: 0x0000006a 0x00000061 0x0000006f 0x00000062
0x7fffffffdf80: 0x0000006e 0x00000071 0x00000063 0x00000064
0x7fffffffdf90: 0x00000068 0x0000006b 0x0000006c 0x00000072
0x7fffffffdfa0: 0x00000066 0x00000073 0x00000065 0x00007fff
//ipgmjaobnqcdhklrfse

通过分析输入的数据和输出的数据,能够猜到func3,func2,func1三个函数可能是对输入做了某种移位处理,编写代码进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
s=[0x69,0x70,0x67,0x6d,0x6a,0x61,0x6f,0x62,0x6e,0x71,0x63,0x64,0x68,0x6b,0x6c,0x72,0x66,0x73,0x65]
print len(s)
print ''.join(chr(i) for i in s)

shuru='abcdefghijklmnopqrs'

cipher=[0x53,0x40,0x79,0x52,0x74,0x66,0x54,0x6c,0x30,0x2b,0x61,0x67,0x2d,0x4c,0x5f,0x33,0x4d,0x7d,0x7b]
print ''.join(chr(i) for i in cipher)
flag=''

for i in range(19):
flag+=chr(cipher[s.index(ord(shuru[i]))])

print flag

成功获得flag

1
2
3
4
5
 ⚙ jcxp@ubuntu  ~/ctf/dianfengjike  python a.py
19
ipgmjaobnqcdhklrfse
S@yRtfTl0+ag-L_3M}{
flag{My-StL_R0T@+3}
CATALOG
  1. 1. 前言
  2. 2. 分析
  3. 3. 调试