《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现

第1课:二进制安全逆向介绍

二进制安全,能干什么

  • 逆向分析:
    负责成品软件的技术原理. 比如分析竞品软件,吸取技术上的优点,进行技术难点公关

  • 病毒分析:
    负责分析病毒样本.研究恶意代码的技术手段等工作.主要是在安全公司,尤其是在杀毒软件公司需求较多.如360 、腾讯电脑管家等.

  • 漏洞挖掘分析:
    负责分析漏洞样本,或者漏洞的挖掘.目前二进制的主要方向.涉及范围广,从主流浏览器 虚拟机 内核到IOT 还有android 和 IOS移动平台.

  • 移动安全:
    负责移动端安全.如移动端的漏洞挖掘,还有加壳混淆等

  • 安全开发:
    包含较广.比如硬件平台,内核安全等.一般也是安全公司.如防火墙 主动防御系统 反外挂等

逆向与开发的对比

以C语言举例,C语言代码属于高级语言代码,不能直接被处理器执行,这时候需要由编译器将C语言代码翻译成处理器可以直接理解的机器代码。

编译流程: C语言代码 -> … -> 汇编语言代码 -> … -> 机器代码

机器代码会按照一定的排列方式存储在可执行文件中,最常见的可执行文件有 Windows 的 exe 文件以及 Linux 的 ELF 文件。

逆向工程操作对象就是这一类可执行文件,逆向即通过机器代码反推程序的原理。机器码毫无可读性,可执行文件编排也十分复杂,所以需要一款强大的辅助软件辅助逆向分析过程,使分析者可以专注于代码本身的逻辑层面。
IDA 生成高级语言代码的流程:

机器代码 -> … -> 汇编代码 -> … -> 高级语言代码

IDA 定位 Main 函数
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第1张图片
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第2张图片
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第3张图片

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第4张图片
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第5张图片
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第6张图片
IDA 对符号重新命名
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第7张图片
这道题演示了如何使用 IDA 载入并分析一个可执行的二进制文件,并通过字符串定位的方式在茫茫的代码海洋中找到 main 函数,我们又使用 IDA 的伪代码功能生成 main 函数的伪代码,并修复没有名字的函数,使得程序可读性得到极大提升。

思路: 运行程序 -> 收集字符串 -> 寻找字符串引用代码 -> 生成伪代码 -> 修复匿名函数 -> 分析程序逻辑 -> 得到 Flag

视频
aHR0cHM6Ly93d3cuYmlsaWJpbGkuY29tL3ZpZGVvL0JWMTdMNDExczdHWC8=
课件
aHR0cHM6Ly9naXRodWIuY29tL1N5Y2xvdmVyVGVhbS9TeWNSZXZMZWFybg==

第2题:简单的加密算法

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  size_t i; // [esp+D0h] [ebp-114h]
  char Str1[260]; // [esp+DCh] [ebp-108h] BYREF

  printf("Hi CTFer,Input your flag:");
  scanf("%s", Str1);
  for ( i = 0; i < j__strlen(Str1); ++i )
    ++Str1[i];
  if ( !j__strcmp(Str1, "gmbh|ZPV`GJOE`JU`IBIB~") )
    printf("you are right!\n");
  else
    printf("you are wrong!\n");
  return 0;
}

结果

target = bytearray(b'gmbh|ZPV`GJOE`JU`IBIB~')

for x in range(len(target)):
    target[x] -=1
print(target.decode())

flag{YOU_FIND_IT_HAHA}

第3题:简单的加密算法2

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  size_t i; // [esp+D0h] [ebp-114h]
  char v5[260]; // [esp+DCh] [ebp-108h] BYREF

  sub_456502("[5] Hi CTFer,Input your flag:");
  sub_4554EF("%s", v5);
  for ( i = 0; i < j__strlen(v5); ++i )
    v5[i] ^= i;
  if ( !j__strcmp(v5, Str2) )
    sub_456502("you are right!\n");
  else
    sub_456502("you are wrong!\n");
  return 0;
}

结果

data = [0x66, 0x6D, 0x63, 0x64, 0x7F, 0x5C, 0x49, 0x52, 0x57, 0x4F, 0x43, 0x45, 0x48, 0x52, 0x47, 0x5B, 0x4F, 0x59, 0x53, 0x5B, 0x55, 0x68]
for i in range(len(data)):
   data[i] ^= i
print(bytes(data).decode())

flag{YOU_FIND_IT_HAHA}

第4题:Base64 编码逆向

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  size_t v3; // eax
  size_t i; // [esp+190h] [ebp-31Ch]
  char Str1[520]; // [esp+19Ch] [ebp-310h] BYREF
  char Str[260]; // [esp+3A4h] [ebp-108h] BYREF

  printf("[4] Hi CTFer,Input your flag:");
  scanf("%s", Str);
  for ( i = 0; i < j__strlen(Str); ++i )
    Str[i] ^= i;
  v3 = j__strlen(Str);
  sub_455A94(Str, Str1, v3);
  if ( !j__strcmp(Str1, "Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA==") )
    printf("you are right!\n");
  else
    printf("you are wrong!\n");
  return 0;
}

结果

import base64

data = base64.b64decode("Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA==")
data = bytearray(data)
for x in range(len(data)):
    data[x] ^= x
print(data.decode())

flag{YOU_FIND_IT_HAHA}

第5题:Base64 变表逆向

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  size_t input_len; // eax
  size_t i; // [esp+190h] [ebp-31Ch]
  char Str1[520]; // [esp+19Ch] [ebp-310h] BYREF
  char input[260]; // [esp+3A4h] [ebp-108h] BYREF

  printf("[4] Hi CTFer,Input your flag:");
  scanf("%s", input);
  for ( i = 0; i < j__strlen(input); ++i )
    input[i] ^= i;
  input_len = j__strlen(input);
  sub_455A94(input, Str1, input_len);
  if ( !j__strcmp(Str1, "Wj1gWE9xPSGUQ0KCPCGET09WR1qSzZ==") )
    printf("you are right!\n");
  else
    printf("you are wrong!\n");
  return 0;
}

进 sub_455A94() 函数,找下算法特征,推断是 BASE64 算法
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第8张图片

标准编码表(T4)

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

魔改之后的编码表(T5)

ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/

所以导致不对

  • base64 编码原理 类似于进制转换,256 -> 64 进制
  • 数据取每 6 bit -> v (0- 64) -> 从编码表中取出下标 v 对应的字符

在这里插入图片描述

分析思路

Wj1gWE9xPSGUQ0KCPCGET09WR1qSzZ==

W 在 T5 中的位置对应到 T4 则是 Z

j 在 T5 中的位置对应到 T4 则是 m

因此将把 Wj 替换成 Zm,后续也是同样的操作,最后用 T4 的脚本即可解密 Flag

结果

import base64

data = 'Wj1gWE9xPSGUQ0KCPCGET09WR1qSzZ' # 去掉 == 
T4 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
T5 = 'ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/'
result = ""
for ch in data:
    result += T4[T5.index(ch)]

result = bytearray(base64.b64decode(result + "=="))
for i in range(len(result)):
    result[i] ^= i
print(result.decode())

flag{YOU_FIND_IT_HAHA}

C语言:base64 算法

int __cdecl sub_45A3F0(int a1, int a2, int a3)
{
  unsigned __int8 v4; // [esp+D3h] [ebp-1Dh]
  unsigned __int8 v5; // [esp+D3h] [ebp-1Dh]
  int v6; // [esp+DCh] [ebp-14h]
  int v7; // [esp+DCh] [ebp-14h]
  int v8; // [esp+DCh] [ebp-14h]
  int v9; // [esp+DCh] [ebp-14h]
  int v10; // [esp+DCh] [ebp-14h]
  int v11; // [esp+DCh] [ebp-14h]
  int v12; // [esp+E8h] [ebp-8h]

  v12 = 0;
  v6 = 0;
  while ( v12 < a3 )
  {
    *(_BYTE *)(v6 + a2) = *((_BYTE *)off_529000 + (((int)*(unsigned __int8 *)(v12 + a1) >> 2) & 0x3F));
    v7 = v6 + 1;
    v4 = (16 * *(_BYTE *)(v12 + a1)) & 0x30;
    if ( v12 + 1 >= a3 )
    {
      *(_BYTE *)(v7 + a2) = *((_BYTE *)off_529000 + v4);
      v8 = v7 + 1;
      *(_BYTE *)(v8 + a2) = 61;
      *(_BYTE *)(++v8 + a2) = 61;
      v6 = v8 + 1;
      break;
    }
    *(_BYTE *)(v7 + a2) = *((_BYTE *)off_529000 + (((int)*(unsigned __int8 *)(v12 + a1 + 1) >> 4) & 0xF | v4));
    v9 = v7 + 1;
    v5 = (4 * *(_BYTE *)(v12 + a1 + 1)) & 0x3C;
    if ( v12 + 2 >= a3 )
    {
      *(_BYTE *)(v9 + a2) = *((_BYTE *)off_529000 + v5);
      v10 = v9 + 1;
      *(_BYTE *)(v10 + a2) = 61;
      v6 = v10 + 1;
      break;
    }
    *(_BYTE *)(v9 + a2) = *((_BYTE *)off_529000 + (((int)*(unsigned __int8 *)(v12 + a1 + 2) >> 6) & 3 | v5));
    v11 = v9 + 1;
    *(_BYTE *)(v11 + a2) = *((_BYTE *)off_529000 + (*(_BYTE *)(v12 + a1 + 2) & 0x3F));
    v6 = v11 + 1;
    v12 += 3;
  }
  *(_BYTE *)(v6 + a2) = 0;
  return a2;
}

第6题:IDA 动态调试

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  size_t i; // [esp+190h] [ebp-340h]
  char input[264]; // [esp+3A4h] [ebp-12Ch] BYREF
  char str1[32]; // [esp+4ACh] [ebp-24h] BYREF

  qmemcpy(str1, &unk_50DE50, 034u);
  printf("[6] Hi CTFer,Input your flag:");
  scanf("%s", input);
  for ( i = 0; i < j__strlen(str1); ++i )
    str1[i] = ((i + 1) ^ str1[i]) - i;
  if ( !j__strcmp(str1, input) )
    printf("you are right\n");
  else
    printf("you are wrong\n");
  return 0;
}

动态调试前要确定调试的目的

1、获取运行时数据
2、获取执行流程
3、验证猜想是否正确

例如要获取数据,就需要提前设置断点。 IDA 支持在伪代码层面调试,所以可以直接在 IDA 的伪代码里面设置断点。
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第9张图片
点击小蓝点之后,这行代码就会变成红色,就代表成功设置断点。

Windows本地exe - 动态调试

首次使用(需先配置)
在这里插入图片描述

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第10张图片
选第3个【Local Windows debugger】

配置完成后,去启动
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第11张图片
如果【安全提醒】 在CTF题目情况下,都是安全的,默认信任。未知的程序,最好去虚拟机里调试。
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第12张图片

此时断点

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第13张图片

告诉 ida 这是字符串(按键:A)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第14张图片
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第15张图片
拿到

Flag{This_IS_T7_DEBUG_EASY}

第7题:IDA 动态调试解RC4(绕过反调试)

有一些算法的加密与解密是相同的算法过程,例如 RC4、部分简单的异或算法等。

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  int j; // [esp+31Ch] [ebp-69Ch]
  int i; // [esp+328h] [ebp-690h]
  size_t v6; // [esp+334h] [ebp-684h]
  char v7[55]; // [esp+340h] [ebp-678h]
  char v8; // [esp+377h] [ebp-641h]
  unsigned int v9; // [esp+380h] [ebp-638h]
  int v10; // [esp+38Ch] [ebp-62Ch]
  char Str[520]; // [esp+398h] [ebp-620h] BYREF
  char v12[264]; // [esp+5A0h] [ebp-418h] BYREF
  char v13[264]; // [esp+6A8h] [ebp-310h] BYREF
  char v14[516]; // [esp+7B0h] [ebp-208h] BYREF

  __CheckForDebuggerJustMyCode(&unk_49C00F);
  j__memset(v14, 0, 0x200u);
  j__memset(v13, 0, 0x100u);
  j__memset(v12, 0, 0x100u);
  j__memset(Str, 0, 0x200u);
  v10 = 0;
  v9 = 0;
  v7[0] = -28;
  v7[1] = 21;
  v7[2] = -60;
  v7[3] = -19;
  v7[4] = -90;
  v7[5] = 47;
  v7[6] = 86;
  v7[7] = 16;
  v7[8] = -69;
  v7[9] = 19;
  v7[10] = -21;
  v7[11] = -83;
  v7[12] = 117;
  v7[13] = 86;
  v7[14] = -57;
  v7[15] = -69;
  v7[16] = -69;
  v7[17] = -23;
  v7[18] = -71;
  v7[19] = -52;
  v7[20] = 2;
  v7[21] = 58;
  v7[22] = 80;
  v7[23] = -97;
  v7[24] = 54;
  v7[25] = -112;
  v7[26] = 105;
  v7[27] = -66;
  v7[28] = 124;
  v7[29] = 66;
  v7[30] = 68;
  v7[31] = -54;
  v7[32] = -58;
  v7[33] = -44;
  v7[34] = 36;
  v7[35] = 92;
  v7[36] = -46;
  v7[37] = -71;
  v7[38] = 36;
  v7[39] = -63;
  v7[40] = 24;
  v7[41] = -109;
  v7[42] = -77;
  v7[43] = -22;
  sub_42F057(v14);
  sub_42C40B("Welcome!! give me your flag:\n");
  do
  {
    v8 = j_j_j___fgetchar();
    if ( v8 == 10 )
      break;
    Str[v9++] = v8;
  }
  while ( (int)v9 < 44 );
  if ( v9 >= 0x200 )
    j____report_rangecheckfailure();
  Str[v9] = 0;
  v6 = j__strlen(Str);
  sub_42CEFB(v13, v14, v6);
  for ( i = 0; i < 256; ++i )
    v12[i] = v13[i];
  sub_42D5B8(v13, Str, v6);
  for ( j = 0; j < 44; ++j )
  {
    if ( v7[j] == Str[j] )
      ++v10;
  }
  if ( v10 == 44 )
    sub_42C40B("Yes, u right!\n");
  else
    sub_42C40B("no no no\n");
  sub_42D0CC("pause");
  return 0;
}

改了函数名

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  int j; // [esp+31Ch] [ebp-69Ch]
  int i; // [esp+328h] [ebp-690h]
  size_t input_len; // [esp+334h] [ebp-684h]
  char v7[55]; // [esp+340h] [ebp-678h]
  char v8; // [esp+377h] [ebp-641h]
  unsigned int index; // [esp+380h] [ebp-638h]
  int v10; // [esp+38Ch] [ebp-62Ch]
  char input[520]; // [esp+398h] [ebp-620h] BYREF
  char v12[264]; // [esp+5A0h] [ebp-418h] BYREF
  char v13[264]; // [esp+6A8h] [ebp-310h] BYREF
  char v14[516]; // [esp+7B0h] [ebp-208h] BYREF

  __CheckForDebuggerJustMyCode(&unk_50C00F);
  j__memset(v14, 0, 0x200u);
  j__memset(v13, 0, 0x100u);
  j__memset(v12, 0, 0x100u);
  j__memset(input, 0, 0x200u);
  v10 = 0;
  index = 0;
  v7[0] = -28;
  v7[1] = 21;
  v7[2] = -60;
  v7[3] = -19;
  v7[4] = -90;
  v7[5] = 47;
  v7[6] = 86;
  v7[7] = 16;
  v7[8] = -69;
  v7[9] = 19;
  v7[10] = -21;
  v7[11] = -83;
  v7[12] = 117;
  v7[13] = 86;
  v7[14] = -57;
  v7[15] = -69;
  v7[16] = -69;
  v7[17] = -23;
  v7[18] = -71;
  v7[19] = -52;
  v7[20] = 2;
  v7[21] = 58;
  v7[22] = 80;
  v7[23] = -97;
  v7[24] = 54;
  v7[25] = -112;
  v7[26] = 105;
  v7[27] = -66;
  v7[28] = 124;
  v7[29] = 66;
  v7[30] = 68;
  v7[31] = -54;
  v7[32] = -58;
  v7[33] = -44;
  v7[34] = 36;
  v7[35] = 92;
  v7[36] = -46;
  v7[37] = -71;
  v7[38] = 36;
  v7[39] = -63;
  v7[40] = 24;
  v7[41] = -109;
  v7[42] = -77;
  v7[43] = -22;
  sub_49F057((int)v14);                         // 反调试
  sub_49C40B("Welcome!! give me your flag:\n");
  do
  {
    v8 = j_j_j___fgetchar();
    if ( v8 == '\n' )
      break;
    input[index++] = v8;
  }
  while ( (int)index < 44 );
  if ( index >= 0x200 )
    j____report_rangecheckfailure();
  input[index] = 0;
  input_len = j__strlen(input);
  fun1(v13, v14, input_len);
  for ( i = 0; i < 256; ++i )
    v12[i] = v13[i];
  sub_49D5B8(v13, input, input_len);            // 推测核心算法,可理解为黑盒,有输入输出的地方
  for ( j = 0; j < 44; ++j )
  {
    if ( v7[j] == input[j] )
      ++v10;
  }
  if ( v10 == 44 )
    sub_49C40B("Yes, u right!\n");
  else
    sub_49C40B("no no no\n");
  sub_49D0CC("pause");
  return 0;
}

反调试逻辑

_BYTE *__cdecl sub_4325D0(_BYTE *a1)
{
  HWND ForegroundWindow; // eax
  unsigned __int64 v2; // rax
  unsigned __int64 v3; // rax
  int v5; // [esp+25Ch] [ebp-438h]
  CHAR String[1028]; // [esp+28Ch] [ebp-408h] BYREF

  if ( IsDebuggerPresent() )
    *a1 = 89;
  else
    *a1 = 88;
  ForegroundWindow = GetForegroundWindow();
  GetWindowTextA(ForegroundWindow, String, 1023);
  if ( j__strstr(String, "WinDbg")
    || j__strstr(String, "x64dbg")
    || j__strstr(String, "x32dbg")
    || j__strstr(String, "OllyICE")
    || j__strstr(String, "OllyDBG")
    || j__strstr(String, "Immunity") )
  {
    a1[1] = 111;
  }
  else
  {
    a1[1] = 48;
  }
  SetLastError((DWORD)"12345");
  OutputDebugStringW("Test for debugger!");
  if ( (char *)GetLastError() == "12345" )
    a1[2] = 110;
  else
    a1[2] = 117;
  if ( !CloseHandle((HANDLE)0x1234) && GetLastError() == 6 )
    a1[3] = 66;
  else
    a1[3] = 65;
  if ( NtCurrentPeb()->BeingDebugged )
    a1[4] = 97;
  else
    a1[4] = 64;
  v2 = __rdtsc();
  v5 = v2;
  v3 = __rdtsc();
  if ( (unsigned int)(v3 - v5) >= 0xFF )
    a1[5] = 100;
  else
    a1[5] = 68;
  a1[6] = 0;
  return a1;
}

Windows本地exe - 附加进程

本地先打开【rc4.exe】程序,然后点击
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第16张图片
将会已附加的形式,去调试一个正在运行的进程

此时会停下来,点击 继续运行

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第17张图片

双击 input 变量

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第18张图片

此时可以看到输入的字符串

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第19张图片
请注意:在 c 语言中 ,字符串的结尾是 0

伪造 44 位字符串【AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA】

重新调试

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第20张图片
双击 input
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第21张图片
找到内存地址【00B6F89C】记录下来

单步一下,此时,函数内部已经算好
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第22张图片

查看内存(按键:G)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第23张图片

选中 44 位,已 0 结尾
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第24张图片
到 16进制【F60DC6D7B7046F0E890DFD835924E8A599C4C8F92B127FB928E05BA06E336AE4B7FA5542F08D11E578E39BD6】

得出结论

内存地址
00B6F89C
输入值
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 
结果
F60DC6D7B7046F0E890DFD835924E8A599C4C8F92B127FB928E05BA06E336AE4B7FA5542F08D11E578E39BD6

再次重新调试

  1. 重新运行程序
  2. 附加进程
  3. 断住

修改内存数据

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第25张图片

然后
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第26张图片

记录下

内存地址
0046F2C8

步过(按键:F8)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第27张图片
在修改内存后,发现是一样的数据。

然后提取 v7 的值,双击进去,复制hex
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第28张图片

得到结果【E415C4EDA62F5610BB13EBAD7556C7BBBBE9B9CC023A509F369069BE7C4244CAC6D4245CD2B924C11893B3EA】

再次重新调试
附加进程,输入44位字符串,并修改内存

记录

内存地址
0065F7AC

看到结果了,双击进去
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第29张图片

转字符串(按键:A)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第30张图片
结果

SYC{Pjx_s_Wom3n_cl0thing_1s_S0oo0o0_cute!1i}

第8题:IDA 代码修复 & 数组识别

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-BCh]
  char v5; // [esp+0h] [ebp-BCh]
  int v6; // [esp+0h] [ebp-BCh]
  HANDLE TimerQueue; // [esp+4h] [ebp-B8h]
  int v8; // [esp+8h] [ebp-B4h]
  HANDLE phNewTimer; // [esp+18h] [ebp-A4h] BYREF
  char v10[52]; // [esp+1Ch] [ebp-A0h] BYREF
  char Destination[52]; // [esp+50h] [ebp-6Ch] BYREF
  char Str[5]; // [esp+84h] [ebp-38h] BYREF
  char Source[47]; // [esp+89h] [ebp-33h] BYREF

  TimerQueue = CreateTimerQueue();
  CreateTimerQueueTimer(&phNewTimer, TimerQueue, Callback, 0, 0, 0x3E8u, 0x20u);
  sub_401450("Welcome To XDU\n", v4);
  sub_401450("Show me your password\n", v5);
  sub_4013C0("%s", (char)Str);
  if ( sub_401000(Str, "flag{") != (_DWORD)Str || Source[32] != 125 )
    sub_401340(v6, TimerQueue);
  sub_401360(Destination, Source, 0x20u);
  sub_4011F0(Destination);
  sub_401270(&unk_4040C0, Destination, v10);
  v8 = strcmp(v10, a23gjf13au98hk3);
  if ( v8 )
    v8 = v8 < 0 ? -1 : 1;
  if ( v8 )
    sub_401340(v8, TimerQueue);
  sub_401450("Congratulations!!!\n", 0);
  DeleteTimerQueueEx(TimerQueue, (HANDLE)0xFFFFFFFF);
  return 0;
}

代码修复

  1. 判断返回值是否有意义
  2. 判断参数的类型
  3. 判断局部变量的类型与数组大小

随便看个函数。交叉引用,查看只有1处调用

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第31张图片

char __cdecl sub_4011F0(char *a1)
{
  char result; // al
  char *v2; // [esp+4h] [ebp-14h]
  char *v3; // [esp+Ch] [ebp-Ch]
  int i; // [esp+10h] [ebp-8h]

  v3 = a1;
  v2 = a1 + 1;
  do
    result = *v3;
  while ( *v3++ );
  if ( v3 - v2 != 32 )
    sub_401340(v3 - v2, v2);
  for ( i = 0; i < 32; ++i )
  {
    a1[i] ^= LOBYTE(dword_404040[i]);
    result = i + 1;
  }
  return result;
}

但是 ida 认为返回是 char 类型,需要 手动改下。

改成无返回类型(按键:V)

点击函数名,右键
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第32张图片

改成【void】代表无返回

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第33张图片

也可以点击函数名,按 V 键

void __cdecl sub_4011F0(char *a1)
{
  char *v1; // [esp+Ch] [ebp-Ch]
  int i; // [esp+10h] [ebp-8h]

  v1 = &a1[strlen(a1) + 1];
  if ( v1 - (a1 + 1) != 32 )
    sub_401340(v1 - (a1 + 1), a1 + 1);
  for ( i = 0; i < 32; ++i )
    a1[i] ^= LOBYTE(dword_404040[i]);
}

此时,发现减少了很多代码,逻辑就很清晰了。

当然也会有问题,进 sub_401340() 函数,发现 入参缺失了

修复入参类型

void __noreturn sub_401340()
{
  char savedregs; // [esp+0h] [ebp+0h]

  sub_401450("Emmmm...Wrong\n", savedregs);
  exit(0);
}

只需要:进入该函数,按 F5 键 ,再 Esc 键,再按 F5 键,即可恢复

这样更少了

void __cdecl sub_4011F0(char *a1)
{
  int i; // [esp+10h] [ebp-8h]

  if ( strlen(a1) != 32 )
    sub_401340();
  for ( i = 0; i < 32; ++i )
    a1[i] ^= LOBYTE(dword_404040[i]);
}

修改入参类型,为指针

再来找个函数,改改。

int __cdecl sub_401270(int a1, const char *a2, int a3)
{
  signed int v3; // kr00_4
  int result; // eax
  signed int v5; // [esp+10h] [ebp-8h]

  v5 = 0;
  v3 = strlen(a2);
  while ( v5 < v3 )
  {
    *(_BYTE *)(a3 + *(_DWORD *)(a1 + 4 * v5)) = a2[v5];
    ++v5;
  }
  result = v5 + a3;
  *(_BYTE *)(v5 + a3) = 0;
  return result;
}

点击函数名,按 V 键

void __cdecl sub_401270(int a1, const char *a2, int a3)
{
  signed int v3; // kr00_4
  signed int v4; // [esp+10h] [ebp-8h]

  v4 = 0;
  v3 = strlen(a2);
  while ( v4 < v3 )
  {
    *(_BYTE *)(a3 + *(_DWORD *)(a1 + 4 * v4)) = a2[v4];
    ++v4;
  }
  *(_BYTE *)(v4 + a3) = 0;
}

发现这样不显著,当然也不是万能的

回到主函数

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第34张图片

观察第3个应该是 指针,跟进去修改

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第35张图片
然后

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第36张图片

void __cdecl sub_401270(int a1, const char *a2, char *a3)
{
  signed int v3; // kr00_4
  signed int v4; // [esp+10h] [ebp-8h]

  v4 = 0;
  v3 = strlen(a2);
  while ( v4 < v3 )
  {
    a3[*(_DWORD *)(a1 + 4 * v4)] = a2[v4];
    ++v4;
  }
  a3[v4] = 0;
}

再来观察

sub_401270(&unk_4040C0, Destination, v9);

推断 第一个是 取值,也是指针

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第37张图片

void __cdecl sub_401270(int a1, const char *a2, char *a3)
{
  signed int v3; // kr00_4
  signed int i; // [esp+10h] [ebp-8h]

  i = 0;
  v3 = strlen(a2);
  while ( i < v3 )
  {
    a3[*(_DWORD *)(a1 + 4 * i)] = a2[i];
    ++i;
  }
  a3[i] = 0;
}

一个无符号整型数据占4个字节

a1 + 4 * i = 整数数组寻址:1个元素4字节,第i个元素偏移量,就是数组首地址 + 4 * i 

第1个入参改为【int *】类型

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第38张图片
这样代码就清晰了,很漂亮

void __cdecl sub_401270(int *a1, const char *a2, char *a3)
{
  signed int v3; // kr00_4
  signed int i; // [esp+10h] [ebp-8h]

  i = 0;
  v3 = strlen(a2);
  while ( i < v3 )
  {
    a3[a1[i]] = a2[i];
    ++i;
  }
  a3[i] = 0;
}

切回主函数,继续观察

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第39张图片

修改字符的存储长度(按键:Y)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第40张图片

此时,主函数的逻辑更清晰了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-BCh]
  char v5; // [esp+0h] [ebp-BCh]
  HANDLE TimerQueue; // [esp+4h] [ebp-B8h]
  int v7; // [esp+8h] [ebp-B4h]
  HANDLE phNewTimer; // [esp+18h] [ebp-A4h] BYREF
  char a3[52]; // [esp+1Ch] [ebp-A0h] BYREF
  char xxxx[52]; // [esp+50h] [ebp-6Ch] BYREF
  char Str[52]; // [esp+84h] [ebp-38h] BYREF

  TimerQueue = CreateTimerQueue();
  CreateTimerQueueTimer(&phNewTimer, TimerQueue, Callback, 0, 0, 0x3E8u, 0x20u);
  sub_401450("Welcome To XDU\n", v4);
  sub_401450("Show me your password\n", v5);
  scanf("%s", (char)Str);
  if ( strstr_0(Str, "flag{") != Str || Str[37] != 125 )
    sub_401340();
  get_flag_body(xxxx, &Str[5], 0x20u);
  sub_4011F0(xxxx);
  sub_401270(a1, xxxx, a3);
  v7 = strcmp(a3, a23gjf13au98hk3);
  if ( v7 )
    v7 = v7 < 0 ? -1 : 1;
  if ( v7 )
    sub_401340();
  sub_401450("Congratulations!!!\n", 0);
  DeleteTimerQueueEx(TimerQueue, (HANDLE)0xFFFFFFFF);
  return 0;
}

先拿 a1 的值

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第41张图片
因为是 4个字节访问的

来到

void __cdecl sub_4011F0(char *a1)
{
  int i; // [esp+10h] [ebp-8h]

  if ( strlen(a1) != 32 )
    sub_401340();
  for ( i = 0; i < 32; ++i )
    a1[i] ^= LOBYTE(dword_404040[i]);
}

因为是 低位 表现的

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第42张图片
修改
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第43张图片
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第44张图片

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第45张图片

然后

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第46张图片


a1 = [0x00000004, 0x0000000F, 0x0000000B, 0x0000001E, 0x0000000E, 0x00000014, 0x0000001F, 0x00000009, 0x00000017, 0x00000002, 0x00000019, 0x0000001C, 0x00000012, 0x00000010, 0x00000000, 0x00000008, 0x00000011, 0x00000001, 0x00000015, 0x00000003, 0x0000000A, 0x0000001D, 0x0000000C, 0x00000016, 0x00000018, 0x0000000D, 0x0000001B, 0x00000005, 0x00000007, 0x00000006, 0x00000013, 0x0000001A]
target = bytearray(b'23gjf13au98hk3a1090zp8qjs41h39jp')

input_xxx = [0] * 32
for i in range(len(target)):
    input_xxx[i] = target[a1[i]]
    # input_xxx[0] = target[4]

print(input_xxx)

X = [0x00000053, 0x00000045, 0x0000005C, 0x0000001E, 0x00000050, 0x00000013, 0x0000002F, 0x00000078, 0x00000004, 0x00000053, 0x00000058, 0x0000004A, 0x00000043, 0x00000001, 0x00000041, 0x0000002A, 0x00000008, 0x00000040, 0x00000067, 0x0000002F, 0x0000000C, 0x0000004A, 0x00000012, 0x0000002E, 0x00000041, 0x0000006C, 0x00000005, 0x00000054, 0x00000040, 0x00000012, 0x0000005B, 0x0000004F]
for i in range(len(target)):
    input_xxx[i] ^= X[i]

a = bytes(input_xxx).decode()
print(a == '5t4t1c_An4lys1s_1s_E4sy_2_me!!!~')

结果

flag{5t4t1c_An4lys1s_1s_E4sy_2_me!!!~}

第9题:UPX脱壳(待完善)

去看视频。

IDA pro 反汇编程序,基础教程

查阅代码流程视图 / 汇编代码 (按键:空格)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第47张图片

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第48张图片

将常量转换为X进制(按键:右击)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第49张图片

Hexadecimal:十六进制
Octal:八进制
Char:字符(按键:R)

查看所有字符串(按键:shift + F12)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第50张图片

交叉引用(按键:X)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第51张图片

复制字节数组(ida插件)

  • 需要安装【LazyIDA】插件
    https://github.com/P4nda0s/LazyIDA.git

  • 将【LazyIDA.py】放到 ida_pro 主目录下的 【\plugins】目录。

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第52张图片

修改变量/函数名(按键:N)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第53张图片
《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第54张图片

调试技巧(步入/步过/直到返回/光标位置)

《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现_第55张图片

常用快捷键

切换文本视图与图表视图 空格键

返回上一个操作地址 ESC

搜索地址和符号 G

对符号进行重命名 N

常规注释 冒号键

可重复注释 分号键

添加标签 Alt+M

查看标签 Ctrl+M

查看段的信息 Ctrl+S

查看交叉应用 X

查看伪代码 F5

搜索文本 Alt+T

搜索十六进 Alt+B

常用快捷键2

a:将数据转换为字符串

f5:一键反汇编

esc:回退键,能够倒回上一部操作的视图(只有在反汇编窗口才是这个作用,如果是在其他窗口按下esc,会关闭该窗口)

shift+f12:可以打开string窗口,一键找出所有的字符串,右击setup,还能对窗口的属性进行设置

ctrl+w:保存ida数据库

ctrl+s:选择某个数据段,直接进行跳转

ctrl+鼠标滚轮:能够调节流程视图的大小

x:对着某个函数、变量按该快捷键,可以查看它的交叉引用

g:直接跳转到某个地址

n:更改变量的名称

y:更改变量的类型

/ :在反编译后伪代码的界面中写下注释

\:在反编译后伪代码的界面中隐藏/显示变量和函数的类型描述,有时候变量特别多的时候隐藏掉类型描述看起来会轻松很多

;:在反汇编后的界面中写下注释

ctrl+shift+w:拍摄IDA快照

u:undefine,取消定义函数、代码、数据的定义

动态调试快捷键

F2:下断点

F3:打开程序

F4:运行到当前光标处(可应用在跳出 循坏)

F7:单步步入(进函数)

F8:单步 步过

F9;运行

F10:打开反汇编选项菜单快捷键

F12:暂时停止

Ctrl+F2:重新开始

Art+F2:结束跟踪

Shift+F2:打开附加选项窗口

Shift+F4:打开条件对话窗

Shift+F7:与F7相同,但是如果被调试程序发生异常而中止,调试器会首先尝试步入被调试程序指定的异常处理

Ctrl+F7:自动步入,在所有的函数调用中一条一条地执行命令,断点或异常时,自动 停止

Shift+F8与F8相同,但是如果被调试程序发生异常而中止,调试器会首先尝试步过被调试程序指定的异常处理

Ctrl+F8:自动步过,一条一条的执行命令,程序到达断点,或者发生异常时,自动步过过程都会停止

Shift+F9:与F9相同,但是如果被调试程序发生异常而中止,调试器会首先尝试执行被调试程序指定的异常处理

Ctrl+F9:执行直到返回,跟踪程序直到遇到返回,在此期间不进入子函数也不更新CPU数据。因为程序是一条一条命令执行的,所以速度可能会慢一些。按Esc键,可以停止跟踪。

Alt+F9:执行直到返回到用户代码段,跟踪程序直到指令所属于的模块不在系统目录中,在此期间不进入子函数也不更新CPU数据。按Esc键,可以停止跟踪。

Ctrl+F11:Run跟踪步入,一条一条执行命令,进入每个子函数调用,并把寄存器的信息加入到Run跟踪的存储数据中。Run跟踪不会同步更新CPU窗口。

Ctrl+F12 :Run跟踪。步过,一条一条执行命令,但是不进入子函数调用,并把寄存器的信息加入到Run跟踪的存储数据中。Run跟踪不会同步更新CPU窗口。

Art+C:快速回到主界面

Alt+B:显示断点窗口

Alt+E:显示模块窗口

Art+L:显示记录窗口

Alt+M:显示内存窗口

Alt+O:显示调试选项窗口

Alt+K:显示呼叫堆栈

Ctrl+E:编辑机器码

Ctrl+G:输入跟随地址

Ctrl+N:查找名称标志,选择你要下断的内容

Ctrl+S:打开查找命令次序窗口

Ctrl+P:显示补丁窗口

Ctrl+F9:返回到跟踪

Ctrl+F8:自动步进扫描,按F12可停止

Ctrl+F7:同上,功能略有不同

Ctrl+F6:回到OL主窗口

你可能感兴趣的:(python,算法,c语言,python)