• 【reverse】buu-[Zer0pts2020]easy_strcmp——main函数的启动过程+IDA动态调试ELF


    依赖

    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 \n", *a2);
      }
      return 0LL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    会这么简单嘛?我们应该关注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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    查阅_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);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    有这些函数被调用了:

    .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
    
    • 1
    • 2

    看了看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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    点击查看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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里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,打出一套组合👊。

    #include 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    Python+libnum库

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

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

    代码

    from libnum import s2n, n2s
    
    
    def main():
        key = [0, 0x410A4335494A0942, 0x0B0EF2F50BE619F0, 0x4F0A3A064A35282B]
        a = b'zer0pts{********CENSORED********}'
        ans = b''
        for i in range(4):
            j = s2n(a[i * 8: i * 8 + 8][::-1]) + key[i]
            ans += n2s(j)[::-1]
        ans += b'}'
        print(ans)
    
    
    if __name__ == "__main__":
        main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    IDA动态调试

    IDA动态调试配置戳这里qwq

    动调时sub_795main函数长这样(因为没有去掉ASLR):

    // write access to const memory has been detected, the output may be wrong!
    __int64 (__fastcall *sub_561199200795())()
    {
      __int64 (__fastcall *result)(); // rax
    
      result = sub_5611992005C6;
      qword_561199401090 = (__int64)sub_5611992005C6;
      off_561199401028 = sub_5611992006EA;
      return result;
    }
    
    __int64 __fastcall sub_5588DE0007C7(int a1, __int64 a2)
    {
      if ( a1 > 1 )
      {
        if ( (unsigned int)sub_5588DE0005C0(*(_QWORD *)(a2 + 8), "zer0pts{********CENSORED********}") )
          sub_5588DE0005A0("Wrong!");
        else
          sub_5588DE0005A0("Correct!");
      }
      else
      {
        sub_5588DE0005B0("Usage: %s \n", *(const char **)a2);
      }
      return 0LL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    即使不知道sub_795hook了strcmp也没关系,我们断点断在main函数,然后“步进”strcmp函数也能找到关键代码。

    sub_6ea点击a1变量,可以定位输入字符串,进而看到它被修改的过程。

    在这里插入图片描述

    在这里插入图片描述

    参考资料

    1. https://zhuanlan.zhihu.com/p/52054044
    2. 一篇写得不错的wp:https://blog.csdn.net/Tokameine/article/details/118148770
  • 相关阅读:
    NumPy 舍入小数、对数、求和和乘积运算详解
    二进制k8s搭建—V1.20版本(高可用集群)—debian-centos系统
    A_02.Aosp11源码编译
    linux-免费ssl证书
    微信小程序用户登录auth.code2Session接口开发
    Whisper + NemoASR + ChatGPT 实现语言转文字、说话人识别、内容总结等功能
    idea 常用快捷键(实用)
    grafana使用小结
    PLC和工控机的网络特性
    近期局势较多变化 适合黄金代理入场吗?
  • 原文地址:https://blog.csdn.net/hans774882968/article/details/126813906