2019DDCTF的一道简单逆向

昨晚躺尸的时候看到群里大佬发了一张DDCTF战况的图就起床做了一下,也就有了这个题解,RE2跟RE1其实核心思想是差不多的,只是多了一些移位操作罢了,算是弥补上个月0CTF、“西湖论剑”逆向零的突破吧
终究还是太菜了,另外一道CPP逆向也是看得头疼
——2019.4.13

0x00 查壳

一看是UPX壳,修复一下然后直接!

0x01 分析

1.载入IDA X86
  • main函数伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax@2
  char v4; // [sp+4h] [bp-804h]@1
  char v5; // [sp+5h] [bp-803h]@1
  char v6; // [sp+404h] [bp-404h]@1
  char Dst; // [sp+405h] [bp-403h]@1

  v6 = 0;
  memset(&Dst, 0, 0x3FFu);
  v4 = 0;
  memset(&v5, 0, 0x3FFu);
  printf("please input code:");
  scanf("%s", &v6);
  sub_401000(&v6);
  if ( !strcmp(&v4, "DDCTF{reverseME}") )
  {
    printf("You've got it!!%s\n", &v4);
    result = 0;
  }
  else
  {
    printf("Try again later.\n");
    result = 0;
  }
  return result;
}
  • sub_401000函数伪代码
unsigned int __cdecl sub_401000(const char *a1)
{
  _BYTE *v1; // ecx@0
  unsigned int v2; // edi@1
  unsigned int result; // eax@1
  int v4; // ebx@2

  v2 = 0;
  result = strlen(a1);
  if ( result )
  {
    v4 = a1 - v1;
    do
    {
      *v1 = byte_402FF8[v1[v4]];
      ++v2;
      ++v1;
      result = strlen(a1);
    }
    while ( v2 < result );
  }
  return result;
}

程序大致就是将输入字符串在函数sub_401000中进行简单变换后与"DDCTF{reverseME}"比较

2.载入OD

直接在变换函数前断点



然后动态调试
伪代码中的变换就是401020~40103E的内容



核心代码无非就三条
……
movsx eax,byte ptr ds:[ebx+eax]//ds:[ebx+eax]寻址获得的数据即为将要处理的数据,并将其字扩展存入eax中
mov dl,byte ptr ds:[eax+0x402FF8]//通过eax和0x402FF8相对寄存器存址获得的字节数据存入dl中
……
mov byte ptr ds:[ecx],dl//dl数据写入数据段
……
//循环执行

所以该函数就是将输入的字符串逐个字符的hex作为偏移量在数据段中寻址获得的字符逐个拼接并写入数据段
即伪代码是

flag=""
for i in input
    flag+=ds:[hex(i)+0x402FF8]

查看地址为0x402FF8起的数据段内容

.rdata:00402FF8 ; char byte_402FF8[]
.rdata:00402FF8 byte_402FF8     db ?                    ; DATA XREF: sub_401000+24�r
.rdata:00402FF9                 align 10h
.rdata:00402FF9 _rdata          ends
.rdata:00402FF9
.data:00403000 ; Section 3. (virtual address 00003000)
.data:00403000 ; Virtual size                  : 000003E4 (    996.)
.data:00403000 ; Section size in file          : 00000200 (    512.)
.data:00403000 ; Offset to raw data for section: 00001600
.data:00403000 ; Flags C0000040: Data Readable Writable
.data:00403000 ; Alignment     : default
.data:00403000 ; ===========================================================================
.data:00403000
.data:00403000 ; Segment type: Pure data
.data:00403000 ; Segment permissions: Read/Write
.data:00403000 _data           segment para public 'DATA' use32
.data:00403000                 assume cs:_data
.data:00403000                 ;org 403000h
.data:00403000 ___security_cookie dd 0BB40E64Eh        ; DATA XREF: _main+6�r
.data:00403000                                         ; __security_check_cookie(x)�r ...
.data:00403004 dword_403004    dd 44BF19B1h            ; DATA XREF: ___report_gsfailure+B0�r
.data:00403004                                         ; ___security_init_cookie+2B�w ...
.data:00403008                 db 0FFh
.data:00403009                 db 0FFh
.data:0040300A                 db 0FFh
.data:0040300B                 db 0FFh
.data:0040300C                 db 0FFh
.data:0040300D                 db 0FFh
.data:0040300E                 db 0FFh
.data:0040300F                 db 0FFh
.data:00403010 dword_403010    dd 0FFFFFFFEh           ; DATA XREF: .text:004013E2�r
.data:00403014 dword_403014    dd 1                    ; DATA XREF: .text:004013C8�r
.data:00403018 aZyxwvutsrqponm db '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>'
.data:00403018                 db '=<;:9876543210/.-,+*)(',27h,'&%$#"! ',0
.data:00403078 ; int argc

注意到0x403018起定义了这么一段字符

~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>'=<;:9876543210/.-,+*)(',27h,'&%$#"!

到此就可以解题了,直接逆算法遍历目标字符串获得输入

dst="DDCTF{reverseME}"
str="~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>'=<;:9876543210/.-,+*)(',27h,'&%$#\"!"
flag=""
for i in dst:
    index=str.find(i)
    flag+=chr(index+(0x403018-0x402FF8))
print("flag:"+flag)

获得flag是ZZ[JX#,9(9,+9QY!


最后包裹上DDCTF{}提交即可

你可能感兴趣的:(2019DDCTF的一道简单逆向)