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

这篇具有很好参考价值的文章主要介绍了《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

二进制安全,能干什么

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

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

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

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

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

逆向与开发的对比

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

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

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

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

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

IDA 定位 Main 函数
ida pro获取伪代码,python,算法,c语言,python
ida pro获取伪代码,python,算法,c语言,python
ida pro获取伪代码,python,算法,c语言,python

ida pro获取伪代码,python,算法,c语言,python
ida pro获取伪代码,python,算法,c语言,python
ida pro获取伪代码,python,算法,c语言,python
IDA 对符号重新命名
ida pro获取伪代码,python,算法,c语言,python
这道题演示了如何使用 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 算法
ida pro获取伪代码,python,算法,c语言,python

标准编码表(T4)

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

魔改之后的编码表(T5)

ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/

所以导致不对

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

ida pro获取伪代码,python,算法,c语言,python

分析思路

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 的伪代码里面设置断点。
ida pro获取伪代码,python,算法,c语言,python
点击小蓝点之后,这行代码就会变成红色,就代表成功设置断点。

Windows本地exe - 动态调试

首次使用(需先配置)
ida pro获取伪代码,python,算法,c语言,python

ida pro获取伪代码,python,算法,c语言,python
选第3个【Local Windows debugger】

配置完成后,去启动
ida pro获取伪代码,python,算法,c语言,python
如果【安全提醒】 在CTF题目情况下,都是安全的,默认信任。未知的程序,最好去虚拟机里调试。
ida pro获取伪代码,python,算法,c语言,python

此时断点

ida pro获取伪代码,python,算法,c语言,python

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

ida pro获取伪代码,python,算法,c语言,python
ida pro获取伪代码,python,算法,c语言,python
拿到

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】程序,然后点击
ida pro获取伪代码,python,算法,c语言,python
将会已附加的形式,去调试一个正在运行的进程

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

ida pro获取伪代码,python,算法,c语言,python

双击 input 变量

ida pro获取伪代码,python,算法,c语言,python

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

ida pro获取伪代码,python,算法,c语言,python
请注意:在 c 语言中 ,字符串的结尾是 0

伪造 44 位字符串【AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA】

重新调试

ida pro获取伪代码,python,算法,c语言,python
双击 input
ida pro获取伪代码,python,算法,c语言,python
找到内存地址【00B6F89C】记录下来

单步一下,此时,函数内部已经算好
ida pro获取伪代码,python,算法,c语言,python

查看内存(按键:G)

ida pro获取伪代码,python,算法,c语言,python

选中 44 位,已 0 结尾
ida pro获取伪代码,python,算法,c语言,python
到 16进制【F60DC6D7B7046F0E890DFD835924E8A599C4C8F92B127FB928E05BA06E336AE4B7FA5542F08D11E578E39BD6】

得出结论

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

再次重新调试

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

修改内存数据

ida pro获取伪代码,python,算法,c语言,python

然后
ida pro获取伪代码,python,算法,c语言,python

记录下

内存地址
0046F2C8

步过(按键:F8)

ida pro获取伪代码,python,算法,c语言,python
在修改内存后,发现是一样的数据。

然后提取 v7 的值,双击进去,复制hex
ida pro获取伪代码,python,算法,c语言,python

得到结果【E415C4EDA62F5610BB13EBAD7556C7BBBBE9B9CC023A509F369069BE7C4244CAC6D4245CD2B924C11893B3EA】

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

记录

内存地址
0065F7AC

看到结果了,双击进去
ida pro获取伪代码,python,算法,c语言,python

转字符串(按键:A)

ida pro获取伪代码,python,算法,c语言,python
结果

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处调用

ida pro获取伪代码,python,算法,c语言,python

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)

点击函数名,右键
ida pro获取伪代码,python,算法,c语言,python

改成【void】代表无返回

ida pro获取伪代码,python,算法,c语言,python

也可以点击函数名,按 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;
}

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

回到主函数

ida pro获取伪代码,python,算法,c语言,python

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

ida pro获取伪代码,python,算法,c语言,python
然后

ida pro获取伪代码,python,算法,c语言,python

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);

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

ida pro获取伪代码,python,算法,c语言,python

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 *】类型

ida pro获取伪代码,python,算法,c语言,python
这样代码就清晰了,很漂亮

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;
}

切回主函数,继续观察

ida pro获取伪代码,python,算法,c语言,python

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

ida pro获取伪代码,python,算法,c语言,python

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

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 的值

ida pro获取伪代码,python,算法,c语言,python
因为是 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]);
}

因为是 低位 表现的

ida pro获取伪代码,python,算法,c语言,python
修改
ida pro获取伪代码,python,算法,c语言,python
ida pro获取伪代码,python,算法,c语言,python

ida pro获取伪代码,python,算法,c语言,python

然后

ida pro获取伪代码,python,算法,c语言,python


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 反汇编程序,基础教程

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

ida pro获取伪代码,python,算法,c语言,python

ida pro获取伪代码,python,算法,c语言,python

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

ida pro获取伪代码,python,算法,c语言,python

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

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

ida pro获取伪代码,python,算法,c语言,python

交叉引用(按键:X)

ida pro获取伪代码,python,算法,c语言,python

复制字节数组(ida插件)

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

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

ida pro获取伪代码,python,算法,c语言,python

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

ida pro获取伪代码,python,算法,c语言,python
ida pro获取伪代码,python,算法,c语言,python

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

ida pro获取伪代码,python,算法,c语言,python文章来源地址https://www.toymoban.com/news/detail-816168.html

常用快捷键

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

返回上一个操作地址 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主窗口

到了这里,关于《算法还原 - CTF》逆向exe程序 + ida Pro 反汇编分析伪C代码 + python算法复现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 最新PDD商家端Anti-Content参数逆向分析与纯算法还原

    【🏠作者主页】: 吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作! 【🌟作者推荐】:对爬虫领域以及JS逆向分析感兴趣的朋友可以关

    2024年04月09日
    浏览(29)
  • 恶意代码分析实战 3 IDA Pro

    利用IDA PRO分析Lab05-01.dll 实验目的 利用IDA Pro分析Lab05-01.dll中发现的恶意代码,回答以下问题: DLLMain的地址是什么? 可以空格转入反汇编查看DLLMain地址,或者 DLLMain的地址是.text:0x1000D02E。 使用Imports窗口并浏览到gethostbyname,导入函数定位到什么地址? 这样的三步来寻找地址

    2024年02月12日
    浏览(37)
  • 【软件逆向】如何逆向Unity3D+il2cpp开发的安卓app【IDA Pro+il2CppDumper+DnSpy+AndroidKiller】

    课程作业要求使用反编译技术,在游戏中实现无碰撞。正常情况下碰撞后角色死亡,修改为直接穿过物体不死亡。 il2CppDumper。 DnSpy。 IDA Pro。 AndroidKiller。 一、使用il2CppDumper导出程序集 将{my_game}.apk后缀修改为{my_game}.zip,使用解压工具解压至文件夹{my_game}。(my_game为apk的文件

    2024年02月05日
    浏览(32)
  • 恶意代码分析实战--IDA pro的使用及课后练习l

    函数窗口 Functions window  位于左半部分 列举可执行文件中的所有函数,可以在众多函数中过滤出想要的函数。这个窗口也对每一个函数关联了一些标志(F L S等),这其中最有用的是L,指明是库函数。 字符串窗口 Strings window (Shift + F12) 显示所有的字符串,可以右键Setup来修

    2024年02月05日
    浏览(32)
  • python逆向还原dnspy反编译的C#算法

    dnspy反编译中的代码如下:

    2024年02月10日
    浏览(32)
  • jsvmp逆向实战x-s、x-t算法还原

    什么是jsvmp jsvmp就是将js源代码首先编译为自定义的字节码,只有对应的解释器才能执行这种字节码,这是一种前端代码虚拟化保护技术。 整体架构流程是服务器端通过对JavaScript代码词法分析 - 语法分析 - 语法树-生成AST-生成私有指令-生成对应私有解释器,将私有指令加密与

    2024年02月06日
    浏览(30)
  • 【软件逆向-分析工具】反汇编和反编译工具

    目录 一、IDA 1.1、简介: 1.2、使用方法: (1)IDA打开文件 (2)IDA主窗口介绍 (3)IDA的基本使用 二、调试器 2.1、简介: 2.2、Ollydbg (1)主界面 (2)断点操作 (3)代码跟踪操作 2.3、gdb (1)简介: (2)安装 (3)基本的调试操作 三、Trace类工具 3.1、简介: 3.2、Qira 反汇

    2024年02月04日
    浏览(27)
  • CTF逆向涉及的各种加密算法与网络安全

    在CTF竞赛中,逆向工程是一项常见的技能,旨在分析和破解各种加密算法以达到解决问题的目的。逆向工程涉及的加密算法种类繁多,下面将介绍几种常见的加密算法,并提供相应的源代码示例。 替换密码(Substitution Cipher) 替换密码是一种简单的加密算法,它通过替换明文

    2024年02月08日
    浏览(33)
  • 66.网游逆向分析与插件开发-角色数据的获取-角色类的数据分析与C++还原

    内容来源于: 易道云信息技术研究院VIP课 上一个内容:65.网游逆向分析与插件开发-角色数据的获取-项目需求与需求拆解-CSDN博客 ReClass.NET工具下载,它下方链接里的 逆向工具.zip 里的reclass目录下:注意它分x64、x32版本,启动是用管理员权限启动否则附加时有些进程附加不上

    2024年02月03日
    浏览(32)
  • C/C++源程序到可执行程序exe的全过程(及汇编和反汇编的区别)

    1.C/C++源程序到可执行程序exe的全过程(及汇编和反汇编的区别) 一个现代编译器的主要工作流程如下: 源程序(source code)→预处理器(preprocessor)→编译器(compiler)→汇编程序(assembler)→目标程序(object code)→连接器(链接器,Linker)→可执行程序(executables)。 简

    2024年02月10日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包