合天网安实验室的逆向做了一遍。

逆向 100

Description

看你的咯,移动安全大神(逆向 100, 已解决 534)

题目描述: dex2jar 是我们的好朋友
相关附件: rev100.zip

Solution

$ file rev100
rev100: Zip archive data, at least v2.0 to extract

可以判断出其实给的文件是个 apk,根据提示用 dex2jar 可以做。我这里用 jadx 反编译:

package ctf.crackme;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class FlagActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flag);
        String flag = "";
        int[] d = new int[]{102, 108, 97, 103, 123, 119, 52, 110, 110, 52, 95, 106, 52, 114, 95, 109, 121, 95, 100, 51, 120, 125};
        for (int i = 0; i < 22; i++) {
            flag = flag.concat(String.valueOf((char) d[i]));
        }
        ((TextView) findViewById(R.id.flagText)).setText(flag);
    }

    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.flag, menu);
        return true;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

直接把数组 d 的值转字符就完事了:

#!/usr/bin/env python
enc = [102, 108, 97, 103, 123, 119, 52, 110, 110, 52, 95, 106, 52, 114, 95, 109, 121, 95, 100, 51, 120, 125]
flag = ''
for i in range(len(enc)):
    flag += chr(enc[i])
print flag
# flag{w4nn4_j4r_my_d3x}

逆向 200

Description

做题累了玩个游戏吧(逆向 200, 已解决 309)

题目描述: pwd1_pwd2
相关附件: rev200.exe

Solution

$ file rev200.exe
rev200.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows

程序加了 tls,但对逆向没有什么影响。直接上 OD 调:

00401B33  |.  83EC 08       sub esp,0x8
00401B36  |.  A1 8CAD4000   mov eax,dword ptr ds:[0x40AD8C]          ; ||||
00401B3B  |.  890424        mov dword ptr ss:[esp],eax               ; ||||
00401B3E  |.  E8 4D5F0000   call <jmp.&msvcrt.puts>                  ; |||\puts
00401B43  |.  8D85 A9FEFFFF lea eax,dword ptr ss:[ebp-0x157]         ; |||
00401B49  |.  894424 04     mov dword ptr ss:[esp+0x4],eax           ; |||
00401B4D  |.  C70424 209040>mov dword ptr ss:[esp],re200_no.00409020 ; |||ASCII "%20s"
00401B54  |.  E8 2F5F0000   call <jmp.&msvcrt.scanf>                 ; ||\scanf
00401B59  |.  8D85 BDFEFFFF lea eax,dword ptr ss:[ebp-0x143]         ; ||
00401B5F  |.  894424 04     mov dword ptr ss:[esp+0x4],eax           ; ||
00401B63  |.  8D85 A9FEFFFF lea eax,dword ptr ss:[ebp-0x157]         ; ||
00401B69  |.  890424        mov dword ptr ss:[esp],eax               ; ||
00401B6C  |.  E8 275F0000   call <jmp.&msvcrt.strcmp>                ; |\strcmp
00401B71  |.  85C0          test eax,eax                             ; |
00401B73  |.  75 18         jnz short re200_no.00401B8D              ; |
00401B75  |.  C70424 269140>mov dword ptr ss:[esp],re200_no.00409126 ; |ASCII "You passed level1!"
00401B7C  |.  E8 0F5F0000   call <jmp.&msvcrt.puts>                  ; \puts
00401B81  |.  C70424 000000>mov dword ptr ss:[esp],0x0
00401B88  |.  E8 5DFAFFFF   call re200_no.004015EA
00401B8D  |>  B8 00000000   mov eax,0x0
00401B92  |.  8D65 F8       lea esp,[local.2]
00401B95  |.  59            pop ecx                                  ;  msvcrt.77558E62
00401B96  |.  5B            pop ebx
00401B97  |.  5D            pop ebp
00401B98  |.  8D61 FC       lea esp,dword ptr ds:[ecx-0x4]
00401B9B  \.  C3            retn

第一个 check 很容易找到 passwd:

堆栈地址=0028FD75, (ASCII "r0b0RUlez!")
eax=00000001

第二个 check 有个 int3 反调试:

004015EA  /$  55            push ebp
004015EB  |.  89E5          mov ebp,esp
004015ED  |.  83EC 18       sub esp,0x18
004015F0  |.  837D 08 09    cmp [arg.1],0x9
004015F4  |.  7F 11         jg short re200_no.00401607
004015F6  |.  8345 08 01    add [arg.1],0x1
004015FA  |.  8B45 08       mov eax,[arg.1]
004015FD  |.  890424        mov dword ptr ss:[esp],eax               ;  re200_no.00401619
00401600  |.  E8 E5FFFFFF   call re200_no.004015EA
00401605  |.  EB 1E         jmp short re200_no.00401625
00401607  |>  A1 90AD4000   mov eax,dword ptr ds:[0x40AD90]          ; |
0040160C  |.  890424        mov dword ptr ss:[esp],eax               ; |re200_no.00401619
0040160F  |.  E8 7C640000   call <jmp.&msvcrt.puts>                  ; \puts
00401614  |.  E8 00000000   call re200_no.00401619
00401619  |$  58            pop eax                                  ;  0028FDD5
0040161A  |.  A3 A8AD4000   mov dword ptr ds:[0x40ADA8],eax          ;  re200_no.00401619
0040161F  |.  CC            int3
00401620  |.  B8 00000000   mov eax,0x0
00401625  |>  C9            leave
00401626  \.  C3            retn

新增的 AddVectoredExceptionHandler 这个 API 将一个指向函数的指针作为参数,把这个函数的地址添加到已注册的异常处理程序链表中。那么这里的 int 3 异常会交给异常处理程序链表中第一个处理函数处理,假如调试器处理这个异常,我们就到不了那里了,所以 OD 的设置一定要忽略所有异常,让程序或系统自己处理。

00401B0E  |.  83EC 04       sub esp,0x4
00401B11  |.  895C24 04     mov dword ptr ss:[esp+0x4],ebx                        ; |
00401B15  |.  890424        mov dword ptr ss:[esp],eax                            ; |re200_no.00401619
00401B18  |.  A1 60B14000   mov eax,dword ptr ds:[<&KERNEL32.GetProcAddress>]     ; |
00401B1D  |.  FFD0          call eax                                              ; \GetProcAddress
00401B1F  |.  83EC 08       sub esp,0x8
00401B22  |.  C74424 04 7F1>mov dword ptr ss:[esp+0x4],re200_no.0040157F
00401B2A  |.  C70424 010000>mov dword ptr ss:[esp],0x1
00401B31  |.  FFD0          call eax                                              ;  re200_no.00401619
00401B33  |.  83EC 08       sub esp,0x8
00401B36  |.  A1 8CAD4000   mov eax,dword ptr ds:[0x40AD8C]                       ; ||||
00401B3B  |.  890424        mov dword ptr ss:[esp],eax                            ; ||||re200_no.00401619
00401B3E  |.  E8 4D5F0000   call <jmp.&msvcrt.puts>                               ; |||\puts

重新调试一遍,可以在 1AF9 处看到程序将 AddVectoredExceptionHandler 的地址(DS:0x40ADAC 存放的是字符串 AddVectoredExceptionHandler)从 kernel32.dll 中取出,并且将 0040157F 函数注册到了这个地方:

00401AF9  |.  8B1D ACAD4000 MOV EBX,DWORD PTR DS:[0x40ADAC]                       ; |
00401AFF  |.  A1 9CAD4000   MOV EAX,DWORD PTR DS:[0x40AD9C]                       ; |
00401B04  |.  890424        MOV DWORD PTR SS:[ESP],EAX                            ; |
00401B07  |.  A1 5CB14000   MOV EAX,DWORD PTR DS:[<&KERNEL32.GetModuleHandleA>]   ; |
00401B0C  |.  FFD0          CALL EAX                                              ; \GetModuleHandleA
00401B0E  |.  83EC 04       SUB ESP,0x4
00401B11  |.  895C24 04     MOV DWORD PTR SS:[ESP+0x4],EBX                        ; |
00401B15  |.  890424        MOV DWORD PTR SS:[ESP],EAX                            ; |
00401B18  |.  A1 60B14000   MOV EAX,DWORD PTR DS:[<&KERNEL32.GetProcAddress>]     ; |
00401B1D  |.  FFD0          CALL EAX                                              ; \GetProcAddress
00401B1F  |.  83EC 08       SUB ESP,0x8
00401B22  |.  C74424 04 7F1>MOV DWORD PTR SS:[ESP+0x4],rev200.0040157F
00401B2A  |.  C70424 010000>MOV DWORD PTR SS:[ESP],0x1
00401B31  |.  FFD0          CALL EAX

找到 157F 处,删除分析后就能看到代码:

0040157F    55              push ebp
00401580    89E5            mov ebp,esp
00401582    83EC 38         sub esp,0x38
00401585    8B45 08         mov eax,dword ptr ss:[ebp+0x8]
00401588    8B40 04         mov eax,dword ptr ds:[eax+0x4]           ; ntdll_12.77DF9D4C
0040158B    8B80 B8000000   mov eax,dword ptr ds:[eax+0xB8]
00401591    8945 F4         mov dword ptr ss:[ebp-0xC],eax
00401594    8B45 F4         mov eax,dword ptr ss:[ebp-0xC]           ; re200_no.00409020
00401597    8B15 A8AD4000   mov edx,dword ptr ds:[0x40ADA8]          ; re200_no.00401619
0040159D    83C2 06         add edx,0x6
004015A0    39D0            cmp eax,edx                              ; msvcrt._iob
004015A2    75 38           jnz short re200_no.004015DC
004015A4    8D45 E0         lea eax,dword ptr ss:[ebp-0x20]
004015A7    894424 04       mov dword ptr ss:[esp+0x4],eax
004015AB    C70424 20904000 mov dword ptr ss:[esp],re200_no.00409020 ; ASCII "%20s"
004015B2    E8 D1640000     call <jmp.&msvcrt.scanf>
004015B7    A1 98AD4000     mov eax,dword ptr ds:[0x40AD98]
004015BC    894424 04       mov dword ptr ss:[esp+0x4],eax
004015C0    8D45 E0         lea eax,dword ptr ss:[ebp-0x20]
004015C3    890424          mov dword ptr ss:[esp],eax
004015C6    E8 7CFFFFFF     call re200_no.00401547

1547 处,可以看到是对内存中长度为 8 的一串数据与 2 进行了异或:

00401547    55              push ebp
00401548    89E5            mov ebp,esp
0040154A    EB 22           jmp short re200_no.0040156E
0040154C    8B45 08         mov eax,dword ptr ss:[ebp+0x8]
0040154F    0FB610          movzx edx,byte ptr ds:[eax]
00401552    8B45 0C         mov eax,dword ptr ss:[ebp+0xC]
00401555    0FB600          movzx eax,byte ptr ds:[eax]
00401558    83F0 02         xor eax,0x2
0040155B    38C2            cmp dl,al
0040155D    74 07           je short re200_no.00401566
0040155F    B8 01000000     mov eax,0x1
00401564    EB 17           jmp short re200_no.0040157D
00401566    8345 08 01      add dword ptr ss:[ebp+0x8],0x1
0040156A    8345 0C 01      add dword ptr ss:[ebp+0xC],0x1
0040156E    8B45 0C         mov eax,dword ptr ss:[ebp+0xC]
00401571    0FB600          movzx eax,byte ptr ds:[eax]
00401574    3C 02           cmp al,0x2
00401576  ^ 75 D4           jnz short re200_no.0040154C
00401578    B8 00000000     mov eax,0x0
0040157D    5D              pop ebp                                  ; re200_no.004015CB
0040157E    C3              retn

在堆栈部分可以看到存储的地址:

0028F62C  /0028F66C
0028F630  |004015CB  返回到 re200_no.004015CB 来自 re200_no.00401547
0028F634  |0028F64C  输入字符串地址
0028F638  |0028FDCC  用来比较的字符串的地址
0028F63C  |00000000
0028F640  |00000000
0028F644  |00000000
0028F648  |00000000

在数据窗口跟随得到数据:

0028FDCC  75 31 6E 6E 66 32 6C 67 02 50 6C 65 61 73 65 20  u1nnf2lgPlease
0028FDDC  65 6E 74 65 72 20 74 68 65 20 73 65 63 6F 6E 64  enter the second

写个脚本逆出结果:

#!/usr/bin/env python
enc = [0x75, 0x31, 0x6E, 0x6E, 0x66, 0x32, 0x6C, 0x67, 0x02]
res = ''
for i in range(len(enc)):
    res += chr(enc[i] ^ 0x02)
print res
# w3lld0ne

逆向 300

Description

ELF(逆向 300, 已解决 139)

题目描述: 靠,我这儿也没有 IDA 啊
相关附件: rev300.zip

Solution

$ file rev300
rev300: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=a93ffe39302e19ef5184a1d86b720b11a7a97941, stripped

总共有两个 check:

int __cdecl main(int a1, char **a2)
{
  if ( a1 > 1 && sub_8048414(a2[1], 0) )
  {
    puts("Access granted");
    sub_8048538((int)a2[1]);
  }
  else
  {
    puts("Access denied");
  }
  return 0;
}

第一个是对程序参数的的一个比较,一个递归的函数:

signed int __cdecl sub_8048414(_BYTE *a1, int a2)
{
  signed int result; // eax

  switch ( a2 )
  {
    case 0:
      if ( *a1 == 'i' )
        goto LABEL_19;
      result = 0;
      break;
    case 1:
      if ( *a1 == 'e' )
        goto LABEL_19;
      result = 0;
      break;
    case 3:
      if ( *a1 == 'n' )
        goto LABEL_19;
      result = 0;
      break;
    case 4:
      if ( *a1 == 'd' )
        goto LABEL_19;
      result = 0;
      break;
    case 5:
      if ( *a1 == 'a' )
        goto LABEL_19;
      result = 0;
      break;
    case 6:
      if ( *a1 == 'g' )
        goto LABEL_19;
      result = 0;
      break;
    case 7:
      if ( *a1 == 's' )
        goto LABEL_19;
      result = 0;
      break;
    case 9:
      if ( *a1 == 'r' )
LABEL_19:
        result = sub_8048414(a1 + 1, 7 * (a2 + 1) % 11);
      else
        result = 0;
      break;
    default:
      result = 1;
      break;
  }
  return result;
}

第二个是把正确的输入和内存中的数据进行异或:

int __cdecl sub_8048538(int a1)
{
  int v2[33]; // [esp+18h] [ebp-A0h]
  int i; // [esp+9Ch] [ebp-1Ch]

  qmemcpy(v2, &unk_8048760, sizeof(v2));
  for ( i = 0; i <= 32; ++i )
    putchar(v2[i] ^ *(char *)(a1 + i % 8));
  return putchar(10);
}

脚本如下:

#!/usr/bin/env python
enc = [0x0000000F, 0x0000001F, 0x00000004, 0x00000009, 0x0000001C, 0x00000012, 0x00000042, 0x00000009, 0x0000000C, 0x00000044, 0x0000000D, 0x00000007, 0x00000009, 0x00000006, 0x0000002D, 0x00000037, 0x00000059, 0x0000001E, 0x00000000, 0x00000059, 0x0000000F, 0x00000008, 0x0000001C, 0x00000023, 0x00000036, 0x00000007, 0x00000055, 0x00000002, 0x0000000C, 0x00000008, 0x00000041, 0x0000000A, 0x00000014]
key = ''
i = 0
for j in range(8):
    if i == 0:
        key += 'i'
    if i == 1:
        key += 'e'
    if i == 3:
        key += 'n'
    if i == 4:
        key += 'd'
    if i == 5:
        key += 'a'
    if i == 6:
        key += 'g'
    if i == 7:
        key += 's'
    if i == 9:
        key += 'r'
    i = 7 * (i + 1) % 11
print key
flag = ''
for i in range(len(enc)):
    flag += chr(enc[i] ^ ord(key[i % 8]))
print flag

对参数的求解也可以用 angr 来完成:

#!/usr/bin/env python
import angr
import claripy
p = angr.Project('./rev300')
flag = claripy.BVS('flag', 50 * 8)
state = p.factory.entry_state(args=['./rev300', flag])
sim = p.factory.simgr(state)
sim.explore(find=0x080485E0, avoid=0x080485FE)
print(sim.found[0].solver.eval(flag, cast_to=bytes))

References

https://www.52pojie.cn/forum.php?mod=viewthread&tid=530793
https://blog.csdn.net/xiangshangbashaonian/article/details/82953042
https://docs.microsoft.com/en-us/windows/win32/debug/using-a-vectored-exception-handler


ctf wp re

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

简单虚拟机指令类题目分析
2017-CSAW-Quals-realism