依赖

  1. IDA7.7
  2. Ubuntu20.04

作者:hans774882968以及hans774882968以及hans774882968

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

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

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

思路

64位ELF。用IDA打开即可看到main函数:

__int64 __fastcall main(int a1, char **a2, char **a3){  if ( a1 > 1 )  {    if ( !strcmp(a2[1], "zer0pts{********CENSORED********}") )      puts("Correct!");    else      puts("Wrong!");  }  else  {    printf("Usage: %s <FLAG>\n", *a2);  }  return 0LL;}

会这么简单嘛?我们应该关注a2[1]是否被修改了。因此看看start函数:

// positive sp value has been detected, the output may be wrong!void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void)){  __int64 v3; // rax  int v4; // esi  __int64 v5; // [rsp-8h] [rbp-8h] BYREF  char *retaddr; // [rsp+0h] [rbp+0h] BYREF  v4 = v5;  v5 = v3;  _libc_start_main(main, v4, &retaddr, init, fini, a3, &v5);  __halt();}

查阅_libc_start_main的资料(参考链接1)可知init函数会在main函数之前被调用。

void __fastcall init(unsigned int a1, __int64 a2, __int64 a3){  signed __int64 v4; // rbp  __int64 i; // rbx  v4 = &off_200DF0 - &funcs_889;  init_proc();  if ( v4 )  {    for ( i = 0LL; i != v4; ++i )      ((void (__fastcall *)(_QWORD, __int64, __int64))*(&funcs_889 + i))(a1, a2, a3);  }}

有这些函数被调用了:

.init_array:0000000000200DE0 E0 06 00 00 00 00 00 00       funcs_889 dq offset sub_6E0 .init_array:0000000000200DE8 95 07 00 00 00 00 00 00       dq offset sub_795

看了看sub_6E0啥也没有,那重点肯定是sub_795了:

// write access to const memory has been detected, the output may be wrong!int (**sub_795())(const char *s1, const char *s2){  int (**result)(const char *, const char *); // rax  result = &strcmp;  qword_201090 = (__int64)&strcmp;  off_201028 = sub_6EA;  return result;}

点击查看off_201028,发现是strcmp函数的地址,也就是说off_201028 = sub_6EAstrcmp的地址篡改为sub_6EA的地址。因此main函数调用strcmp的时候会调用sub_6EA

__int64 __fastcall sub_6EA(__int64 a1, __int64 a2){  int i; // [rsp+18h] [rbp-8h]  int v4; // [rsp+18h] [rbp-8h]  int j; // [rsp+1Ch] [rbp-4h]  for ( i = 0; *(_BYTE *)(i + a1); ++i )    ;  v4 = (i  3) + 1;  for ( j = 0; j < v4; ++j )    *(_QWORD *)(8 * j + a1) -= qword_201060[j];  return qword_201090(a1, a2);}

这里a1, a2类型不对,应该是const char *。回忆一下sub_795这句qword_201090 = (__int64)&strcmp,可知qword_201090(a1, a2)就是调用原有的strcmp函数。至此我们已经知道a2[1]被修改的过程。

总结:一种hook,很有趣。

代码

CPP实现

考虑到这里要把字符串当_QWORD使用,python不方便实现,因此我们用IDA安装目录下可找到的defs.h,赋能cpp,打出一套组合"cpp">#include <bits/stdc++.h>#include "defs.h"using namespace std;typedef long long LL;#define rep(i,a,b) for(int i = (a);i <= (b);++i)#define re_(i,a,b) for(int i = (a);i < (b);++i)#define dwn(i,a,b) for(int i = (a);i >= (b);--i)void dbg() {    puts ("");}template<typename T, typename... R>void dbg (const T &f, const R &... r) {    cout << f << " ";    dbg (r...);}int main() {    char a1[40] = "zer0pts{********CENSORED********}";    _QWORD qword_201060[4] = {0, 0x410A4335494A0942, 0x0B0EF2F50BE619F0, 0x4F0A3A064A35282B};    int v4 = strlen (a1);    re_ (j, 0, 4) {        * (_QWORD *) (8 * j + a1) += qword_201060[j];    }    dbg (a1);    return 0;}

Python+libnum库

libnum可以让我们方便地把char数组当成整数来进行内存操作,同时支持一些基本的数论操作(如:求逆元、分解质因数)。安装libnum(注意:只支持python3):

pip config set global.index-url https://pypi.douban.com/simple # 注意现在pip只支持https的源了pip install libnum