依赖
- 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.1 或inet 10.0.2.15 ,这说明主机不能通过ip访问虚拟机,需要走下一步的流程。
VirtualBox虚拟机设置桥接模式,与宿主机互相ping通
根据参考链接1的两个链接,选择你在用的wifi来设置即可。设置完毕后,不需要重启虚拟机,再次使用ip a 命令即可看到Hostname。
填写IDA的Debug application setup
- Application:你要调试的ELF的完整路径。对于远程调试会话,该路径为调试服务器上的路径。如果选择不使用完整路径,远程服务器将搜索它的当前工作目录。比如:
<x>/RE3_DebugMe 。 - Directory:对于远程调试,此字段表示远程目录。比如:
<x> 。 - Parameters:用于指定在进程启动时传递给它的任何命令行参数。对于远程调试会话,进程输出将在用于启动调试服务器的控制台中显示。具体ELF具体分析吧。
- Hostname:远程调试服务器主机或IP地址。在上面两步中获取。
- Port:远程调试服务器监听的TCP端口号,保持默认即可。
- 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;
int v4;
unsigned int v5;
unsigned int v6;
unsigned int v7;
unsigned int v8;
int v9;
int v10;
char v11;
char v12;
char v13;
int v14;
char v15;
int v16;
char v17;
char v18;
__int64 v20;
int i;
unsigned int v22;
int v23;
int v24;
char *nptr;
const char **v26;
int v27;
int v28;
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伪代码处调试。
参考资料
- VirtualBox网络改成桥接模式:https://www.cnblogs.com/zwh1993/p/12425227.html、https://blog.csdn.net/zcf1319/article/details/105250300
- IDA动态调试配置:https://blog.csdn.net/weixin_32051661/article/details/116679761
|