• BUUCTF reverse wp 56 - 60


    [ACTF新生赛2020]SoulLike

    在这里插入图片描述

    __int64 __fastcall main(int a1, char **a2, char **a3)
    {
      char v5; // [rsp+7h] [rbp-B9h]
      int i; // [rsp+8h] [rbp-B8h]
      int j; // [rsp+Ch] [rbp-B4h]
      int flag_content[14]; // [rsp+10h] [rbp-B0h] BYREF
      char flag[110]; // [rsp+4Ah] [rbp-76h] BYREF
      unsigned __int64 v10; // [rsp+B8h] [rbp-8h]
    
      v10 = __readfsqword(0x28u);
      printf("input flag:");
      scanf("%s", &flag[6]);
      strcpy(flag, "actf{");
      v5 = 1;
      for ( i = 0; i <= 4; ++i )
      {
        if ( flag[i] != flag[i + 6] )
        {
          v5 = 0;
          goto next;
        }
      }
      if ( !v5 )
        goto failed;
    next:
      for ( j = 0; j <= 11; ++j )
        flag_content[j] = flag[j + 11];
      if ( (unsigned __int8)sub_83A(flag_content) && flag[23] == '}' )
      {
        printf("That's true! flag is %s", &flag[6]);
        return 0LL;
      }
      else
      {
    failed:
        printf("Try another time...");
        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
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    sub_83A太大了无法分析
    在这里插入图片描述

    强行让IDA分析大函数, 在IDA安装目录的cfg文件夹找到hexrays.cfg修改配置, 把MAX_FUNCSIZE改大点
    在这里插入图片描述

    在这里插入图片描述
    重启再F5出来, 得到3k+行的代码

    __int64 __fastcall sub_83A(_DWORD *a1)
    {
      int i; // [rsp+1Ch] [rbp-44h]
      int v3[14]; // [rsp+20h] [rbp-40h]
      unsigned __int64 v4; // [rsp+58h] [rbp-8h]
    
      v4 = __readfsqword(0x28u);
      *a1 ^= 0x2Bu;
      a1[1] ^= 0x6Cu;
      a1[2] ^= 0x7Eu;
      a1[3] ^= 0x56u;
      a1[4] ^= 0x39u;
      a1[5] ^= 3u;
      a1[6] ^= 0x2Du;
      a1[7] ^= 0x28u;
      a1[8] ^= 8u;
    ...
      v3[6] = 53;
      v3[7] = 110;
      v3[8] = 0;
      v3[9] = 19;
      v3[10] = 30;
      v3[11] = 56;
      for ( i = 0; i <= 11; ++i )
      {
        if ( v3[i] != a1[i] )
        {
          printf("wrong on #%d\n", (unsigned int)i);
          return 0LL;
        }
      }
      return 1LL;
    }
    
    • 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
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    基本都是xor操作, 不想硬逆的话(硬逆也不是不行, 万行以内均可硬逆), 可以copy到c代码进行爆破, 或者pwntools逐位爆破, 或者采用angr自动化分析flag
    最简洁的是pwntools逐位爆破, (angr的方法内存不够跑不出

    from pwn import *
    import re
    
    context.log_level = 'debug'
    ended = False
    flag = ['z' for _ in range(12)]
    for i in range(12):
        if ended == True: break
        for ch in range(32, 127):
            io = process('./SoulLike')
            flag[i] = chr(ch)
            payload = 'actf{' + ''.join(flag) + '}'
            io.sendline(payload)
            recv_content = io.recvline()
            idx = re.findall(b'on #(.*?)\n', recv_content)[0]
            idx = int(idx)
            
            if b"That's true!" in recv_content:
                print(recv_content)
                ended = True
                break
    
            if idx != i: break
            io.close()    
    io.close()
    
    • 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

    [GWCTF 2019]re3

    $ file attachment
    attachment: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=c3b276030138cc9e29a0244b296b8f005a487a77, stripped
    
    • 1
    • 2
    void __fastcall __noreturn main(int a1, char **a2, char **a3)
    {
      int i; // [rsp+8h] [rbp-48h]
      char s[40]; // [rsp+20h] [rbp-30h] BYREF
      unsigned __int64 v5; // [rsp+48h] [rbp-8h]
    
      v5 = __readfsqword(0x28u);
      __isoc99_scanf("%39s", s);
      if ( (unsigned int)strlen(s) != 32 )
      {
        puts("Wrong!");
        exit(0);
      }
      mprotect(&dword_400000, 0xF000uLL, 7);
      for ( i = 0; i <= 223; ++i )
        *((_BYTE *)sub_402219 + i) ^= 0x99u;
      sub_40207B(&unk_603170);
      sub_402219(s);
    }
    
    unsigned __int64 __fastcall sub_40207B(__int64 a1)
    {
      char v2[16]; // [rsp+10h] [rbp-50h] BYREF
      __int64 v3; // [rsp+20h] [rbp-40h] BYREF
      __int64 v4; // [rsp+30h] [rbp-30h] BYREF
      __int64 v5; // [rsp+40h] [rbp-20h] BYREF
      unsigned __int64 v6; // [rsp+58h] [rbp-8h]
    
      v6 = __readfsqword(0x28u);
      sub_401CF9(&BASE64_table_603120, 64LL, v2);   // MD5
      sub_401CF9(&unk_603100, 20LL, &v3);
      sub_401CF9(&Prime_Constants_char_6030C0, 53LL, &v4);
      sub_401CF9(&MD5_Constants_4025C0, 256LL, &v5);
      sub_401CF9(v2, 64LL, a1);                     // MD5
      return __readfsqword(0x28u) ^ v6;
    }
    
    • 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
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    写个IDC脚本解密代码段

    #include 
     
    static main(void){
        auto i = 0;
        auto m = 0;
        for(i = 0x402219; i <= 0x402219 + 223; i=i+1){
            m = byte(i);
            patch_byte(i,m ^ 0x99);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果解密之后的汇编把 push rbpmov rbp, rsp隔开看成两个函数, 就先右键undefine再c, 再全选中后p定义函数, 此时F5就可以看到解密后的反编译伪代码
    在这里插入图片描述

    __int64 __fastcall sub_402219(__int64 input)
    {
      unsigned int v2; // [rsp+18h] [rbp-D8h]
      int i; // [rsp+1Ch] [rbp-D4h]
      char v4[200]; // [rsp+20h] [rbp-D0h] BYREF
      unsigned __int64 v5; // [rsp+E8h] [rbp-8h]
    
      v5 = __readfsqword(0x28u);
      sub_400A71((__int64)v4, (__int64)&unk_603170);
      sub_40196E((__int64)v4, input);               // AES
      sub_40196E((__int64)v4, input + 16);          // AES
      v2 = 1;
      for ( i = 0; i <= 31; ++i )
      {
        if ( *(_BYTE *)(i + input) != byte_6030A0[i] )
          v2 = 0;
      }
      return v2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    最后会和byte_6030A0已知的数组进行比较, 猜测经过的一串处理是某种加密, 用findcrypt插件查看密码函数特征
    在这里插入图片描述
    识别出处理unk_603170的函数是MD5和AES轮密钥函数, 处理input的函数是AES, 整体分析得到流程: MD5两次base64table得到的数值经过AES轮密钥加密得到key, 然后用key对输入的字符串高16位和低16位分别加密(因为不存在iv, 所以加密模式是ECB), 最后和byte_6030A0数组进行比较, key部分可以动调得到(跳过大段逆向)
    在这里插入图片描述

    另外记得chmod +x attachment
    在这里插入图片描述

    在这里插入图片描述

    用原版调试
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    得到key就简单了, REV

    from Crypto.Cipher import AES
    from Crypto.Util.number import *
    
    key = 0xCB8D493521B47A4CC1AE7E62229266CE
    testval = 0xBC0AADC0147C5ECCE0B140BC9C51D52B46B2B9434DE5324BAD7FB4B39CDB4B5B
    key = long_to_bytes(key)
    testval = long_to_bytes(testval)
    round = AES.new(key, mode=AES.MODE_ECB)
    flag = round.decrypt(testval)
    print(flag.decode())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [GXYCTF2019]simple CPP

    在这里插入图片描述

    这里主函数比较长, 不贴完整代码, 因为没有壳而且处理逻辑不直观还有exe不包含恶意代码, 可以考虑直接主机IDA动调
    在IDA目录下的dbgsrv文件夹, 打开win64_remote64
    在这里插入图片描述

    IDA debug选项设置之后就可以动调
    在这里插入图片描述

    这里动调可以确定第一个异或逻辑的v12
    在这里插入图片描述

    在这里插入图片描述

    调试时可以分析出这一段逻辑是在对中间值自身做移位叠加(其实就是颠倒存储顺序, 左移8位就是一个字节, buf的低字节存到高地址)
    在这里插入图片描述接着需要绕过反调试, 在test eax, eax时设置eax为0
    在这里插入图片描述

    根据判断条件得知需要确定v26, v25, v31, v32; v20, v33的值
    在这里插入图片描述

    逆回去分析各个值
    在这里插入图片描述

    整理得到四个变量的方程组, 用z3求解

    from z3 import *
     
    v13, v14, v15, v16 = BitVecs('v13 v14 v15 v16',64)
    s=Solver()
    
    s.add(v14 & ~v16 == 0x11204161012)
    s.add((v14 & ~v16) | (v15 & v16) | (v14 & ~v15) | (v16 & ~v15) == 0x3E3A4717373E7F1F)
    s.add((v14 & ~v15) & (v16) | (v14) & ((v15 & v16) | v15 & ~v16 | ~(v15 | v16)) == 0x8020717153E3013)
    s.add(((v14 & ~v16) | (v15 & v16) | v15 & v14) == (~v16 & v14 | 0xC00020130082C0C))
    s.add(v13 == 0x32310600)
    # s.add(((v14 & ~v16) | (v15 & v16) | (v14 & ~v15) | (v16 & ~v15)) ^ v13 == 0x3E3A4717050F791F)
    
    s.check()
    m = s.model()
    for i in m:
        print("%s = 0x%x" % (i, m[i].as_long()))
    
    '''
    v16 = 0x3e3a460533286f0d
    v14 = 0x8020717153e3013
    v15 = 0xc0002213008acac
    v13 = 0x32310600
    '''
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    再把v13 ~ v16作为字节串处理, 进行高低端序颠倒处理的逆过程, 然后与i_will_check_is_debug_or_not进行xor还原flag (这里已知比赛时的hint: 第二部分字符串为e!P0or_a

    L = []
    L.append(hex(m[v16].as_long())[2:].rjust(16, '0'))
    L.append(hex(m[v15].as_long())[2:].rjust(16, '0'))
    L.append(hex(m[v14].as_long())[2:].rjust(16, '0'))
    L.append(hex(m[v13].as_long())[2:-2])
    
    print(L)
    temp = []
    for each in L:
        if each != '323106':
            for i in range(0, 16, 2):
                tmp = each[i: i + 2]
                tmp = int(tmp, 16)
                temp.append(tmp)
        else:
            for i in range(0, 6, 2):
                tmp = each[i: i + 2]
                tmp = int(tmp, 16)
                temp.append(tmp)
    
    print(temp, len(temp))
    string = 'i_will_check_is_debug_or_not'
    flag = ''
    hint = 'e!P0or_a'
    for i in range(len(temp)):
        if i >= 8 and i < 16:
            flag += hint[i % 8]
        else:
            flag += chr(ord(string[i]) ^ temp[i])
    
    print('flag{' + flag + '}')
    
    • 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
    • 27
    • 28
    • 29
    • 30
    • 31

    [FlareOn5]Ultimate Minesweeper

    在这里插入图片描述

    打开dnSpy进行一波C#逆向

    public MainForm() // 函数入口
    {
    	this.InitializeComponent();
    	this.MineField = new MineField(MainForm.VALLOC_NODE_LIMIT); // 地雷初始化
    	this.AllocateMemory(this.MineField); // 分配空间
    	this.mineFieldControl.DataSource = this.MineField;
    	this.mineFieldControl.SquareRevealed += this.SquareRevealedCallback; // 事件响应
    	this.mineFieldControl.FirstClick += this.FirstClickCallback; 
    	this.stopwatch = new Stopwatch();
    	this.FlagsRemaining = this.MineField.TotalMines;
    	this.mineFieldControl.MineFlagged += this.MineFlaggedCallback;
    	this.RevealedCells = new List<uint>();
    }
    
    public MineField(uint size)
    {
    	this.Size = size;
    	this.MinesPresent = new bool[(int)size, (int)size];
    	this.MinesVisible = new bool[(int)size, (int)size];
    	this.MinesFlagged = new bool[(int)size, (int)size];
    }
    
    private void AllocateMemory(MineField mf)
    {
    	for (uint num = 0U; num < MainForm.VALLOC_NODE_LIMIT; num += 1U)
    	{
    		for (uint num2 = 0U; num2 < MainForm.VALLOC_NODE_LIMIT; num2 += 1U)
    		{
    			bool flag = true;
    			uint r = num + 1U;
    			uint c = num2 + 1U;
    			if (this.VALLOC_TYPES.Contains(this.DeriveVallocType(r, c)))
    			{
    				flag = false;
    			}
    			mf.GarbageCollect[(int)num2, (int)num] = flag;
    		}
    	}
    }
    
    // Token: 0x0600000C RID: 12 RVA: 0x00002348 File Offset: 0x00000548
    private void SquareRevealedCallback(uint column, uint row)
    {
    	if (this.MineField.BombRevealed)
    	{
    		this.stopwatch.Stop();
    		Application.DoEvents();
    		Thread.Sleep(1000);
    		new FailurePopup().ShowDialog();
    		Application.Exit();
    	}
    	this.RevealedCells.Add(row * MainForm.VALLOC_NODE_LIMIT + column);
    	if (this.MineField.TotalUnrevealedEmptySquares == 0)
    	{
    		this.stopwatch.Stop();
    		Application.DoEvents();
    		Thread.Sleep(1000);
    		new SuccessPopup(this.GetKey(this.RevealedCells)).ShowDialog();
    		Application.Exit();
    	}
    }
    
    // Token: 0x0600000D RID: 13 RVA: 0x000023E4 File Offset: 0x000005E4
    private string GetKey(List<uint> revealedCells)
    {
    	revealedCells.Sort();
    	Random random = new Random(Convert.ToInt32(revealedCells[0] << 20 | revealedCells[1] << 10 | revealedCells[2]));
    	byte[] array = new byte[32];
    	byte[] array2 = new byte[]
    	{
    		245,
    		75,
    		65,
    		142,
    		68,
    		71,
    		100,
    		185,
    		74,
    		127,
    		62,
    		130,
    		231,
    		129,
    		254,
    		243,
    		28,
    		58,
    		103,
    		179,
    		60,
    		91,
    		195,
    		215,
    		102,
    		145,
    		154,
    		27,
    		57,
    		231,
    		241,
    		86
    	};
    	random.NextBytes(array);
    	uint num = 0U;
    	while ((ulong)num < (ulong)((long)array2.Length))
    	{
    		byte[] array3 = array2;
    		uint num2 = num;
    		array3[(int)num2] = (array3[(int)num2] ^ array[(int)num]);
    		num += 1U;
    	}
    	return Encoding.ASCII.GetString(array2);
    }
    
    • 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
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    GetKey应该是跟flag有关的函数, 不过这里会和随机数组进行异或, 静态分析不通则转换思路, 游戏的目标是点出三个非雷区格子, 而且C#逆向可以直接修改源码所以采取patch的方法解决这个exe, 把踩雷之后的处理逻辑注释掉然后全部保存为_patched.exe, 暴力找出目标格子
    在这里插入图片描述

    最后再重新运行连点出三个格子就跳flag了

    [MRCTF2020]PixelShooter

    在这里插入图片描述

    APK逆向, 拖入jeb (一开始用jadx分析不了非java源码
    发现java代码没有关键逻辑
    搜索一下得知安卓unity游戏的核心逻辑一般位于main\assets\bin\Data\Managed\Assembly-CSharp.dll
    可以直接得到flag
    在这里插入图片描述

  • 相关阅读:
    161_可视化_Power BI 复刻 GitHub 贡献热力图
    Nacos 服务注册注销源码浅析
    MySQL多表查询
    线段树合并
    软件测试面试题:目前主要的测试用例设计方法是什么?
    vue3.0的变化汇总
    uniapp uni-combox 数据源使用对象,选择后获取对应项的ID,可指定自定义的balbel,value
    clickhouse MPPDB数据库 运维实用SQL总结IV
    JVM实战:JVM运行时数据区
    图形学-向量基础与应用
  • 原文地址:https://blog.csdn.net/qq_33976344/article/details/126941186