羊城杯2023 | Reverse

羊城杯2023

CSGO

学习到了sharpOD orz
IDA的话没办法动调 至少很难动调
FindCrypt插件可以发现有base64加密
查看交叉引用 在汇编代码中定位到字串

.text:000000000049B144                 mov     [rsp+0C8h+var_88], rcx
.text:000000000049B149                 mov     [rsp+0C8h+var_80], rdi
.text:000000000049B14E                 mov     [rsp+0C8h+var_70], rdx
.text:000000000049B153                 lea     rcx, BASE64_table_4D9278 ; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"...
.text:000000000049B15A                 movzx   ebx, byte ptr [rax+rcx+0Bh]
.text:000000000049B15F                 xor     eax, eax
.text:000000000049B161                 call    runtime_intstring
.text:000000000049B166                 mov     rcx, [rsp+0C8h+var_88]
.text:000000000049B16B                 mov     rdi, rax
.text:000000000049B16E                 mov     rsi, rbx
.text:000000000049B171                 xor     eax, eax
.text:000000000049B173                 mov     rbx, [rsp+0C8h+var_70]
.text:000000000049B178                 call    runtime_concatstring2
.text:000000000049B17D                 mov     rcx, [rsp+0C8h+var_80]
.text:000000000049B182                 inc     rcx
.text:000000000049B185                 mov     rdx, rax
.text:000000000049B188                 mov     rax, rcx
.text:000000000049B18B                 mov     rcx, rbx

在main的汇编可以找到最后加密对比字符串

mov     rcx, rbx
lea     rbx, aCpqebacrpNZepY ; "cPQebAcRp+n+ZeP+YePEWfP7bej4YefCYd/7cuP"...
call    runtime_memequal
test    al, al
jnz     short loc_49B282

但是直接解发现是乱码
那么要么换表 要么base64后还进行了加密
但是看密文形式(=) 只可能是换表 但只有上面找到的一处lea ... ABCD...
理论可以动调查看
但IDA动调会直接while(1)卡死
反调试逻辑也比较复杂
这里学习到了用x64dbg的sharpOD插件过掉反调试的方法

搜索安装好插件 把默认勾上的都勾上
在x64中搜索ABCD... 定位到代码段
下个断点 就能运行了
输入flag后一直F9 可以发现出现了一个很大的循环 对base64表进行了变换

0000000000B5B14 | 48:895424 58            | mov qword ptr ss:[rsp+58],rdx           | [rsp+58]:"LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHI"
0000000000B5B15 | 48:8D0D 1EE10300        | lea rcx,qword ptr ds:[B99278]           | 0000000000B99278:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

可以发现这里逻辑就是做了一个整体平移 那么提取出变表base64解密即可
DASCTF{73913519-A0A6-5575-0F10-DDCBF50FA8CA}



抽空还得看看具体怎么反调试的 以及IDA怎么反反调试

其实通过这段代码(概率性的F5出来了)

    v19 = (unsigned __int8)BASE64_table_4D9278[v25 + 11];
    v8 = runtime_intstring(
           0,
           v19,
           (unsigned int)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
           v24,
           (int)v25 + 11,
           v12,
           v13,
           v14,
           v15,
           v53,
           v59);
    LODWORD(v9) = v19;

可以猜到大致是移位11位 外面套了个64的循环 那么应该是取index赋值之类

Blast

用D810稍微去了点混淆
main

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  ...
  v45 = a1;
  v44 = a2;
  v43 = &v8;
  v42 = &v8;
  v41 = &v8;
  v40 = &v8;
  v46 = 0;
  s = (char *)(&v8 - 4);
  v38 = &v8 - 4;
  v37 = &v8 - 24;
  v36 = &v8 - 4;
  v35 = &v47;
  v34 = &v8 - 4;
  v9 = HIDWORD(a2);
  v8 = 0;
  while ( 1 )
  {
    do
      v33 = *v41;
    while ( dword_40F9A8 >= 10 && dword_40F9A8 < 10 );
    v32 = v33;
    do
      v31 = strlen((const char *)(unsigned int)a0123456789abcd);
    while ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 );
    do
      v30 = v32 < v31;
    while ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 );
    if ( !v30 )
      break;
    do
      v29 = *v41;
    while ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 );
    do
      v28 = &a0123456789abcd[v29];
    while ( dword_40F9A8 >= 10 && dword_40F9A8 < 10 );
    byte_40E060[*v41] = *v28;
    do
      v27 = *v41;
    while ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 );
    v26 = v27 + 1;
    if ( dword_40F9A8 >= 10 && dword_40F9A8 < 10 )
LABEL_38:
      *v41 = v26;
    *v41 = v26;
    if ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 )
      goto LABEL_38;
    while ( dword_40F9A8 >= 10 && dword_40F9A8 < 10 )
      ;
  }
  v25 = gets(INPUT);                            // INPUT here
  *v40 = 0;
  do
LABEL_16:
    v24 = *v40;
  while ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 );
  v23 = v24 < 47;
  if ( v24 < 47 )
  {
    v22 = *v40;
    *s = INPUT[v22];
    sub_404010(v37);                            // md5常量初始化
    do
    {
      v21 = 1;
      v20 = strlen(s);
    }
    while ( !((v21 | 1) & (dword_40F9A8 < 10) | ((unsigned __int8)(~(dword_40F9A8 < 10) | v21 ^ 1) ^ (((v21 & 1) == 0) | 0xFE)) & 1) );
    if ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 )
      goto LABEL_42;
    while ( 1 )
    {
      sub_404140(v37, s, (unsigned int)v20);
      sub_40A510(v37, v38);
      *v36 = 0;
      if ( dword_40F9A8 >= 10 || dword_40F9A8 < 10 )
        break;
LABEL_42:
      sub_404140(v37, s, (unsigned int)v20);
      sub_40A510(v37, v38);
      *v36 = 0;
    }
    while ( 1 )
    {
      v19 = *v36;
      v18 = v19 < 16;
      if ( v19 >= 16 )
      {
        v10 = *v40;
        do
          v9 = v10 + 1;
        while ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 );
        *v40 = v9;
        goto LABEL_16;
      }
      v17 = *v36;
      v16 = v17;
      v15 = *((unsigned __int8 *)v38 + v17);
      v14 = 0;
      v13 = 16;
      v3 = v35;
      *(_BYTE *)v35 = byte_40E060[v15 / 16LL];
      v4 = v36;
      v5 = v34;
      *(_BYTE *)v34 = byte_40E060[*((unsigned __int8 *)v38 + *v36) % 16];
      v6 = v40;
      byte_40E700[33 * *v40 + 2 * *v4] = *(_BYTE *)v3;
      byte_40E700[33 * *v6 + 2 * *v4 - (v14 - 1)] = *(_BYTE *)v5;
      do
        v12 = *v36;
      while ( dword_40F9A8 >= 10 && dword_40F9A8 < 10 );
      v11 = v12 + 1;
      if ( dword_40F9A8 >= 10 && dword_40F9A8 < 10 )
LABEL_44:
        *v36 = v11;
      *v36 = v11;
      if ( dword_40F9A8 < 10 && dword_40F9A8 >= 10 )
        goto LABEL_44;
    }
  }
  sub_402370();
  return 0LL;
}

看着好多do-while 其实就只执行一次
FindCrypt找到md5加密特征 同时在sub_404010也找到md5常量
再加上找到的

.data:000000000040E0A0	global	Big_Numbers1_40E0A0	$c0	b'14d89c38cd0fb23a14be2798d449c182'
.data:000000000040E0C1	global	Big_Numbers1_40E0C1	$c0	b'a94837b18f8f43f29448b40a6e7386ba'
.data:000000000040E0E2	global	Big_Numbers1_40E0E2	$c0	b'af85d512594fc84a5c65ec9970956ea5'
.data:000000000040E103	global	Big_Numbers1_40E103	$c0	b'af85d512594fc84a5c65ec9970956ea5'
.data:000000000040E124	global	Big_Numbers1_40E124	$c0	b'10e21da237a4a1491e769df6f4c3b419'
.data:000000000040E145	global	Big_Numbers1_40E145	$c0	b'a705e8280082f93f07e3486636f3827a'
.data:000000000040E166	global	Big_Numbers1_40E166	$c0	b'297e7ca127d2eef674c119331fe30dff'
.data:000000000040E187	global	Big_Numbers1_40E187	$c0	b'b5d2099e49bdb07b8176dff5e23b3c14'
.data:000000000040E1A8	global	Big_Numbers1_40E1A8	$c0	b'83be264eb452fcf0a1c322f2c7cbf987'
.data:000000000040E1C9	global	Big_Numbers1_40E1C9	$c0	b'a94837b18f8f43f29448b40a6e7386ba'
.data:000000000040E1EA	global	Big_Numbers1_40E1EA	$c0	b'71b0438bf46aa26928c7f5a371d619e1'
.data:000000000040E20B	global	Big_Numbers1_40E20B	$c0	b'a705e8280082f93f07e3486636f3827a'
.data:000000000040E22C	global	Big_Numbers1_40E22C	$c0	b'ac49073a7165f41c57eb2c1806a7092e'
.data:000000000040E24D	global	Big_Numbers1_40E24D	$c0	b'a94837b18f8f43f29448b40a6e7386ba'
.data:000000000040E26E	global	Big_Numbers1_40E26E	$c0	b'af85d512594fc84a5c65ec9970956ea5'
.data:000000000040E28F	global	Big_Numbers1_40E28F	$c0	b'ed108f6919ebadc8e809f8b86ef40b05'
.data:000000000040E2B0	global	Big_Numbers1_40E2B0	$c0	b'10e21da237a4a1491e769df6f4c3b419'
.data:000000000040E2D1	global	Big_Numbers1_40E2D1	$c0	b'3cfd436919bc3107d68b912ee647f341'
.data:000000000040E2F2	global	Big_Numbers1_40E2F2	$c0	b'a705e8280082f93f07e3486636f3827a'
.data:000000000040E313	global	Big_Numbers1_40E313	$c0	b'65c162f7c43612ba1bdf4d0f2912bbc0'
.data:000000000040E334	global	Big_Numbers1_40E334	$c0	b'10e21da237a4a1491e769df6f4c3b419'
.data:000000000040E355	global	Big_Numbers1_40E355	$c0	b'a705e8280082f93f07e3486636f3827a'
.data:000000000040E376	global	Big_Numbers1_40E376	$c0	b'3cfd436919bc3107d68b912ee647f341'
.data:000000000040E397	global	Big_Numbers1_40E397	$c0	b'557460d317ae874c924e9be336a83cbe'
.data:000000000040E3B8	global	Big_Numbers1_40E3B8	$c0	b'a705e8280082f93f07e3486636f3827a'
.data:000000000040E3D9	global	Big_Numbers1_40E3D9	$c0	b'9203d8a26e241e63e4b35b3527440998'
.data:000000000040E3FA	global	Big_Numbers1_40E3FA	$c0	b'10e21da237a4a1491e769df6f4c3b419'
.data:000000000040E41B	global	Big_Numbers1_40E41B	$c0	b'f91b2663febba8a884487f7de5e1d249'
.data:000000000040E43C	global	Big_Numbers1_40E43C	$c0	b'a705e8280082f93f07e3486636f3827a'
.data:000000000040E45D	global	Big_Numbers1_40E45D	$c0	b'd7afde3e7059cd0a0fe09eec4b0008cd'
.data:000000000040E47E	global	Big_Numbers1_40E47E	$c0	b'488c428cd4a8d916deee7c1613c8b2fd'
.data:000000000040E49F	global	Big_Numbers1_40E49F	$c0	b'39abe4bca904bca5a11121955a2996bf'
.data:000000000040E4C0	global	Big_Numbers1_40E4C0	$c0	b'a705e8280082f93f07e3486636f3827a'
.data:000000000040E4E1	global	Big_Numbers1_40E4E1	$c0	b'3cfd436919bc3107d68b912ee647f341'
.data:000000000040E502	global	Big_Numbers1_40E502	$c0	b'39abe4bca904bca5a11121955a2996bf'
.data:000000000040E523	global	Big_Numbers1_40E523	$c0	b'4e44f1ac85cd60e3caa56bfd4afb675e'
.data:000000000040E544	global	Big_Numbers1_40E544	$c0	b'45cf8ddfae1d78741d8f1c622689e4af'
.data:000000000040E565	global	Big_Numbers1_40E565	$c0	b'3cfd436919bc3107d68b912ee647f341'
.data:000000000040E586	global	Big_Numbers1_40E586	$c0	b'39abe4bca904bca5a11121955a2996bf'
.data:000000000040E5A7	global	Big_Numbers1_40E5A7	$c0	b'4e44f1ac85cd60e3caa56bfd4afb675e'
.data:000000000040E5C8	global	Big_Numbers1_40E5C8	$c0	b'37327bb06c83cb29cefde1963ea588aa'
.data:000000000040E5E9	global	Big_Numbers1_40E5E9	$c0	b'a705e8280082f93f07e3486636f3827a'
.data:000000000040E60A	global	Big_Numbers1_40E60A	$c0	b'23e65a679105b85c5dc7034fded4fb5f'
.data:000000000040E62B	global	Big_Numbers1_40E62B	$c0	b'10e21da237a4a1491e769df6f4c3b419'
.data:000000000040E64C	global	Big_Numbers1_40E64C	$c0	b'71b0438bf46aa26928c7f5a371d619e1'
.data:000000000040E66D	global	Big_Numbers1_40E66D	$c0	b'af85d512594fc84a5c65ec9970956ea5'
.data:000000000040E68E	global	Big_Numbers1_40E68E	$c0	b'39abe4bca904bca5a11121955a2996bf'

找个md5网站解一解 发现是这种形式 md5(md5($pass))
所以这是对单字符做了两遍md5的结果
那么拿到最后数据 爆破一下即可
exp:

s = """
...
"""
s = s.split('\n')
s = s[:-1]
enc = []
for c in s:
    enc.append(c[-33:-1])
enc = enc[1:]
from hashlib import md5
flag = ""
for target in enc:
    # print(target)
    for x in range(32,128):
        _hash = md5(str(chr(x)).encode()).hexdigest()
        ss = _hash
        _hash = md5(str(ss).encode()).hexdigest()
        if(_hash == target):
            flag += chr(x)
            break
    print(flag)
# Hello_Ctfer_Velcom_To_my_Mov_and_md5(md5)_world


以上是做题的一些trick 其实还是想了解一下这题完整的逻辑
由于函数不多 挨个翻
在 sub_404CB0 看到是md5加密算法(不可能魔改md5吧...)
交叉引用查看哪里调用了md5
在 sub_404140 找到了唯三调用的点

  v14 = *v51 + 24;
  v33 = *v51 + 8;
  v32 = v14;
  sub_404CB0(v33, v14);
  *v48 = *(_DWORD *)v46;
  while ( 1 )
  {
    v31 = *v48;
    v30 = v31 + 64;
    if ( (unsigned int)(v31 + 64) > *(_DWORD *)v49 )
      break;
    v28 = *v51 + 8;
    while ( dword_40FA30 < 10 && dword_40FA30 >= 10 )
      ;
    while ( 1 )
    {
      sub_404CB0(v28, (char *)*v50 + (unsigned int)*v48);
      if ( dword_40FA30 >= 10 || dword_40FA30 < 10 )
        break;
      sub_404CB0(v28, (char *)*v50 + (unsigned int)*v48);
    }
    v27 = *v48;
    v26 = v27 + 64;

由于第二个第三个在一个虚假的do-while里 实则只执行了第二个md5 加上前面的md5 有效的md5调用了两次
同时通过 v28->v51->v14/v33 也能发现这两次md5有关联 那么合理推测md5(md5(xxx))就很自然了
由于这题控制流不是那么平坦 平坦化后看逻辑还好 但是动调跳来跳去挺烦的 所以动调就免了(以后有时间再来试试)

vm_wo

macos的ARM指令集... 因为没有mac动调不了一点...
题目逻辑简单
输入长度为29的flag后进行虚拟机操作加密 然后与一直enc比对
虚拟机前的opcode准备部分:

do
    {
      v8[0] = 0x20D01011903001ALL;
      *(_QWORD *)((char *)v8 + 7) = 0x300010201180702LL;
      BYTE2(v8[0]) = *a1;
      interpretBytecode((__int64)v8, 15);
      v7[0] = 0x20D02011903001ALL;
      *(_QWORD *)((char *)v7 + 7) = 0x400010201180602LL;
      BYTE2(v7[0]) = vm_body[0];
      interpretBytecode((__int64)v7, 15);
      v6[0] = 0x20D03011903001ALL;
      *(_QWORD *)((char *)v6 + 7) = 0x500010201180502LL;
      BYTE2(v6[0]) = vm_body[0];
      interpretBytecode((__int64)v6, 15);
      v5[0] = 0x20D04011903001ALL;
      *(_QWORD *)((char *)v5 + 7) = 0x600010201180402LL;
      BYTE2(v5[0]) = vm_body[0];
      interpretBytecode((__int64)v5, 15);
      *a1++ = ((unsigned __int8)vm_body[0] >> 5) | (8 * vm_body[0]);
      --v2;
    }

最开始就是这里没有理解准确
以v7[0]为例

v7[0] = 0x20D02011903001ALL; -> 低8个字节 020D02011903001A
*(_QWORD *)((char *)v7 + 7) = 0x400010201180602LL;
注意到这里是+7 所以会覆盖掉v7[0]的最高字节
->400010201180602 020D02011903001A -> 04000102011806020D02011903001A
刚好15字节

这就是静态分析容易出错的点... 如果能动调的话就特别简单...
然后找对应的opcode(hex)
发现就是一个循环移位+异或的加密逻辑

0 mov vm_body[0] 3
3 mov vm_body[1] vm_body[0]>>1
6 mov vm_body[2] vm_body[0]<<7
9 mov vm_body[0] vm_body[1]|vm_body[2]
12 xor vm_body[0] vm_body[3]
15 mov vm_body[0] 3
18 mov vm_body[1] vm_body[0]>>2
21 mov vm_body[2] vm_body[0]<<6
24 mov vm_body[0] vm_body[1]|vm_body[2]
27 xor vm_body[0] vm_body[4]
30 mov vm_body[0] 3
33 mov vm_body[1] vm_body[0]>>3
36 mov vm_body[2] vm_body[0]<<5
39 mov vm_body[0] vm_body[1]|vm_body[2]
42 xor vm_body[0] vm_body[5]
45 mov vm_body[0] 3
48 mov vm_body[1] vm_body[0]>>4
51 mov vm_body[2] vm_body[0]<<4
54 mov vm_body[0] vm_body[1]|vm_body[2]
57 xor vm_body[0] vm_body[6]

注意下vm_body[3]处是dword的赋值 刚好对应BYTE的[3],[4],[5],[6]
正向爆破即可

DASCTF{you_are_right_so_cool}

EZ加密器

学习到了IDA7.5 Signsrch插件的使用
做的时候犯了一个错误 题目对输入的vericode没有做正确性的检查!!!
只检查了长度... 只要是6位都会继续... 所以后面需要爆破
大致流程:
先输入6位数字 然后base64变表加密得到新的key
再对input做加密 最后HEX拼接与已知比较
比较的密文在加密input的时候做了xor7的修改

关键部分的加密大致可以知道是有sbox的流加密
用IDA插件 Sighsrch

.rdata:0140016080	0040	DES_fp [..64]
.rdata:01400160C0	0030	DES permuted choice key (table) [..48]
.rdata:0140016100	0038	DES permuted choice table (key) [..56]
.rdata:0140016160	0020	DES_p32i [..32]
.rdata:0140016180	0200	DES S-boxes [..512]
.rdata:0140016380	0030	DES_ei [..48]
.rdata:01400163C0	0040	DES initial permutation IP [..64]

知道是DES 那么枚举输入数字爆破即可

from Crypto.Cipher import DES
from base64 import *
from tqdm import *

s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
s2 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
cipher = bytes.fromhex("0723105D5C12217DCDC3601F5ECB54DA9CCEC2279F1684A13A0D716D17217F4C9EA85FF1A42795731CA3C55D3A4D7BEA")
for i in trange(100000,1000000):
    s = str(i).zfill(6)
    key = b64encode((s.encode())).decode().translate(str.maketrans(s1,s2)).encode()
    des = DES.new(key,DES.MODE_ECB)
    pt = des.decrypt(cipher)
    if b'DASCTF' in pt:
        print(pt)
        break

DASCTF{f771b96b71514bb6bc20f3275fa9404e}

  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值