文章

DASCTF 2023六月挑战赛|二进制专项

二进制专项-签到

1
2
3
简单统计下参与人数祝大家玩得开心~

DASCTF{Welcome_to_DASCTF5}

Reverse-careful

题目给出了一个恶意样本,请分析出样本请求服务器的域名(flag的形式为DASCTF{md5(域名)}) 其中md5值都是小写

拿到题目直接 IDA Pro 打开, 一眼就可以看 gethostbyname();

动态调试可以获取name的值,但是这个地方获取的name不是真正的

image

直接 快捷键 x 看 gethostbyname() 交叉引用 还有另一处

image

直在这里下断点,然后动态调试

image

得到 域名 Just_An_APIH00k11.com

image

1
2
3
4
5
6
7
8
9
from hashlib import md5
def md5sum(string):
    return md5(string.encode('utf-8')).hexdigest()
    pass

x = md5sum('Just_An_APIH00k11.com')
print(x)
# f18566f93963f72f463fdfa2d163c37c
# flag{f18566f93963f72f463fdfa2d163c37c}

Reverse-babyRe

so easy

根据字符串 error! 引用处一步一步往上找交叉引用的函数

应该是函数的主要逻辑的地方了

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
__int64 __fastcall sub_140007A80(int a1, __int64 a2)
{
  char *v2; // rdi
  __int64 i; // rcx
  char v5[32]; // [rsp+0h] [rbp-40h] BYREF
  char v6; // [rsp+40h] [rbp+0h] BYREF
  DWORD dwProcessId; // [rsp+44h] [rbp+4h]
  HANDLE v8; // [rsp+68h] [rbp+28h]
  char Parameter[132]; // [rsp+90h] [rbp+50h] BYREF
  int j; // [rsp+114h] [rbp+D4h]
  HRSRC hResInfo; // [rsp+138h] [rbp+F8h]
  size_t Size; // [rsp+154h] [rbp+114h]
  HGLOBAL hResData; // [rsp+178h] [rbp+138h]
  void *Src; // [rsp+198h] [rbp+158h]
  void *v15; // [rsp+1B8h] [rbp+178h]
  __int64 v16; // [rsp+1D8h] [rbp+198h]
  __int64 v17; // [rsp+1F8h] [rbp+1B8h]
  int k; // [rsp+214h] [rbp+1D4h]
  __int64 v19; // [rsp+2E8h] [rbp+2A8h]

  v2 = &v6;
  for ( i = 130i64; i; --i )
  {
    *(_DWORD *)v2 = -858993460;
    v2 += 4;
  }
  sub_1400012A8(&unk_1400140F4);
  dwProcessId = GetCurrentProcessId();
  v8 = OpenProcess(0x1FFFFFu, 0, dwProcessId);
  if ( a1 != 2 )
    exit(0);
  for ( j = 0; ; ++j )
  {
    v19 = j;
    if ( j >= strlen(*(const char **)(a2 + 8)) )
      break;
    Parameter[j] = *(_BYTE *)(*(_QWORD *)(a2 + 8) + j);
  }
  hResInfo = FindResourceW(0i64, (LPCWSTR)0x65, L"cod");
  LODWORD(Size) = SizeofResource(0i64, hResInfo);
  hResData = LoadResource(0i64, hResInfo);
  Src = LockResource(hResData);
  v15 = malloc((unsigned int)Size);
  memcpy(v15, Src, (unsigned int)Size);
  sub_140001087(v15);
  v16 = qword_14000FC30(v8, 0i64, 874i64, 4096i64, 64);
  qword_14000FC28(v8, v16, v15, 874i64, 0i64);
  v17 = qword_14000FC18(v8, 0i64, 0i64, v16, Parameter, 0, 0i64);
  Sleep(0x1F4u);
  for ( k = 0; k < 44; ++k )
    ;
  CreateThread(0i64, 0i64, (LPTHREAD_START_ROUTINE)StartAddress, Parameter, 0, 0i64);
  Sleep(0x190u);
  sub_140001253(v5, &unk_14000BEE0);
  return 0i64;
}

直接看的话还不是很好看,我先修复下函数的名字

可以看注释

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
__int64 __fastcall sub_140007A80(int argv_value, char **argv)
{
  char *v2; // rdi
  __int64 i; // rcx
  char v5[32]; // [rsp+0h] [rbp-40h] BYREF
  char v6; // [rsp+40h] [rbp+0h] BYREF
  DWORD dwProcessId; // [rsp+44h] [rbp+4h]
  HANDLE v8; // [rsp+68h] [rbp+28h]
  char Parameter[132]; // [rsp+90h] [rbp+50h] BYREF
  int j; // [rsp+114h] [rbp+D4h]
  HRSRC hResInfo; // [rsp+138h] [rbp+F8h]
  size_t Size; // [rsp+154h] [rbp+114h]
  HGLOBAL hResData; // [rsp+178h] [rbp+138h]
  void *Src; // [rsp+198h] [rbp+158h]
  void *data; // [rsp+1B8h] [rbp+178h]
  __int64 v16; // [rsp+1D8h] [rbp+198h]
  __int64 v17; // [rsp+1F8h] [rbp+1B8h]
  int k; // [rsp+214h] [rbp+1D4h]
  __int64 v19; // [rsp+2E8h] [rbp+2A8h]

  v2 = &v6;
  for ( i = 130i64; i; --i )
  {
    *v2 = -858993460;
    v2 += 4;
  }
  sub_1400012A8(&unk_1400140F4);                // 检测调试,需要给patch掉,直接进去把里面的代码改成 ret
  dwProcessId = GetCurrentProcessId();
  v8 = OpenProcess(0x1FFFFFu, 0, dwProcessId);
  if ( argv_value != 2 )                        // 必须有2个参数
    exit(0);
  for ( j = 0; ; ++j )
  {
    v19 = j;
    if ( j >= strlen(argv[1]) )
      break;
    Parameter[j] = argv[1][j];                  // 将第一个参数赋值给 Parameter
                                                // bbre.exe flagstrings
  }
  hResInfo = FindResourceW(0i64, 0x65, L"cod");
  LODWORD(Size) = SizeofResource(0i64, hResInfo);
  hResData = LoadResource(0i64, hResInfo);
  Src = LockResource(hResData);
  data = malloc(Size);
  memcpy(data, Src, Size);
  Jmp_Xor_data(data);                           // 这里一个异或加解密的操作 长度是 874
  v16 = qword_14000FC30(v8, 0i64, 874i64, 4096i64, 64);
  qword_14000FC28(v8, v16, data, 874i64, 0i64);
  v17 = qword_14000FC18(v8, 0i64, 0i64, v16, Parameter, 0, 0i64);// Parameter 只有在这个函数才被加密
  Sleep(0x1F4u);
  for ( k = 0; k < 44; ++k )
    ;
  CreateThread(0i64, 0i64, StartAddress, Parameter, 0, 0i64);// startAddress 是一个检测flag的 
  Sleep(0x190u);
  sub_140001253(v5, &unk_14000BEE0);
  return 0i64;
}


原来还可以这样读资源文件

image

  • Xor_data()

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall Xor_data(char a1[])
{
  __int64 result; // rax
  int i; // [rsp+24h] [rbp+4h]

  result = sub_1400012A8(&unk_1400140F4);
  for ( i = 0; i < 874; ++i )
  {
    a1[i] ^= byte_14000F000[i % 4];
    result = (unsigned int)(i + 1);
  }
  return result;
}

需要注意的是 byte_14000F000,

image

如果 程序处于调试状态他会修改 byte_14000F000,这里等会给它 patch 掉

image

  • IsDebuggerPresent()
1
2
3
#返回值
如果当前进程在调试器上下文中运行则返回值为非零
如果当前进程未在调试器的上下文中运行则返回值为零

byte_14000F000 初始值是

18h, 57h, 68h, 64h

经过动态调试后 才发现执行完 Jmp_Xor_data() 后, data 是一堆代码

image

  • 动态调试需要传参

image

直接去看 data,data里存的地址才是数据

image

如果说你看到 or [rcx+43F8ECh], al​ 就说明,初始的 byte_14000F000 还是被改了

这样的话,可以断点在异或解密的前一条直接,然后把 byte_14000F000 改回去

image

随便打个断点

image

直接给它改回去

image

然后继续运行动态解密完后我们在看下data

有些花指令,我们给它处理一下

image

花指令

image

出来完花指令后 快捷键p 创建函数 然后可以直接F5 看C 伪代码和里面我写好的注释

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
__int64 __fastcall sub_22777CDEDAD(char a1[])
{
  char *v1; // rdi
  __int64 i; // rcx
  __int64 result; // rax
  char v4; // [rsp+20h] [rbp+0h] BYREF
  char v5[292]; // [rsp+30h] [rbp+10h] BYREF
  unsigned int k; // [rsp+154h] [rbp+134h]
  unsigned int v7; // [rsp+174h] [rbp+154h]
  unsigned int v8; // [rsp+194h] [rbp+174h]
  int v9; // [rsp+1B4h] [rbp+194h]
  char v10[44]; // [rsp+1D8h] [rbp+1B8h] BYREF
  unsigned int v11; // [rsp+204h] [rbp+1E4h]
  char *__attribute__((__org_arrdim(0,0))) j; // [rsp+228h] [rbp+208h]
  int v13; // [rsp+244h] [rbp+224h]
  __int64 v14; // [rsp+3D8h] [rbp+3B8h]

  v1 = &v4;
  for ( i = 150i64; i; --i )
  {
    *v1 = -858993460;
    v1 += 4;
  }
  memset(v5, 0, 0x101ui64);
  qmemcpy(v10, "]Bb)", 4);
  v10[4] = 3;
  v10[5] = 54;
  v10[6] = 71;
  v10[7] = 65;
  v10[8] = 21;
  v10[9] = 54;
  v11 = 0;
  for ( j = a1; *j; ++j )
    ++v11;
  for ( k = 0; k < 0x100; ++k )
    v5[k] = k;
  v8 = 0;
  v7 = 0;
  for ( k = 0; k < 0x100; ++k )
  {
    v9 = v5[k];
    v7 = (v10[v8] + v9 + 2 * v7) % 0x100;
    v5[k] = v5[v7];
    v5[v7] = v9;
    if ( ++v8 >= 0xA )
      v8 = 0;
  }
  v8 = 0;
  sub_22777CDEF9E();                            // 没用,等会nop掉; 随便在 nop 处下个断点,来获取上面生成v5的值,用来给下面的解密
  v7 = v8;
  for ( k = 0; ; ++k )
  {
    result = v11;
    if ( k >= v11 )
      break;
    v7 = (v8 + v7) % 0x100;
    v8 = (v5[v7] + v8) % 0x100;
    v9 = v5[v7];
    v5[v7] = v5[v8];
    v5[v8] = v9;
    v13 = v5[(v5[v7] + v8 + v5[v8]) % 0x100];
    a1[k] ^= v13;                               // 加密处,毫无疑问 a1 就是我们传入的flag
    v14 = k;
    a1[k] += k % 0xD;
  }
  return result;
}

nop 掉 call sub_22777CDEF9E

image

然后打断点,然后F9运行到这

imageF9 后,然后把这个int 3 也 nop 掉, 接下在上面的开头处 快捷键p ,然后f5 看C 伪代码

image

再把上面的 v5 给他提取出来

image

密文在Xor_data()函数里可以找到

image

然后写解密脚本

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
v5 = [
  0x04, 0xFD, 0x67, 0xF4, 0x3F, 0x13, 0xC6, 0x86, 0x29, 0xAA, 
  0x89, 0x68, 0x93, 0x41, 0x6B, 0x9F, 0x95, 0x7C, 0x96, 0x87, 
  0x0C, 0x1C, 0x18, 0x7E, 0x23, 0x51, 0x2B, 0x72, 0x55, 0x94, 
  0x75, 0x3E, 0xFE, 0x00, 0x16, 0x30, 0x9B, 0x9C, 0xF6, 0x43, 
  0x5F, 0x69, 0x85, 0xE2, 0xE7, 0xAB, 0x7A, 0x5A, 0x0A, 0xA6, 
  0x81, 0x77, 0x17, 0x6C, 0xF2, 0x33, 0x6D, 0x35, 0x49, 0x0B, 
  0x61, 0x06, 0x34, 0xD7, 0x4B, 0x9E, 0xA3, 0x3C, 0xB5, 0x5C, 
  0x48, 0xB3, 0x05, 0xDC, 0xA7, 0x80, 0x71, 0x09, 0xAE, 0x28, 
  0x5B, 0xA8, 0xE6, 0x44, 0x14, 0xE3, 0x63, 0x4E, 0xF3, 0x1E, 
  0xC1, 0xDF, 0x26, 0x46, 0x07, 0x2F, 0xB1, 0x0D, 0xED, 0x6A, 
  0x19, 0xD1, 0xDE, 0x5E, 0x6F, 0x1D, 0x97, 0xC8, 0x66, 0xD6, 
  0x7B, 0xA0, 0x62, 0x3A, 0x40, 0xC5, 0x59, 0x1B, 0xCF, 0x83, 
  0x50, 0xC2, 0x8F, 0x58, 0xE5, 0xEA, 0x84, 0x38, 0x11, 0xC9, 
  0x37, 0x2E, 0xCE, 0xB8, 0x10, 0x90, 0xD9, 0x98, 0x45, 0xCD, 
  0xD5, 0x03, 0x57, 0x99, 0x25, 0x08, 0x74, 0xDD, 0x7D, 0xD4, 
  0x24, 0x12, 0x22, 0xB9, 0xBE, 0x0E, 0x4A, 0x20, 0xAF, 0xB4, 
  0x4F, 0x21, 0xF5, 0xCA, 0xEB, 0x0F, 0x9D, 0x36, 0xD0, 0xC3, 
  0x91, 0x3B, 0x2C, 0xEE, 0x1F, 0xE0, 0xB7, 0x70, 0xA2, 0x56, 
  0x9A, 0xB2, 0xA1, 0x60, 0xC0, 0xCC, 0xAC, 0x7F, 0xF9, 0x8A, 
  0xBD, 0xCB, 0x8C, 0xB6, 0x8D, 0xDB, 0x39, 0xFC, 0xD3, 0x88, 
  0xDA, 0x4D, 0x78, 0xFB, 0xBF, 0x1A, 0x02, 0x76, 0xBC, 0x47, 
  0x64, 0xE4, 0xA4, 0x4C, 0x15, 0x5D, 0xD2, 0xA5, 0x2D, 0xAD, 
  0xF7, 0xBA, 0x54, 0xF0, 0x8E, 0xA9, 0xEC, 0x52, 0x27, 0x42, 
  0xD8, 0xBB, 0xB0, 0x53, 0x31, 0x82, 0x8B, 0xEF, 0xC7, 0xF8, 
  0x32, 0xF1, 0xE9, 0xE8, 0x01, 0xFF, 0xC4, 0xFA, 0xE1, 0x65, 
  0x79, 0x73, 0x92, 0x6E, 0x2A, 0x3D, 0x00
  ]

flag = [0] * 45
flag[0] = 0xF7
flag[1] = 0x2E
flag[2] = 0x34
flag[3] = 0xF0
flag[4] = 0x72
flag[5] = 0xCF
flag[6] = 94
flag[7] = 10
flag[8] = 0xBB
flag[9] = 0xEC
flag[10] = 0xB1
flag[11] = 0x2B
flag[12] = 0x70
flag[13] = 0x88
flag[14] = 0x88
flag[15] = 0xED
flag[16] = 0x46
flag[17] = 0x38
flag[18] = 0xDB
flag[19] = 0xDA
flag[20] = 0x6C
flag[21] = 0xBD
flag[22] = 0xD4
flag[23] = 6
flag[24] = 0x77
flag[25] = 0xF2
flag[26] = 0xCF
flag[27] = 0x56
flag[28] = 0x88
flag[29] = 0xC6
flag[30] = 0x31
flag[31] = 0xD2
flag[32] = 0xB7
flag[33] = 90
flag[34] = 0xC1
flag[35] = 0x42
flag[36] = 0xB0
flag[37] = 0xF4
flag[38] = 72
flag[39] = 0x37
flag[40] = 0xF5
flag[41] = 0x2C
flag[42] = 245
flag[43] = 88


v8 = 0
v7 = 0
for k in range(44):
    v7 = (v8 + v7) % 0x100
    v8 = (v5[v7] + v8) % 0x100
    v9 = v5[v7]
    v5[v7] = v5[v8]
    v5[v8] = v9
    v13 = v5[(v5[v7] + v8 + v5[v8]) % 0x100]
    flag[k] -= k % 0xD
    flag[k] ^= v13
    flag[k] = flag[k] % 128
print(flag)

print(''.join(map(chr,flag)))
1
2
[68, 65, 83, 67, 84, 70, 123, 48, 51, 52, 52, 54, 99, 50, 99, 45, 100, 102, 102, 55, 45, 49, 49, 101, 100, 45, 57, 50, 56, 53, 45, 53, 52, 101, 49, 97, 100, 57, 56, 100, 54, 52, 57, 125, 0]
DASCTF{03446c2c-dff7-11ed-9285-54e1ad98d649}

Reverse-ez_exe

一共三个文件

image

ez_exe.exe 是 python3.11 打包的

image

用 python3.11 执行 pyinstxtractor.py 去解包

image

再用 Pycdc 去还原 python源代码

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
#=[09:17:03]-➤  /opt/reverse/Pycdc ez_py.pyc 
# Source Generated with Decompyle++
# File: ez_py.pyc (Python 3.11)

import ctypes
from time import *
from ctypes import *
from ctypes import wintypes
from hashlib import md5

class _STARTUPINFO(Structure):
    _fields_ = [
        ('cb', c_ulong),
        ('lpReserved', c_char_p),
        ('lpDesktop', c_char_p),
        ('lpTitle', c_char_p),
        ('dwX', c_ulong),
        ('dwY', c_ulong),
        ('dwXSize', c_ulong),
        ('dwYSize', c_ulong),
        ('dwXCountChars', c_ulong),
        ('dwYCountChars', c_ulong),
        ('dwFillAttribute', c_ulong),
        ('dwFlags', c_ulong),
        ('wShowWindow', c_ushort),
        ('cbReserved2', c_ushort),
        ('lpReserved2', c_char_p),
        ('hStdInput', c_ulong),
        ('hStdOutput', c_ulong),
        ('hStdError', c_ulong)]


class _PROCESS_INFORMATION(Structure):
    _fields_ = [
        ('hProcess', c_void_p),
        ('hThread', c_void_p),
        ('dwProcessId', c_ulong),
        ('dwThreadId', c_ulong)]

StartupInfo = _STARTUPINFO()
ProcessInfo = _PROCESS_INFORMATION()
key1 = bytes(md5(b'bin1bin1bin1').hexdigest().encode())
file = open('bin1', 'rb').read()
arr = range(len(file))()
open('bin1', 'wb').write(bytes(arr))
sleep(0)
bet = ctypes.windll.kernel32.CreateProcessA(b'bin1', ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), ctypes.c_int(0), byref(StartupInfo), byref(ProcessInfo))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ProcessInfo.hProcess), ctypes.c_int(-1))
open('bin1', 'wb').write(file)

直接看的话,好像有点奇怪,key1 都没有调用的地方

主要还是解密了bin1, 用 python3.11 运行 ez_py.pyc,会生成一个 gmon.out

image

运行后,bin1 会从密文状态解密成一个 PE32 可执行程序,

image

image

HeD 打开 这个 ez_py.pyc 然后将 bin1 替换成bin2, 然后再去运行这个 ez_py.pyc

image

也是意料之中成长解密了bin2也运行了

image

image

接下来用 IDA Pro 分析 bin2

一个 btea 加密

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int flag[11]; // [esp+10h] [ebp-B8h]
  int key[4]; // [esp+3Ch] [ebp-8Ch] BYREF
  int v6[12]; // [esp+4Ch] [ebp-7Ch] BYREF
  char Str[50]; // [esp+7Eh] [ebp-4Ah] BYREF
  int k; // [esp+B0h] [ebp-18h]
  int j; // [esp+B4h] [ebp-14h]
  size_t i; // [esp+B8h] [ebp-10h]
  char *v11; // [esp+BCh] [ebp-Ch]

  __main();
  printf("please input your flag:\n");
  scanf("%s", Str);
  if ( strlen(Str) != 44 )
  {
    MessageBoxA(0, "worng length!", &Caption, 0);
    exit(0);
  }
  v11 = Str;
  for ( i = 0; strlen(Str) >> 2 > i; ++i )
  {
    v6[i] = *(_DWORD *)v11;
    v11 += 4;
  }
  key[0] = 0x4B5F;
  key[1] = 0xDEAD;
  key[2] = 0x11ED;
  key[3] = 0xB3CC;
  btea((unsigned int *)v6, 11, key);            // 加密
  for ( j = 0; j <= 10; ++j )
    ;
  flag[0] = 0xCC45699D;
  flag[1] = 0x683D5352;
  flag[2] = 0xB8BB71A0;
  flag[3] = 0xD3817AD;
  flag[4] = 0x7547E79E;
  flag[5] = 0x4BDD8C7C;
  flag[6] = 0x95E25A81;
  flag[7] = 0xC4525103;
  flag[8] = 0x7049B46F;
  flag[9] = 0x5417F77C;
  flag[10] = 0x65567138;
  for ( k = 0; k <= 10; ++k )
  {
    if ( flag[k] != v6[k] )
    {
      MessageBoxA(0, "error!", &Caption, 0);
      exit(0);
    }
  }
  MessageBoxA(0, "right!", &Caption, 0);
  return 0;
}
  • btea
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
int __cdecl btea(unsigned int a1[], int a2, int a3[])
{
  unsigned int *v3; // eax
  int *v4; // eax
  int result; // eax
  unsigned int *v6; // eax
  int v7; // [esp+8h] [ebp-20h]
  int v8; // [esp+8h] [ebp-20h]
  int v9; // [esp+Ch] [ebp-1Ch]
  int v10; // [esp+Ch] [ebp-1Ch]
  unsigned int j; // [esp+10h] [ebp-18h]
  int i; // [esp+10h] [ebp-18h]
  unsigned int v13; // [esp+14h] [ebp-14h]
  unsigned int v14; // [esp+14h] [ebp-14h]
  unsigned int v15; // [esp+18h] [ebp-10h]
  unsigned int v16; // [esp+1Ch] [ebp-Ch]
  int a2a; // [esp+34h] [ebp+Ch]

  if ( a2 <= 1 )
  {
    if ( a2 < -1 )
    {
      a2a = -a2;
      v10 = 52 / a2a + 6;
      v14 = 2033695134 * v10;
      v16 = *a1;
      do
      {
        v8 = (v14 >> 2) & 3;
        for ( i = a2a - 2; i; --i )
        {
          v6 = &a1[i];
          *v6 -= ((v16 ^ v14) + (a1[i - 1] ^ a3[v8 ^ i & 3])) ^ (((4 * v16) ^ (a1[i - 1] >> 5))
                                                               + ((v16 >> 3) ^ (16 * a1[i - 1])));
          v16 = *v6;
        }
        *a1 -= (((4 * v16) ^ (a1[a2a - 1] >> 5)) + ((v16 >> 3) ^ (16 * a1[a2a - 1]))) ^ ((v16 ^ v14)
                                                                                       + (a1[a2a - 1] ^ a3[v8]));
        result = *a1;
        v16 = *a1;
        v14 -= 2033695134;
        --v10;
      }
      while ( v10 );
    }
  }
  else
  {
    v9 = 52 / a2;
    v13 = 0;
    v15 = a1[a2 - 1];
    do
    {
      v13 += 0x7937B99E;
      v7 = (v13 >> 2) & 3;
      for ( j = 0; j < a2 - 1; ++j )
      {
        v3 = &a1[j];
        *v3 += ((a1[j + 1] ^ v13) + (v15 ^ a3[v7 ^ j & 3])) ^ (((4 * a1[j + 1]) ^ (v15 >> 5))
                                                             + ((a1[j + 1] >> 3) ^ (16 * v15)));
        v15 = *v3;
      }
      v4 = (int *)&a1[a2 - 1];
      *v4 += ((*a1 ^ v13) + (v15 ^ a3[v7 ^ j & 3])) ^ (((4 * *a1) ^ (v15 >> 5)) + ((*a1 >> 3) ^ (16 * v15)));
      result = *v4;
      v15 = result;
      --v9;
    }
    while ( v9 );
  }
  return result;
}

baidu 到了俩解密的,需要修改部分代码,也简单,改完解密直接flag

  • btea解密脚本1
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define MX (z>>5^y<<2) + (y>>3^z<<4)^(sum^y) + (k[p&3^e]^z);
 
//long btea(long* v, long n, long* k) {
int btea(int* v, int n, int* k) {
	unsigned long z=v[n-1], y=v[0], sum=0, e, DELTA=0x7937B99E;
	long p, q ;
	if (n > 1) {          /* Coding Part */
		//q = 6 + 52/n;
		q = 52/n;
		while (q-- > 0) {
			sum += DELTA;
			e = (sum >> 2) & 3;
			for (p=0; p<n-1; p++) y = v[p+1], z = v[p] += MX;
			y = v[0];
			z = v[n-1] += MX;
		}
		return 0 ; 
	} else if (n < -1) {  /* Decoding Part */
		n = -n;
		//q = 6 + 52/n;
		q =  52/n;
		sum = q*DELTA ;
		while (sum != 0) {
			e = (sum >> 2) & 3;
			for (p=n-1; p>0; p--) z = v[p-1], y = v[p] -= MX;
			z = v[n-1];
			y = v[0] -= MX;
			sum -= DELTA;
		}
		return 0;
	}
	return 1;
}

int main(int argc, char const *argv[])
{
    int key[4]={0};
    key[0] = 0x4B5F;
    key[1] = 0xDEAD;
    key[2] = 0x11ED;
    key[3] = 0xB3CC;
    int flag[11] = {0};
    flag[0] = 0xCC45699D;
    flag[1] = 0x683D5352;
    flag[2] = 0xB8BB71A0;
    flag[3] = 0xD3817AD;
    flag[4] = 0x7547E79E;
    flag[5] = 0x4BDD8C7C;
    flag[6] = 0x95E25A81;
    flag[7] = 0xC4525103;
    flag[8] = 0x7049B46F;
    flag[9] = 0x5417F77C;
    flag[10] = 0x65567138;

    btea(flag,-11,key);
    printf("%s\n",flag);

    return 0;
}
  • btea解密脚本2
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
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x7937B99E
// #define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
 
  void btea(uint32_t *v, int n, uint32_t const key[4]) {
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1) {          /* Coding Part */
      rounds = 52/n;
      //rounds = 6 + 52/n;
      sum = 0;
      z = v[n-1];
      do {
        sum += DELTA;
        e = (sum >> 2) & 3;
        for (p=0; p<n-1; p++) {
          y = v[p+1];
          z = v[p] += MX;
        }
        y = v[0];
        z = v[n-1] += MX;
      } while (--rounds);
    } else if (n < -1) {  /* Decoding Part */
      n = -n;
      rounds = 52/n;
      //rounds = 6 + 52/n;
      sum = rounds*DELTA;
      y = v[0];
      do {
        e = (sum >> 2) & 3;
        for (p=n-1; p>0; p--) {
          z = v[p-1];
          y = v[p] -= MX;
        }
        z = v[n-1];
        y = v[0] -= MX;
      } while ((sum -= DELTA) != 0);
    }
  }
 
int main()
{
    //uint32_t const key[4]={0x01234567,0x89ABCDEF,0xFEDCBA98,0x76543210};
    uint32_t key[4] = {0};
    key[0] = 0x4B5F;
    key[1] = 0xDEAD;
    key[2] = 0x11ED;
    key[3] = 0xB3CC;
    //uint32_t data[2]={0x12345678,0x87654321};
    uint32_t data[11];
    data[0] = 0xCC45699D;
    data[1] = 0x683D5352;
    data[2] = 0xB8BB71A0;
    data[3] = 0x0D3817AD;
    data[4] = 0x7547E79E;
    data[5] = 0x4BDD8C7C;
    data[6] = 0x95E25A81;
    data[7] = 0xC4525103;
    data[8] = 0x7049B46F;
    data[9] = 0x5417F77C;
    data[10]= 0x65567138;
    uint32_t *sent=data;
    //btea(sent,1,key);

    btea(sent,-11,key);
    //printf("%s\n\n",data);
    for (int i = 0; i < 44; i++){
		printf("%c",*((char *)data +i) & 0xff);
    }
    return 0;
}

1
DASCTF{7eb20cb2-deac-11ed-ae42-94085339ce84}

Reverse-cap

capture what?

  • 主要逻辑代码
  • 大概就是获取屏幕的图片 bmp 然后加密保存为cap.bin

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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
__int64 __fastcall sub_7FF651DA1030(HWND hWnd)
{
  HBITMAP v2; // r14
  HDC hdcSrc; // r13
  HDC DC; // rsi
  HDC CompatibleDC; // r15
  int hSrc; // ebx
  int wSrc; // eax
  HBITMAP CompatibleBitmap; // rax
  signed int v9; // ebx
  HANDLE FileW; // rax
  void *v11; // r12
  signed int v12; // r10d
  _BYTE *v13; // r9
  int v14; // ecx
  int v15; // edx
  void *lpBuffer; // [rsp+60h] [rbp-59h]
  HGLOBAL hMem; // [rsp+68h] [rbp-51h]
  struct tagRECT Rect; // [rsp+70h] [rbp-49h] BYREF
  struct tagBITMAPINFO bmi; // [rsp+80h] [rbp-39h] BYREF
  char v21; // [rsp+ACh] [rbp-Dh]
  char v22; // [rsp+ADh] [rbp-Ch]
  char v23; // [rsp+AEh] [rbp-Bh]
  char v24; // [rsp+AFh] [rbp-Ah]
  char v25; // [rsp+B0h] [rbp-9h]
  char v26; // [rsp+B1h] [rbp-8h]
  int v27; // [rsp+B2h] [rbp-7h]
  DWORD NumberOfBytesWritten; // [rsp+B8h] [rbp-1h] BYREF
  char pv[4]; // [rsp+C0h] [rbp+7h] BYREF
  LONG v30; // [rsp+C4h] [rbp+Bh]
  UINT cLines; // [rsp+C8h] [rbp+Fh]

  NumberOfBytesWritten = 0;
  v2 = 0i64;
  hdcSrc = GetDC(0i64);
  DC = GetDC(hWnd);                             // GetDC函数为一个指定窗口的客户端区域或者整个屏幕从一个设备上下文(DC)中提取一个句柄
  CompatibleDC = CreateCompatibleDC(DC);
  if ( CompatibleDC )
  {
    GetClientRect(hWnd, &Rect);
    SetStretchBltMode(DC, 4);
    hSrc = GetSystemMetrics(1);
    wSrc = GetSystemMetrics(0);
    if ( StretchBlt(DC, 0, 0, Rect.right, Rect.bottom, hdcSrc, 0, 0, wSrc, hSrc, 0xCC0020u) )
    {
      CompatibleBitmap = CreateCompatibleBitmap(DC, Rect.right - Rect.left, Rect.bottom - Rect.top);// CreateCompatibleBitmap函数创建的位图的颜色格式与hdc参数标识的设备的颜色格式匹配
      v2 = CompatibleBitmap;
      if ( CompatibleBitmap )
      {
        SelectObject(CompatibleDC, CompatibleBitmap);
        if ( BitBlt(CompatibleDC, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top, DC, 0, 0, 0xCC0020u) )
        {
          GetObjectW(v2, 32, pv);
          bmi.bmiHeader.biWidth = v30;
          bmi.bmiHeader.biHeight = cLines;
          bmi.bmiHeader.biSize = 0x28;
          *&bmi.bmiHeader.biPlanes = 2097153i64;
          memset(&bmi.bmiHeader.biSizeImage, 0, 20);
          v9 = 4 * cLines * ((32 * v30 + 31) / 32);
          hMem = GlobalAlloc(0x42u, v9);
          lpBuffer = GlobalLock(hMem);
          GetDIBits(DC, v2, 0, cLines, lpBuffer, &bmi, 0);
          FileW = CreateFileW(L"cap.bin", 0x40000000u, 0, 0i64, 2u, 0x80u, 0i64);
          v23 ^= 0x64u;
          v24 ^= 0x61u;
          v11 = FileW;
          v25 ^= 0x73u;
          v26 ^= 0x63u;
          bmi.bmiHeader.biSize ^= 0x79625F63u;
          bmi.bmiHeader.biWidth ^= 0x7361645Fu;
          bmi.bmiHeader.biHeight ^= 0x65667463u;
          *&bmi.bmiHeader.biPlanes ^= 7017839094296175470ui64;
          bmi.bmiColors[0].rgbReserved = ((v9 + 54) >> 8) ^ 0x62;
          v21 = ((v9 + 54) >> 16) ^ 0x79;
          v22 = ((v9 + 54) >> 24) ^ 0x5F;
          v27 = 1852139074;
          qmemcpy(bmi.bmiColors, ",.", 2);
          bmi.bmiColors[0].rgbRed = (v9 + 54) ^ 0x5F;
          v12 = 0;
          bmi.bmiHeader.biSizeImage ^= 0x66746373u;
          bmi.bmiHeader.biXPelsPerMeter ^= 0x5F636E65u;
          bmi.bmiHeader.biYPelsPerMeter ^= 0x645F7962u;
          bmi.bmiHeader.biClrUsed ^= 0x74637361u;
          bmi.bmiHeader.biClrImportant ^= 0x636E6566u;
          if ( v9 > 0 )
          {
            v13 = lpBuffer;
            do
            {
              v14 = v12 + 3;
              v15 = (1321528399i64 * (v12 + 3)) >> 32;
              ++v12;
              *v13++ ^= key[v14 - 13 * ((v15 >> 31) + (v15 >> 2))];
            }
            while ( v12 < v9 );
          }
          WriteFile(FileW, bmi.bmiColors, 0xEu, &NumberOfBytesWritten, 0i64);
          WriteFile(v11, &bmi, 0x28u, &NumberOfBytesWritten, 0i64);
          WriteFile(v11, lpBuffer, v9, &NumberOfBytesWritten, 0i64);
          GlobalUnlock(hMem);
          GlobalFree(hMem);
          CloseHandle(v11);
        }
        else
        {
          MessageBoxW(hWnd, L"BitBlt has failed", L"Failed", 0);
        }
      }
      else
      {
        MessageBoxW(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", 0);
      }
    }
    else
    {
      MessageBoxW(hWnd, L"StretchBlt has failed", L"Failed", 0);
    }
  }
  else
  {
    MessageBoxW(hWnd, L"CreateCompatibleDC has failed", L"Failed", 0);
  }
  DeleteObject(v2);
  DeleteObject(CompatibleDC);
  ReleaseDC(0i64, hdcSrc);
  ReleaseDC(hWnd, DC);
  return 0i64;
}

最后写入的lpBuffer也就是bmp 图像的数据段

v3 指向的地址也就是 plBuffer

  • 接下来就是找到v3 是和谁异或的,取得是key[]的值。动态分析一下把和 v13 异或的值取出来

1
*v13++ ^= key[v14 - 13 * ((v15 >> 31) + (v15 >> 2))];

image

  • 断点脚本
1
2
eax = get_reg_value('eax')
print(chr(eax),end='')

基本就是按照这个循环去异或

1
_by_dasctfenc

  • 先拿一个正常的BMP 看一下结构

image

image

现在除了前0x36不知道是啥,但是后面的信息我们已经清楚加密流程了

  • 解密
1
2
3
4
5
6
7
8
9
10
11
key = list(b'_by_dasctf')

f1 = open('C:/Users/GameG/Downloads/cap/cap.bin','rb').read()[0x36:]
f2 = open('C:/Users/GameG/Downloads/cap/data.bmp','wb')

f1 = bytearray(f1)
for i in range(len(f1)):
    f1[i] = f1[i] ^ key[i % len(key)]
f2.write(bytes([0]*0x36))
f2.write(bytes(f1))
f2.close()

  • 结果,考虑到这里的数据是异或加密的,那前面的也可以尝试解一下

image

  • 正常的bmp 这个地方都是00 00 , 但加密后 是 我们的 密钥 enc_by_dasctf ,任何数异或0都是它本身,正好呢,0x36位置异或的是 _ 正好和前面的 enc 衔接上,大概前面也是异或,只是异或key的顺序不同

image

1
2
3
4
5
6
7
8
9
10
11
key = list(b'nc_by_dasctfe')

f1 = open('C:/Users/GameG/Downloads/cap/cap.bin','rb').read()
f2 = open('C:/Users/GameG/Downloads/cap/data.bmp','wb')

f1 = bytearray(f1)
for i in range(len(f1)):
    f1[i] = f1[i] ^ key[i % len(key)]
f2.write(bytes(f1))
f2.close()

得到 原始图片

image

Reverse-unsym

看到字符串里有 go 的特征,先用脚本恢复一下go符号https://github.com/renshareck/IDAGolangHelper_SupportGo1.20

image

用 Findcrypt 找到了一些加密算法 AES

image

NoNo~~~~~~~~~~~

本文由作者按照 CC BY 4.0 进行授权

© imLZH1. 保留部分权利。

本站总访问量

本站采用 Jekyll 主题 Chirpy

热门标签