reverse IDA使用技巧IDA动态调试Linux_ELF配置+例题:SCUCTF新生赛2021-DebugMe

Posted hans774882968

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了reverse IDA使用技巧IDA动态调试Linux_ELF配置+例题:SCUCTF新生赛2021-DebugMe相关的知识,希望对你有一定的参考价值。

文章目录

依赖

  • IDA7.7
  • VirtualBox虚拟机Ubuntu20.04
  • SCUCTF新生赛2021-DebugMe:ELF在这找:https://github.com/bluesadi/SCUCTF-Backup

作者:hans774882968以及hans774882968以及hans774882968

本文juejin:https://juejin.cn/post/7142302432326860830/

本文52pojie:https://www.52pojie.cn/thread-1686855-1-1.html

本文csdn:https://blog.csdn.net/hans774882968/article/details/126813166

IDA动态调试ELF配置

首先我们需要一台虚拟机,理论上云服务器也没问题,但用本地的虚拟机更方便。大致跟着参考链接2走一遍就行,但本文会指出一些需要注意的地方。

首先,把IDA安装目录/dbgsrv/linux_server64复制到虚拟机的某目录下(记为<x>/linux_server64),把它运行起来。它默认会占用23946端口。

接下来需要知道虚拟机的Hostname。

获取远程主机的Hostname

如果是云服务器就不用走这一步了,但对于本地虚拟机来说这一步是必要的。参考链接2使用了已不再维护(而且在Ubuntu20.04中不可用)的ifconfig命令。对于Ubuntu20.04,应使用ip a命令。输出信息很长,但我们只需要关注输出中inet 某ip地址的部分。如果你发现你的输出中只有inet 127.0.0.1inet 10.0.2.15,这说明主机不能通过ip访问虚拟机,需要走下一步的流程。

VirtualBox虚拟机设置桥接模式,与宿主机互相ping通

根据参考链接1的两个链接,选择你在用的wifi来设置即可。设置完毕后,不需要重启虚拟机,再次使用ip a命令即可看到Hostname。

填写IDA的Debug application setup

  1. Application:你要调试的ELF的完整路径。对于远程调试会话,该路径为调试服务器上的路径。如果选择不使用完整路径,远程服务器将搜索它的当前工作目录。比如:<x>/RE3_DebugMe
  2. Directory:对于远程调试,此字段表示远程目录。比如:<x>
  3. Parameters:用于指定在进程启动时传递给它的任何命令行参数。对于远程调试会话,进程输出将在用于启动调试服务器的控制台中显示。具体ELF具体分析吧。
  4. Hostname:远程调试服务器主机或IP地址。在上面两步中获取。
  5. Port:远程调试服务器监听的TCP端口号,保持默认即可。
  6. Password:远程调试服务器所需的密码,本地虚拟机不需要。

点击OK按钮,IDA会立刻载入ELF开始调试。

例题:SCUCTF新生赛2021-DebugMe

file命令:ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped

用IDA打开可立刻看到main函数:

int __cdecl main(int argc, const char **argv, const char **envp)

  int v3; // eax
  int v4; // edi
  unsigned int v5; // esi
  unsigned int v6; // ecx
  unsigned int v7; // eax
  unsigned int v8; // eax
  int v9; // eax
  int v10; // r9d
  char v11; // si
  char v12; // dl
  char v13; // si
  int v14; // r11d
  char v15; // si
  int v16; // r9d
  char v17; // al
  char v18; // al
  __int64 v20; // [rsp+0h] [rbp-30h]
  int i; // [rsp+8h] [rbp-28h]
  unsigned int v22; // [rsp+Ch] [rbp-24h]
  int v23; // [rsp+10h] [rbp-20h]
  int v24; // [rsp+14h] [rbp-1Ch]
  char *nptr; // [rsp+18h] [rbp-18h]
  const char **v26; // [rsp+20h] [rbp-10h]
  int v27; // [rsp+28h] [rbp-8h]
  int v28; // [rsp+2Ch] [rbp-4h]

  v28 = 0;
  v27 = argc;
  v26 = argv;
  if ( argc == 1 )
  
    puts("Usage: ./AnotherOs <your number>");
  
  else if ( v27 == 2 )
  
    nptr = (char *)v26[1];
    v24 = strtol(nptr, 0LL, 10);
    v3 = ~((unsigned __int16)~(*(_DWORD *)(&v20 - 2) & ~((*((_DWORD *)&v20 - 4) | 0xE15) & (~(unsigned __int16)*((_DWORD *)&v20 - 4) | 0xF1EA))) & (unsigned __int16)~(~(unsigned __int16)*((_DWORD *)&v20 - 4) & (*((_DWORD *)&v20 - 4) | 0xE15) & (~(unsigned __int16)*((_DWORD *)&v20 - 4) | 0xF1EA))) & 0x672 | (~*((_DWORD *)&v20 - 4) ^ (~(unsigned __int8)*((_DWORD *)&v20 - 4) & 0x62 | *(_DWORD *)(&v20 - 2) & 0x2ABDF188)) & 0x2ABDF1EA;
    v4 = *((_DWORD *)&v20 - 4);
    v5 = ~(~(~(~v4 & ~((~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~(v4 & (~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15)) & ~(~(~v4 & v3) & ~(v4 & ~v3))) & ~(~(~(~v4 & ~((~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~(v4 & (~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~(~v4 & v3) & ~(v4 & ~v3)));
    v6 = v5 & (v5 ^ ~(v4 & ~((v4 | 0xD5420E15) & (~v4 | 0x2ABDF1EA))) & ~(~v4 & (v4 | 0xD5420E15) & (~v4 | 0x2ABDF1EA))) | (~v4 ^ (~v4 & v5 | v4 & ~(~(~v4 & ~((~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~(v4 & (~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15)) & ~(~(~v4 & v3) & ~(v4 & ~v3))) & ~(~(~(~v4 & ~((~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~(v4 & (~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~(~v4 & v3) & ~(v4 & ~v3)))) & 0x2ABDF1EA;
    v7 = ~((v4 | 0xD5420DCA) & (~v4 | 0x2ABDF235) & ~(~(~v4 & v6) & ~(v4 & ~v6))) & ~(~((v4 | 0xD5420DCA) & (~v4 | 0x2ABDF235)) & ~(~v4 & v6) & ~(v4 & ~v6));
    v8 = ~v7 & (~v7 ^ ~(v4 & ~((v4 | 0xD5420E15) & (~v4 | 0x2ABDF1EA))) & ~(~v4 & (v4 | 0xD5420E15) & (~v4 | 0x2ABDF1EA))) | (~v4 ^ (~v4 & ~v7 | v4 & v7)) & 0x2ABDF1EA;
    v9 = ~(~(~v4 & v8) & ~(v4 & ~v8));
    v22 = ~(~(~(~v4 & ~((~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~(v4 & (~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15)) & v9) & ~(~(~(~v4 & ~((~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~(v4 & (~v23 | 0x2ABDF1EA) & (v23 | 0xD5420E15))) & ~v9));
    if ( v24 == v22 )
    
      puts("Correct! Here is your flag:");
      for ( i = 0; i < 34; ++i )
      
        v10 = *((_DWORD *)&v20 - 4);
        v11 = enc[i] & (enc[i] ^ ~(v10 & ~((v10 | 0x15) & (~(_BYTE)v10 | 0xEA))) & ~(~(_BYTE)v10 & (v10 | 0x15) & (~(_BYTE)v10 | 0xEA))) | (~(_BYTE)v10 ^ (~(_BYTE)v10 & enc[i] | v10 & ~enc[i])) & 0xEA;
        v12 = ~(~(_BYTE)v10 & v11) & ~(v10 & ~v11);
        enc[i] = ~(~((v10 | 0x57) & (~(_BYTE)v10 | 0xA8) & ~v12) & ~(~((v10 | 0x57) & (~(_BYTE)v10 | 0xA8)) & v12));
        v13 = enc[i];
        v14 = *((_DWORD *)&v20 - 4);
        v15 = v13 & (v13 ^ ~(v14 & ~((v14 | 0x15) & (~(_BYTE)v14 | 0xEA))) & ~(~(_BYTE)v14 & (v14 | 0x15) & (~(_BYTE)v14 | 0xEA))) | (~(_BYTE)v14 ^ (~(_BYTE)v14 & v13 | v14 & ~v13)) & 0xEA;
        enc[i] = ~(~(~(~(_BYTE)v14 & ~((~key[i] | 0xEA) & (key[i] | 0x15))) & ~(v14 & (~key[i] | 0xEA) & (key[i] | 0x15)) & ~(~(~(_BYTE)v14 & v15) & ~(v14 & ~v15))) & ~(~(~(~(_BYTE)v14 & ~((~key[i] | 0xEA) & (key[i] | 0x15))) & ~(v14 & (~key[i] | 0xEA) & (key[i] | 0x15))) & ~(~(_BYTE)v14 & v15) & ~(v14 & ~v15)));
        v16 = *((_DWORD *)&v20 - 4);
        v17 = enc[i] & (enc[i] ^ ~(v16 & ~((v16 | 0x15) & (~(_BYTE)v16 | 0xEA))) & ~(~(_BYTE)v16 & (v16 | 0x15) & (~(_BYTE)v16 | 0xEA))) | (~(_BYTE)v16 ^ (~(_BYTE)v16 & enc[i] | v16 & ~enc[i])) & 0xEA;
        v18 = ~(~(~(_BYTE)v16 & v17) & ~(v16 & ~v17));
        enc[i] = ~(~(~(~(_BYTE)v16 & 0xA8) & ~(v16 & 0x57) & v18) & ~(~(~(~(_BYTE)v16 & 0xA8) & ~(v16 & 0x57)) & ~v18));
        putchar(enc[i]);
      
      putchar(10);
    
    else
    
      puts("You have inputted a wrong number.");
      puts("Difficult huh? try to Use IDA Remote Debugger to pass this input!");
      puts("The flag will be automatically decoded and printed!");
    
  
  return 0;

我们的目标就是动态调试,拿到v22的值,并修改v24的值。

Debug application setup的时候,Parameters给一个不错的整数114514。程序载入之后,我们点击导航栏下面的一个叫Use source-level debugging的图标,并按F5反汇编,开启源码级调试。于是我们很快可以得到下图:

接下来改v24。似乎IDA的调试器不能很方便地跳转到某个地址,这里考虑通过汇编来修改寄存器。

找到if(v24 == v22)对应的汇编代码:

.text:00000000004008B3 mov     eax, [rbp+var_1C] ; v24
.text:00000000004008B6 cmp     eax, [rbp+var_24] ; v22
.text:00000000004008B9 jz      short loc_4008ED

在执行完0x4008b3的语句时,将rax的值改成0x5ad,即可进入期望的代码块。接下来可以继续在c伪代码处调试。

参考资料

  1. VirtualBox网络改成桥接模式:https://www.cnblogs.com/zwh1993/p/12425227.html、https://blog.csdn.net/zcf1319/article/details/105250300
  2. IDA动态调试配置:https://blog.csdn.net/weixin_32051661/article/details/116679761

以上是关于reverse IDA使用技巧IDA动态调试Linux_ELF配置+例题:SCUCTF新生赛2021-DebugMe的主要内容,如果未能解决你的问题,请参考以下文章

攻防世界reverse进阶easyre-153 writeup(#gdb调试父子进程#ida版本差异)

ida动态调试无法转化代码

IDA动态调试破解EXE文件与分析APK流程

IDA动态调试破解EXE文件与分析APK流程

Android逆向 ida动态调试问题

IDA 动态调试 ELF 文件