0xGame2025Week4WriteUp
0xGame2025 Week4 Reverse方向全解&详解
镜渊折枝
考点:native层逆向,AES
附件apk运行起来是这样:

JEB打开,可以得到下面这些MainActivity和相关函数的内容:
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
118package com.example.easynative;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.activity.ComponentActivity;
import java.io.InputStream;
import kotlin.Metadata;
import kotlin.io.ByteStreamsKt;
import kotlin.jvm.internal.Intrinsics;
@Metadata(d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001A\u00020\u00042\b\u0010\u0005\u001A\u0004\u0018\u00010\u0006H\u0014¨\u0006\u0007"}, d2 = {"Lcom/example/easynative/MainActivity;", "Landroidx/activity/ComponentActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "app_release"}, k = 1, mv = {1, 9, 0}, xi = 0x30)
public final class MainActivity extends ComponentActivity {
public static final int $stable;
static {
}
@Override // androidx.activity.ComponentActivity
protected void onCreate(Bundle arg3) {
super.onCreate(arg3);
this.setContentView(layout.activity_main);
EditText v3 = (EditText)this.findViewById(id.flagInput);
((Button)this.findViewById(id.button)).setOnClickListener((View arg2) -> MainActivity.onCreate$lambda$0(v3, this, arg2));
}
private static final void onCreate$lambda$0(EditText arg2, MainActivity arg3, View arg4) {
Intrinsics.checkNotNullParameter(arg3, "this$0");
String v2 = arg2.getText().toString();
InputStream v4 = arg3.getAssets().open("secret.bin");
Intrinsics.checkNotNullExpressionValue(v4, "open(...)");
byte[] v4_1 = ByteStreamsKt.readBytes(v4);
if(((CharSequence)v2).length() > 0) {
Context v1 = (Context)arg3;
if(new FlagChecker(v1, v2).check(v4_1)) { // v4从secret.bin中被读取出来,读取的内容给了v4_1作为check的参数,输入的内容作为FlagChecker的第二个参数
Toast.makeText(v1, ((CharSequence)arg3.getString(string.correct)), 0).show();
return;
}
Toast.makeText(v1, ((CharSequence)arg3.getString(string.wrong)), 0).show();
}
}
}
package com.example.easynative;
import android.content.Context;
import java.security.Key;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import kotlin.Metadata;
import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.jvm.internal.Intrinsics;
import kotlin.text.Charsets;
@Metadata(d1 = {"\u0000.\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u000E\n\u0002\b\u0002\n\u0002\u0010\u000B\n\u0000\n\u0002\u0010\u0012\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0005\b\u0007\u0018\u0000 \u00112\u00020\u0001:\u0001\u0011B\u0015\u0012\u0006\u0010\u0002\u001A\u00020\u0003\u0012\u0006\u0010\u0004\u001A\u00020\u0005¢\u0006\u0002\u0010\u0006J\u000E\u0010\u0007\u001A\u00020\b2\u0006\u0010\t\u001A\u00020\nJ\u0019\u0010\u000B\u001A\u00020\b2\u0006\u0010\f\u001A\u00020\r2\u0006\u0010\u000E\u001A\u00020\nH\u0086 J\u001E\u0010\u000F\u001A\u00020\n2\u0006\u0010\u0004\u001A\u00020\u00052\u0006\u0010\u0010\u001A\u00020\n2\u0006\u0010\t\u001A\u00020\nR\u000E\u0010\u0002\u001A\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000E\u0010\u0004\u001A\u00020\u0005X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u0012"}, d2 = {"Lcom/example/easynative/FlagChecker;", "", "context", "Landroid/content/Context;", "plainText", "", "(Landroid/content/Context;Ljava/lang/String;)V", "check", "", "iv", "", "compare", "length", "", "data", "encrypt", "key", "Companion", "app_release"}, k = 1, mv = {1, 9, 0}, xi = 0x30)
public final class FlagChecker {
@Metadata(d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002¨\u0006\u0003"}, d2 = {"Lcom/example/easynative/FlagChecker$Companion;", "", "()V", "app_release"}, k = 1, mv = {1, 9, 0}, xi = 0x30)
public static final class Companion {
private Companion() {
}
public Companion(DefaultConstructorMarker arg1) {
}
}
public static final int $stable;
public static final Companion Companion;
private final Context context;
private final String plainText;
static {
FlagChecker.Companion = new Companion(null);
FlagChecker.$stable = 8;
System.loadLibrary("easynative");
}
public FlagChecker(Context arg2, String arg3) {
Intrinsics.checkNotNullParameter(arg2, "context");
Intrinsics.checkNotNullParameter(arg3, "plainText");
super();
this.context = arg2;
this.plainText = arg3; // 输入的内容被赋值给plaintext
}
public final boolean check(byte[] arg4) { // 参数是从secret.bin读取的内容
Intrinsics.checkNotNullParameter(((Object)arg4), "iv"); // arg4即从secret.bin读取的内容被标识为iv
String v0 = this.context.getString(string.key); // v0为string.key
Intrinsics.checkNotNullExpressionValue(v0, "getString(...)");
String v1 = this.plainText; // v1是输入的内容plaintext
byte[] v0_1 = v0.getBytes(Charsets.UTF_8); // v0读取为bytes
Intrinsics.checkNotNullExpressionValue(((Object)v0_1), "this as java.lang.String).getBytes(charset)");
byte[] v4 = this.encrypt(v1, v0_1, arg4); // 三个参数分别为:输入的plaintext,string.key,从secret.bin读取的iv
return this.compare(v4.length, v4); // compare从native层easynative.so导入
}
public final native boolean compare(int arg1, byte[] arg2) {
}
public final byte[] encrypt(String arg3, byte[] arg4, byte[] arg5) {
Intrinsics.checkNotNullParameter(arg3, "plainText"); // 此处再次对参数进行了标识
Intrinsics.checkNotNullParameter(((Object)arg4), "key");
Intrinsics.checkNotNullParameter(((Object)arg5), "iv");
SecretKeySpec v0 = new SecretKeySpec(arg4, "AES"); // 加密算法AES,CTR模式
Cipher v4 = Cipher.getInstance("AES/CTR/NoPadding");
v4.init(1, ((Key)v0), ((AlgorithmParameterSpec)new IvParameterSpec(arg5)));
byte[] v3 = arg3.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(((Object)v3), "this as java.lang.String).getBytes(charset)");
byte[] v3_1 = v4.doFinal(v3);
Intrinsics.checkNotNull(((Object)v3_1));
return v3_1;
}
}可以看到Mainactivity使用了FlagChecker来校验,而FlagChecker对输入的内容进行了AES加密,然后用native层的compare函数来检验加密结果。下面是native层compare函数的内容(IDA中显示的函数名是check而非compare):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19__int64 __fastcall check(JNIEnv *a1, __int64 a2, int a3, void *a4)
{
__int64 v5; // x0
__int64 i; // x9
int v7; // w11
int v8; // w10
v5 = (__int64)(*a1)->GetByteArrayElements(a1, a4, 0LL);
if ( a3 != 48 )
return 0LL;
for ( i = 0LL; i != 48; ++i )
{
v7 = (unsigned __int8)byte_5A2[i];
v8 = *(unsigned __int8 *)(v5 + i) ^ (unsigned __int8)i;
if ( v8 != v7 )
return 0LL;
}
return 1LL;
}可以看到compare函数是先拿AES加密结果异或索引后才进行校验的,所以解密应该拿compare中的密文和索引异或得到AES加密的数据,然后找出key和iv进行AES解密。
脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13from Crypto.Cipher import AES
enc=[0x38, 0xEA, 0x9A, 0x5A, 0x3C, 0xFB, 0x0C, 0x98, 0x1A, 0x66, 0x8B, 0x46, 0x6A, 0x06, 0x19, 0x08, 0x41, 0xCB, 0x36, 0x9E, 0x16, 0xC5, 0xBE, 0x2F, 0xF0, 0x9D, 0xBF, 0xA7, 0x34, 0x67, 0x51, 0x4C, 0xE5, 0xC2, 0x78, 0xA5, 0x0F, 0x35, 0xFC, 0x1D, 0x39, 0x8B, 0x49, 0x38, 0x34, 0x69, 0x30, 0xF7]
#先对数据进行异或还原
for i in range(len(enc)):
enc[i]=enc[i]^i
#提取AES相关参数
key=b"0xgame-SecretKey"
iv=bytes.fromhex("8A A5 61 0C 74 41 1B 11 A3 53 68 DE 56 BF 6A B3")
#AES解密
cipher=AES.new(key, AES.MODE_CTR, nonce=b'', initial_value=iv)
flag=cipher.decrypt(bytes(enc))
print("flag: ",flag.decode())
flag:0xGame{Native_1s_also_1nt3r3st1ng_ef492d80a257c}
夜雀之歌
考点:迷宫题,动态调试,反调试
附件程序运行起来是这样的:

用IDA打开,通过Shift+F12字符串窗口可以找到一些信息定位main函数的位置

main函数有一大串的循环语句,结合上图终端输出的WASD可以推测这是一道迷宫逆向题。#是迷宫的墙,终端显示的是迷宫的一小部分,在终端按下WASD可以控制P移动。所以第一步要提取迷宫
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//main函数
__int64 sub_7FF6645A1D2B()
{
int v0; // eax
int v1; // eax
int v2; // eax
int v3; // eax
int v5; // [rsp+28h] [rbp-8h]
int v6; // [rsp+2Ch] [rbp-4h]
sub_7FF7EC581FC0();
sub_7FF7EC581B30();
sub_7FF7EC5815B6();
v6 = 0;
do
{
sub_7FF7EC581C5F();
sub_7FF7EC5815B6();
puts(&aWasdEsc);
v5 = getch();
sub_7FF7EC583060("%c\n", (unsigned int)v5);
if ( v5 == 119 )
{
LABEL_19:
sub_7FF7EC581BB7(0LL, 0xFFFFFFFFLL);
v0 = v6++;
byte_7FF7EC58E0E0[v0] = 119;
continue;
}
if ( v5 <= 119 )
{
if ( v5 == 115 )
goto LABEL_22;
if ( v5 <= 115 )
{
if ( v5 == 100 )
goto LABEL_21;
if ( v5 <= 100 )
{
if ( v5 == 97 )
goto LABEL_20;
if ( v5 <= 97 )
{
if ( v5 == 87 )
goto LABEL_19;
if ( v5 <= 87 )
{
if ( v5 == 83 )
{
LABEL_22:
sub_7FF7EC581BB7(0LL, 1LL);
v3 = v6++;
byte_7FF7EC58E0E0[v3] = 115;
continue;
}
if ( v5 <= 83 )
{
if ( v5 == 68 )
{
LABEL_21:
sub_7FF7EC581BB7(1LL, 0LL);
v2 = v6++;
byte_7FF7EC58E0E0[v2] = 100;
continue;
}
if ( v5 <= 68 )
{
if ( v5 == 27 )
return 0LL;
if ( v5 == 65 )
{
LABEL_20:
sub_7FF7EC581BB7(0xFFFFFFFFLL, 0LL);
v1 = v6++;
byte_7FF7EC58E0E0[v1] = 97;
}
}
}
}
}
}
}
}
}
while ( dword_7FF7EC58E0C4 != dword_7FF7EC58E0D4 || dword_7FF7EC58E0C8 != dword_7FF7EC58E0D8 );
sub_7FF7EC583060("Congratulations your flag is: 0xGame{md5(your_path)} (lowercase)");
return 0LL;
}点进LABEL_19下的第一个函数可以看到一个比较逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22__int64 __fastcall sub_7FF7EC581BB7(int a1, int a2)
{
int v3; // [rsp+24h] [rbp-Ch]
int v4; // [rsp+28h] [rbp-8h]
int v5; // [rsp+2Ch] [rbp-4h]
v5 = dword_7FF7EC58E0C4 + a1;
v4 = dword_7FF7EC58E0C8 + a2;
v3 = dword_7FF7EC58E0C4 + a1 + 101 * (dword_7FF7EC58E0C8 + a2);
if ( byte_7FF7EC589040[v3] == 35 )//比较逻辑
{
puts("不能移动到墙壁");
}
else
{
byte_7FF7EC589040[101 * dword_7FF7EC58E0C8 + dword_7FF7EC58E0C4] = 32;
dword_7FF7EC58E0C4 = v5;
dword_7FF7EC58E0C8 = v4;
byte_7FF7EC589040[v3] = 80;
}
return 0LL;
}在Hex View跟随比较逻辑中的这个数组的地址(7FF7EC589040)可以看到迷宫:

但是提取出迷宫会发现,这个迷宫和直接运行程序时终端显示的迷宫不一样
推测程序加了反调试。最直接的绕过方法就是用xdbg调试,用插件ScyllaHide直接绕过反调试检测。在转储找到相同的地址可以得到真正的迷宫

选中迷宫地图数据右键二进制保存到文件,把文件格式改为txt

然后就可以让AI写个解迷宫脚本解出路径,按照程序的提示把路径进行MD5加密就可以得到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
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
64from collections import deque
def solve_maze(maze, start, end, wall='#', free=' '):
"""Maze solver using BFS"""
directions = {
(-1, 0): 'w', # up
(1, 0): 's', # down
(0, -1): 'a', # left
(0, 1): 'd' # right
}
rows, cols = len(maze), len(maze[0])
visited = [[False] * cols for _ in range(rows)]
# BFS
queue = deque([(start[0], start[1], "")])
visited[start[0]][start[1]] = True
while queue:
row, col, path = queue.popleft()
if (row, col) == end:
return path
for dr, dc in directions.keys():
new_row, new_col = row + dr, col + dc
if (0 <= new_row < rows and 0 <= new_col < cols and
not visited[new_row][new_col] and
maze[new_row][new_col] != wall):
visited[new_row][new_col] = True
new_path = path + directions[(dr, dc)]
queue.append((new_row, new_col, new_path))
return None
# Load maze and solve
with open("final_maze.txt", "r") as f:
maze = [list(line.strip()) for line in f]
start = (1, 1)
end = (99, 99)
print(f"Maze size: {len(maze)} x {len(maze[0])}")
print(f"Start: {start}, End: {end}")
# Solve the maze
solution = solve_maze(maze, start, end, wall='#', free=' ')
if solution:
print(f"Solution found!")
print(f"Path length: {len(solution)}")
# Print FULL path
print(f"\nFULL PATH:")
print(solution)
# Save full path
with open("maze_solution.txt", "w") as f:
f.write(solution)
print(f"\nFull path saved to maze_solution.txt")
else:
print("No solution found!")
flag:0xGame{44fc4bc18c87b3992af1d7726185ed36}
绯想天则
考点:unity逆向改游戏机制
附件是个unity游戏

boss血条很厚,怎么打都打不过,角色很容易死。所以考虑改游戏机制
参考从出题到做题——浅探Unity游戏逆向,找到IWANNATENSHIFUMOのINESBATTLE\I WANNA TENSHI FUMOのFIGHT WITH INES_Data\Managed\Assembly-CSharp.dll,用dnspy打开进行修改。修改思路是给角色加上锁血和一击毙命buff,修改的类是CharacterStats,至于怎么修改可以把整个类的代码发给AI问怎么改锁血和一击毙命,因为附件程序工程比较大,没有上下文的话不好描述,此处省略修改方案了。修改方法是在类代码页右键选择编辑类

修改完点击右下角的编译即可。此外,笔者在编译的时候遇到了一些报错:random’is anbiguous reference between ‘unityengine.random’and ‘system.random,此时把代码中所有的Random.Range都改成UnityEngine.Random.Range即可。
修改完成后,重新运行游戏就可以速通得到flag了

flag:0xGame{TenshiSamaSaiko_IWANNATENSHIFUMO_INESBATTLE}
幻视调律
考点:函数重写,RC4,TEA
附件程序运行起来是很常规的输入flag提示:

用IDA打开,Shift+F12可以看到可疑字符串

跟踪可疑字符串可以找到main函数

简单优化一下

可以看到函数对输入的内容进行了RC4加密后才校验。但是作为第四周的题目,这样子显然还是太直白了。动调一下可以发现,strcmp函数被重写了



可以看到,strcmp最后被导向了sub_140003000,函数反编译如下(经过优化),是个TEA加密
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_BOOL8 __fastcall sub_140003000(void *a1, _DWORD *a2)
{
_DWORD Buf2[14]; // [rsp+20h] [rbp-60h] BYREF
_DWORD *key; // [rsp+58h] [rbp-28h]
void *Buf1; // [rsp+60h] [rbp-20h]
int j; // [rsp+6Ch] [rbp-14h]
int sum; // [rsp+70h] [rbp-10h]
unsigned int p2; // [rsp+74h] [rbp-Ch]
unsigned int p1; // [rsp+78h] [rbp-8h]
int i; // [rsp+7Ch] [rbp-4h]
Buf1 = a1;
key = a2;
Buf2[0] = 1111022553;
Buf2[1] = 987309029;
Buf2[2] = -1041056711;
Buf2[3] = 979189025;
Buf2[4] = 189565873;
Buf2[5] = 173973774;
Buf2[6] = 1090109393;
Buf2[7] = -591836169;
Buf2[8] = -394857933;
Buf2[9] = 668605093;
Buf2[10] = 0xB952BACB;
for ( i = 0; i <= 9; ++i )
{
sum = 0;
p1 = *((_DWORD *)Buf1 + i);
p2 = *((_DWORD *)Buf1 + i + 1);
sum = 0x9E3779B9;
for ( j = 0; j <= 31; ++j )
{
p1 += (sum + p2) ^ (*key + 16 * p2) ^ ((p2 >> 5) + key[1]);
sum -= 0x61C88647;
p2 += (sum + p1) ^ (key[2] + 16 * p1) ^ ((p1 >> 5) + key[3]);
}
*((_DWORD *)Buf1 + i) = p1;
*((_DWORD *)Buf1 + i + 1) = p2;
}
return memcmp(Buf1, Buf2, 0x2CuLL) != 0;
}所以,完整的加密流程是先RC4再TEA,TEA的key是main函数中strcmp的参数str2,密文是TEA最后面出现的Buf2。
还有一些需要关注的点:程序事实上进行了两次RC4加密,第一次是为了输出”input your flag:”,第二次才是加密输入的内容。所以,动调时在函数进行输出后、第二次RC4加密前才能提取出真的S盒
在反编译出来的函数的基础上可以写出解密脚本:
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#include<stdio.h>
#include<stdint.h>
void RC4_decrypt(char *a1, int a2) {
//直接提取S盒
unsigned char byte_140008060[256] = {
0x99, 0x9B, 0x45, 0x40, 0x22, 0xF6, 0x8B, 0x79, 0x85, 0xD8, 0x7E, 0xF2, 0xBD, 0x98, 0x4C, 0x87,
0x2C, 0xB5, 0x1F, 0x25, 0xA5, 0xD7, 0xAA, 0x56, 0x3C, 0x42, 0x9A, 0x1D, 0x27, 0xC7, 0x0D, 0x4E,
0x3E, 0x71, 0x63, 0x8A, 0x82, 0x46, 0xF3, 0x53, 0x70, 0xE2, 0x11, 0x16, 0x2A, 0xD3, 0xA0, 0x2D,
0x5F, 0x60, 0x8D, 0xD9, 0xFA, 0xD0, 0xC5, 0x64, 0x00, 0xEE, 0xDF, 0xB3, 0x4A, 0x06, 0x8E, 0xB4,
0x8C, 0x86, 0xD2, 0x38, 0x58, 0x01, 0x48, 0x61, 0x32, 0xD6, 0x90, 0xD1, 0x5B, 0xAD, 0x7F, 0x77,
0x4B, 0x09, 0x12, 0xCE, 0x75, 0xC8, 0x05, 0x96, 0xDE, 0xF9, 0xA2, 0xE0, 0x66, 0x0B, 0xAB, 0xE1,
0xCD, 0x4D, 0xF0, 0x92, 0x9D, 0x83, 0x7B, 0xBF, 0x6E, 0xF7, 0xF8, 0x41, 0x6C, 0x30, 0x37, 0xCA,
0x89, 0xA8, 0xD5, 0xEF, 0x23, 0x68, 0xBB, 0xE3, 0xB2, 0x9F, 0xC6, 0xB6, 0xA3, 0xFD, 0xE8, 0xEB,
0x3A, 0x8F, 0x10, 0xA6, 0x1B, 0x67, 0xE5, 0xC0, 0xF5, 0x81, 0x97, 0x39, 0x57, 0x6A, 0x2E, 0xE9,
0x3F, 0x80, 0x2F, 0xA4, 0xE4, 0xEC, 0xE7, 0x24, 0x9C, 0x43, 0xDB, 0x52, 0xED, 0x08, 0x21, 0x51,
0xFB, 0xD4, 0x69, 0xC9, 0xFE, 0x17, 0x78, 0x14, 0x54, 0xDD, 0xCF, 0x74, 0xFC, 0x59, 0xC2, 0x7D,
0x7C, 0x84, 0x1E, 0x15, 0x72, 0x5A, 0x31, 0x0F, 0xDA, 0xB0, 0xCB, 0xBE, 0xAE, 0x73, 0x3D, 0x7A,
0xC3, 0x62, 0x29, 0xC4, 0x20, 0x6D, 0x36, 0x34, 0xDC, 0xAC, 0x33, 0xA7, 0xFF, 0x55, 0x93, 0xB1,
0xE6, 0xB8, 0xC1, 0xF1, 0xF4, 0x18, 0x6B, 0x91, 0xBA, 0x47, 0x65, 0x0C, 0x13, 0x1C, 0xB7, 0x3B,
0x35, 0xA1, 0x19, 0x6F, 0x0E, 0x49, 0x0A, 0x07, 0x5C, 0x02, 0x94, 0x1A, 0x4F, 0xAF, 0x04, 0x2B,
0xCC, 0xB9, 0x88, 0xA9, 0x5E, 0x26, 0xEA, 0x50, 0x44, 0x95, 0x28, 0x5D, 0x03, 0xBC, 0x76, 0x9E
};
unsigned __int8 v3; // [rsp+0h] [rbp-10h]
int i; // [rsp+4h] [rbp-Ch]
int v5; // [rsp+8h] [rbp-8h]
int v6; // [rsp+Ch] [rbp-4h]
v6 = 0;
v5 = 0;
for ( i = 0; i < a2; ++i )
{
v6 = (v6 + 1) % 256;
v5 = (byte_140008060[v6] + v5) % 256;
v3 = byte_140008060[v6];
byte_140008060[v6] = byte_140008060[v5];
byte_140008060[v5] = v3;
*(char *)(i + a1) = byte_140008060[(unsigned __int8)(byte_140008060[v6] + byte_140008060[v5])] ^ *(char *)(a1 + i);
}
}
int main(){
//用作key的str2
unsigned char Str2[44] = {
0x9F, 0xBE, 0xAF, 0x3F, 0x87, 0x12, 0xF0, 0x50, 0x75, 0xC0, 0xA4, 0xF6, 0x78, 0x42, 0x77, 0x55,
0x80, 0x7B, 0x02, 0x67, 0x5E, 0xFE, 0x07, 0xEC, 0x76, 0x77, 0xC8, 0x31, 0x66, 0x95, 0x0A, 0x25,
0x7B, 0xE2, 0x83, 0x02, 0x9C, 0x7B, 0x9C, 0xE9, 0x93, 0x2E, 0x66, 0xC7
};
//下面是在反编译TEA的基础上改的,直接解密Buf2
uint32_t Buf2[14]; // [rsp+20h] [rbp-60h] BYREF
uint32_t *key; // [rsp+58h] [rbp-28h]
//void *Buf1; // [rsp+60h] [rbp-20h]
int j; // [rsp+6Ch] [rbp-14h]
int sum; // [rsp+70h] [rbp-10h]
unsigned int p2; // [rsp+74h] [rbp-Ch]
unsigned int p1; // [rsp+78h] [rbp-8h]
int i; // [rsp+7Ch] [rbp-4h]
//Buf1 = a1;
key = (uint32_t *)Str2;
Buf2[0] = 1111022553;
Buf2[1] = 987309029;
Buf2[2] = -1041056711;
Buf2[3] = 979189025;
Buf2[4] = 189565873;
Buf2[5] = 173973774;
Buf2[6] = 1090109393;
Buf2[7] = -591836169;
Buf2[8] = -394857933;
Buf2[9] = 668605093;
Buf2[10] = 0xB952BACB;
//这里原来是从<Buf[0],Buf[1]>到<Buf[9],Buf[10]>一对一对加密,现在反过来从<Buf[9],Buf[10]>开始到<Buf[0],Buf[1]>
for ( i = 9; i >= 0; --i )
{
sum = 0;
p1 = *((uint32_t*)Buf2 + i);
p2 = *((uint32_t*)Buf2 + i + 1);
sum = 0x9E3779B9;
//模拟正向加密得到最后的sum值
for(int i = 0; i < 32; i++){
sum-=0x61C88647;
}
//p1,p2的处理顺序调换,加减调换,sum加减调换
for ( j = 0; j <= 31; ++j )
{
p2 -= (sum + p1) ^ (key[2] + 16 * p1) ^ ((p1 >> 5) + key[3]);
sum += 0x61C88647;
p1 -= (sum + p2) ^ (*key + 16 * p2) ^ ((p2 >> 5) + key[1]);
}
*((uint32_t*)Buf2 + i) = p1;
*((uint32_t*)Buf2 + i + 1) = p2;
}
RC4_decrypt((char *)Buf2,44);
for(i=0;i<44;i++)
printf("%c",((char *)Buf2)[i]);
return 0;
}
flag:0xGame{20994d4d-30ab-4918-b00a-5ca6ae138613}
花鸟风月
考点:花指令,魔改base64,异或,动态调试
附件程序运行起来是这样的:

用IDA打开,Shift+F12在字符串窗口可以找到类似base64编码表的可疑字符串

随后可以跟踪到这样一个貌似base64编码的函数

选中函数名按X回溯上层函数,会发现无法反编译了,栈帧报错

在报错的附近可以看到反汇编失败的一堆数字,以及一个典型的jnz+jz花指令
选中上图中的loc_4012F2按U可以得到下图

此时只需要把jnz和jz以及后面的0E8h给nop掉即可,再选中unk_4012F3按C即可得到修正后的汇编代码

同样的花指令大概有3个,去除后可以在上文找到另一个报错

这里可以发现报错处的上一句是retn,所以把下面的loc_4012C0右键create function

然后再回过头看这个报错,前面不远有一个很可疑的call $+5,显然是人为修改的

仔细分析这段汇编代码会发现,这也是一个花指令

实际上程序在jz处不跳直接retn后,会回到xor eax,1这一行,原因是:call $+5即call 0x0040122D,又即push rip(0x0040122D) jmp 0x0040122D,所以retn后程序回到栈上的rip(0x0040122D)处,即xor eax,1。IDA反编译会被这个retn给欺骗,误以为函数到此结束,其实后面还有一些内容。所以直接吧loc_401223这一整段nop掉,就可以得到完整的反编译代码

优化一下这个函数和上文的类似base编码的函数的命名,可以得到。
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
60int sub_4012C0()
{
char *v0; // eax
char *v1; // eax
char *v2; // eax
char *v3; // eax
char *v5; // eax
char *v6; // eax
char *v7; // eax
char *v8; // eax
int v9; // [esp-4h] [ebp-ECh]
int v10; // [esp+10h] [ebp-D8h] BYREF
int v11; // [esp+14h] [ebp-D4h]
int i; // [esp+18h] [ebp-D0h]
_BYTE v13[100]; // [esp+1Ch] [ebp-CCh] BYREF
_BYTE v14[100]; // [esp+80h] [ebp-68h] BYREF
v11 = 0x81428113;
v0 = sub_401000((int)&unk_422054);
sub_401670(v0);
v1 = sub_401000((int)&unk_422068);
sub_4016B0(v1, (char)&v10);
for ( i = 0; i < 10; ++i )
{
v11 = 21106755 * v11 + 1126795285;
v11 ^= __ROL4__(
v10,
i * (byte_422007 + byte_422004)
% (byte_422008
+ byte_422004
+ byte_422005 * byte_422008
+ byte_422AA0 * (byte_422002 + byte_422006 + byte_422003)
- byte_422005 * byte_422005));
}
if ( v11 == 0xA745DA5D )
{
v2 = sub_401000((int)&unk_42206C);
sub_401670(v2);
v5 = sub_401000((int)&unk_422094);
sub_401670(v5);
v6 = sub_401000((int)&unk_4220A4);
sub_4016B0(v6, (char)v14);
XORencrypt(v14, v10, byte_422004 + byte_422002 + byte_422008 * byte_422003);
baseEncode((int)v14, (int)v13, byte_422005 + byte_422001 + byte_422008 * byte_422003);
v9 = byte_422004 * byte_422003 * byte_422002;
v7 = sub_401000((int)&unk_4220A8);
if ( sub_40B460(v13, v7, v9) )
v8 = sub_401000((int)&unk_4220FC);
else
v8 = sub_401000((int)&unk_4220E8);
sub_401670(v8);
return 0;
}
else
{
v3 = sub_401000((int)&unk_422080);
sub_401670(v3);
return 0;
}
}其中byte_42200x的值都是x+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
53int sub_4012C0()
{
char *v0; // eax
char *v1; // eax
char *v2; // eax
char *v3; // eax
char *v5; // eax
char *v6; // eax
char *v7; // eax
char *v8; // eax
int v9; // [esp-4h] [ebp-ECh]
int v10; // [esp+10h] [ebp-D8h] BYREF
int v11; // [esp+14h] [ebp-D4h]
int i; // [esp+18h] [ebp-D0h]
_BYTE v13[100]; // [esp+1Ch] [ebp-CCh] BYREF
_BYTE v14[100]; // [esp+80h] [ebp-68h] BYREF
v11 = 0x81428113;
v0 = sub_401000((int)&unk_422054);
sub_401670(v0);
v1 = sub_401000((int)&unk_422068);
sub_4016B0(v1, (char)&v10);
for ( i = 0; i < 10; ++i )
{
v11 = 0x1421043 * v11 + 0x43298815;
v11 ^= __ROL4__(v10, i * (8 + 5) % (9 + 5 + 6 * 9 + 0 * (3 + 7 + 4) - 6 * 6));
}
if ( v11 == 0xA745DA5D )
{
v2 = sub_401000((int)&unk_42206C);
sub_401670(v2);
v5 = sub_401000((int)&unk_422094);
sub_401670(v5);
v6 = sub_401000((int)&unk_4220A4);
sub_4016B0(v6, (char)v14);
XORencrypt(v14, v10, 5 + 3 + 9 * 4);
baseEncode((int)v14, (int)v13, 6 + 2 + 9 * 4);
v9 = 5 * 4 * 3;
v7 = sub_401000((int)&unk_4220A8);
if ( sub_40B460(v13, v7, v9) )
v8 = sub_401000((int)&unk_4220FC);
else
v8 = sub_401000((int)&unk_4220E8);
sub_401670(v8);
return 0;
}
else
{
v3 = sub_401000((int)&unk_422080);
sub_401670(v3);
return 0;
}
}动调可以发现程序的提示词是实时解密的,sub_401000是解密函数,sub_401670是输出函数,sub_4016B0是输入函数。下面是具体过程:
在函数的开头下断点,开始调试。程序停在了下图这个地方,可以看到随后有一些奇怪的数据,直接nop掉


继续运行程序会发现执行过sub_7C1670(对应静态分析时的sub_401670,动态调试换基址了所以地址前两位有些不同)后程序会有提示输出,因此推断sub_7C1670是print

而观察静态分析时的代码可以发现,sub_401000几乎和sub_401670是同时出现的,动态调试对应的sub_7C1000函数反编译如下

同时可以发现byte_7E3440此时存储的正好是Input key (Hex) (此时已经步过这个函数了,所以byte_7E3440已经被处理过了):

所以可以推测提示词是实时解密的,sub_401000是解密函数
接下来还有个sub_7C16B0,推测是scanf

在动态调试随后的代码里,会发现还有花指令(有两处,不知道是不是静态分析时的去混淆没有保存)

同样的操作nop掉后,反编译得到
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
51void sub_7C12C0()
{
char *v0; // eax
char *v1; // eax
char *v2; // eax
char *v3; // eax
char *v4; // eax
char *v5; // eax
char *v6; // eax
char *v7; // eax
int v8; // [esp-4h] [ebp-ECh]
int v9; // [esp+10h] [ebp-D8h] BYREF
int v10; // [esp+14h] [ebp-D4h]
int i; // [esp+18h] [ebp-D0h]
_BYTE v12[100]; // [esp+1Ch] [ebp-CCh] BYREF
_BYTE v13[100]; // [esp+80h] [ebp-68h] BYREF
v10 = 0x81428113;
v0 = decodeNotice((int)&unk_7E2054);
print(v0);
v1 = decodeNotice((int)&unk_7E2068);
scanf(v1, (char)&v9);
for ( i = 0; i < 10; ++i )
{
v10 = 0x1421043 * v10 + 0x43298815;
v10 ^= __ROL4__(v9, i * (8 + 5) % (9 + 5 + 6 * 9 + 0 * (3 + 7 + 4) - 6 * 6));
}
if ( v10 == 0xA745DA5D )
{
v2 = decodeNotice((int)&unk_7E206C);
print(v2);
v4 = decodeNotice((int)&unk_7E2094);
print(v4);
v5 = decodeNotice((int)&unk_7E20A4);
scanf(v5, (char)v13);
XORencrypt((int)v13, v9, 5 + 3 + 9 * 4);
baseEncode((int)v13, (int)v12, 6 + 2 + 9 * 4);
v8 = 5 * 4 * 3;
v6 = decodeNotice((int)&unk_7E20A8);
if ( sub_7CB460(v12, v6, v8) )
v7 = decodeNotice((int)&unk_7E20FC);
else
v7 = decodeNotice((int)&unk_7E20E8);
print(v7);
}
else
{
v3 = decodeNotice((int)&unk_7E2080);
print(v3);
}
}此时得到更加清晰的代码了,同时可以发现程序首先对输入的key进行了校验。
为了继续了解程序逻辑,这里选择修改if语句条件的判断结果来绕过key检验,只需要在cmp后修改ZF标志位,让jnz不成立即可


继续运行程序会有输入flag的提示

至此,可以猜出程序的大致逻辑:接收一个key并进行检验,检验成功则继续检验flag,同时需要注意key会作为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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51void sub_7C12C0()
{
char *v0; // eax
char *v1; // eax
char *v2; // eax
char *v3; // eax
char *v4; // eax
char *v5; // eax
char *targetResult; // eax
char *v7; // eax
int v8; // [esp-4h] [ebp-ECh]
int key; // [esp+10h] [ebp-D8h] BYREF
int v10; // [esp+14h] [ebp-D4h]
int i; // [esp+18h] [ebp-D0h]
_BYTE encodedInput[100]; // [esp+1Ch] [ebp-CCh] BYREF
_BYTE input[100]; // [esp+80h] [ebp-68h] BYREF
v10 = 0x81428113;
v0 = decodeNotice((int)&unk_7E2054); // input key
print(v0);
v1 = decodeNotice((int)&unk_7E2068);
scanf(v1, (char)&key);
for ( i = 0; i < 10; ++i )
{
v10 = 0x1421043 * v10 + 0x43298815;
v10 ^= __ROL4__(key, i * (8 + 5) % (9 + 5 + 6 * 9 + 0 * (3 + 7 + 4) - 6 * 6));
}
if ( v10 == 0xA745DA5D )
{
v2 = decodeNotice((int)&unk_7E206C); // key is correct
print(v2);
v4 = decodeNotice((int)&unk_7E2094); // input flag
print(v4);
v5 = decodeNotice((int)&unk_7E20A4);
scanf(v5, (char)input);
XORencrypt((int)input, key, 5 + 3 + 9 * 4);
baseEncode((int)input, (int)encodedInput, 6 + 2 + 9 * 4);
v8 = 5 * 4 * 3;
targetResult = decodeNotice((int)&unk_7E20A8);// 此处解密的值被用来和输入值的加密结果进行比较了,所以推测是真正的flag密文
if ( strcmp(encodedInput, targetResult, v8) )
v7 = decodeNotice((int)&unk_7E20FC);
else
v7 = decodeNotice((int)&unk_7E20E8);
print(v7);
}
else
{
v3 = decodeNotice((int)&unk_7E2080);
print(v3);
}
}targetResult也是实时解密的结果,所以可以动调提取
程序的加密流程是先XORencrypt再baseEncode,所以解密要先baseDecode再XORenc。baseEncode优化后如下,可以发现不是标准的base64编码
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
35int __cdecl baseEncode(int input, int output, int length)
{
int result; // eax
char code2; // [esp+Ah] [ebp-56h]
int i; // [esp+Ch] [ebp-54h]
char code4; // [esp+12h] [ebp-4Eh]
char code3; // [esp+13h] [ebp-4Dh]
int v8; // [esp+14h] [ebp-4Ch]
int v9; // [esp+14h] [ebp-4Ch]
char baseTable[68]; // [esp+18h] [ebp-48h] BYREF
v8 = 0;
strcpy(baseTable, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
for ( i = 0; i < length; i += 3 )
{
code3 = baseTable[((*(i + input + 1) >> 4) | (16 * *(i + input))) & 63];// baseTable[((input[i+1]>>4)|(input[i]<<4))&63]
if ( i + 1 >= length )
code3 = '=';
else
code2 = baseTable[((*(i + input + 2) >> 6) | (4 * *(i + input + 1))) & 63];// baseTable[((input[i+2]>>6)|(input[i+1]<<2))&63]
if ( i + 2 >= length )
code4 = '=';
else
code4 = baseTable[*(i + input + 2) & 63]; // baseTable[input[i+2]&63]
*(v8 + output) = baseTable[(*(i + input) >> 2) & 63];// baseTable[(input[i]>>2)&63]
v9 = v8 + 1;
*(v9 + output) = code2;
*(++v9 + output) = code3;
*(++v9 + output) = code4;
v8 = v9 + 1;
result = i + 3;
}
*(v8 + output) = 0;
return result;
}标准base64编码是按照字符顺序,把3个8bit的char转成4个6bit,即
1
2
3
4code1=baseTable[input[i]>>2];
code2=baseTable[(input[i]<<6)|(input[i+1]>>4)];
code3=baseTable[(input[i+1]<<4)|(input[i+2]>>6)];
code4=baseTable[input[i+2]<<2];而程序里code2和code3顺序反了
1
2
3
4
5code1=baseTable[(input[i]>>2)&63];
code2=baseTable[((input[i+1]<<2)|(input[i+2]>>6))&63];
code3=baseTable[((input[i]<<4)|(input[i+1]>>4))&63];
code4=baseTable[input[i+2]&63];
//PS:这里因为&了63,所以拼成8位的code被截掉了高2位,剩下6位也就是说,程序的魔改base64编码和标准base64编码的不同点在于,编码时置换了第2、3个字符。因此我们此处的解密就是把每4个字符的第2、3个字符互换位置,然后使用标准base64解码即可。下面是basedecode的脚本
1
2
3
4
5
6
7
8
9
10
11
12import base64
enc=[0x4D, 0x41, 0x50, 0x44, 0x63, 0x33, 0x47, 0x74, 0x50, 0x51, 0x33, 0x34, 0x76, 0x4D, 0x33, 0x70, 0x4E, 0x42, 0x62, 0x31, 0x50, 0x57, 0x47, 0x78, 0x63, 0x63, 0x48, 0x74, 0x76, 0x78, 0x48, 0x31, 0x4E, 0x56, 0x61, 0x39, 0x4A, 0x50, 0x54, 0x74, 0x61, 0x63, 0x53, 0x35, 0x37, 0x49, 0x69, 0x6C, 0x4E, 0x35, 0x75, 0x78, 0x4A, 0x53, 0x57, 0x35, 0x49, 0x77, 0x6D, 0x3D]
basedecoded=""
for i in range(0, len(enc),4):
basedecoded+=chr(enc[i])
basedecoded+=chr(enc[i+2])
basedecoded+=chr(enc[i+1])
basedecoded+=chr(enc[i+3])
print(basedecoded)
#MPADcG3tP3Q4v3MpNbB1PGWxcHctvHx1NaV9JTPtaSc57iIlNu5xJWS5Imw=
xorencoded=base64.b64decode(basedecoded)接下来是XORdecrypt。首先需要得到密钥,既然程序给了密钥的检验算法,可以复现这个算法来爆破密钥。下面是爆破脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include<stdio.h>
#define __ROL4__(x, y) ((((x) << (y)) | ((x) >> (32 - (y))))&0xFFFFFFFF)
int main(){
unsigned int v9,key;
for(key=0;key<=0xFFFFFFFF;key++){
v9 = 0x81428113;
for ( int i = 0; i < 10; ++i )
{
v9 = 0x1421043 * v9 + 0x43298815;
v9 ^= __ROL4__(key, i * (8 + 5) % (9 + 5 + 6 * 9 + 0 * (3 + 7 + 4) - 6 * 6));
}
if ( v9 == 0xA745DA5D )
printf("key: 0x%08x\n",key);
}
return 0;
}
//key: 0x98bf3b77然后是XOR解密,由于异或过程中不涉及密文前后位,只是单纯使用key,所以只需要复现异或过程就行了。但是照抄也不是很对,XORencrypt使用key的时候是进行了取位操作,把key分成了四个部分的,所以这部分要单独拿出来处理。脚本:
1
2
3
4
5
6
7
8
9
10def XORdecrypt(data, key):
for i in range(44):
key_byte=(key >> (8 * (i % 4)))&0xff#单独处理每次使用的key,每次只取四分之一,直接抄原代码没有&0xff的话会解密失败。
data[i]^=key_byte
data[i]=data[i]&0xff
for j in range(44):
key_byte=(key >> (8 * (j % 4)))&0xff
data[j]^=((key_byte >> 4) | (16 * key_byte))
data[j]=data[j]&0xff
return bytes(data)完整脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import base64
def XORdecrypt(data, key):
for i in range(44):
key_byte=(key >> (8 * (i % 4)))&0xff
data[i]^=key_byte
data[i]=data[i]&0xff
for j in range(44):
key_byte=(key >> (8 * (j % 4)))&0xff
data[j]^=((key_byte >> 4) | (16 * key_byte))
data[j]=data[j]&0xff
return bytes(data)
enc=[0x4D, 0x41, 0x50, 0x44, 0x63, 0x33, 0x47, 0x74, 0x50, 0x51, 0x33, 0x34, 0x76, 0x4D, 0x33, 0x70, 0x4E, 0x42, 0x62, 0x31, 0x50, 0x57, 0x47, 0x78, 0x63, 0x63, 0x48, 0x74, 0x76, 0x78, 0x48, 0x31, 0x4E, 0x56, 0x61, 0x39, 0x4A, 0x50, 0x54, 0x74, 0x61, 0x63, 0x53, 0x35, 0x37, 0x49, 0x69, 0x6C, 0x4E, 0x35, 0x75, 0x78, 0x4A, 0x53, 0x57, 0x35, 0x49, 0x77, 0x6D, 0x3D]
basedecoded=""
for i in range(0, len(enc),4):
basedecoded+=chr(enc[i])
basedecoded+=chr(enc[i+2])
basedecoded+=chr(enc[i+1])
basedecoded+=chr(enc[i+3])
#print(basedecoded)
xorencoded=base64.b64decode(basedecoded)
#print(len(xorencoded))
flag=XORdecrypt(list(xorencoded),0x98bf3b77)
print(flag)
flag:0xGame{e8778581-e94f-48d5-943e-69ff46f54d1f}
云消雾散
考点:复杂程序分析,魔改RC4
难点在于复杂,没有其他套路
附件给了一个img.exe和enc.bmp。bmp是一种图片格式,推测img.exe是bmp图片加密程序,enc.bmp是加密的图片,要求逆向加密程序进而解密图片
img.exe用IDA打开,shift+F12可以找到”enc.bmp”

跟踪可以到这样一个函数
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__int64 __fastcall sub_140001936(int a1, int a2)
{
int v2; // r9d
int v3; // edx
int v4; // r8d
int v5; // r9d
int v6; // r9d
int v7; // edx
int v8; // r8d
int v9; // r9d
int v10; // r9d
int v11; // edx
int v12; // r8d
int v13; // r9d
int v14; // r9d
int v15; // edx
int v16; // r8d
int v17; // r9d
int v18; // r9d
int v19; // r9d
int v20; // r8d
int v21; // r9d
int v22; // edx
int v23; // r8d
int v24; // r9d
int v25; // edx
int v26; // r8d
int v27; // r9d
int v28; // r9d
int v29; // r9d
int v30; // r8d
int v31; // r9d
int v32; // edx
int v33; // r8d
int v34; // r9d
int v35; // edx
int v36; // r8d
int v37; // r9d
int v38; // edx
int v39; // r8d
int v40; // r9d
int v41; // edx
int v42; // r8d
int v43; // r9d
int v44; // edx
int v45; // r8d
int v46; // r9d
int v47; // edx
int v48; // r8d
int v49; // r9d
__int64 v51; // [rsp+0h] [rbp-80h]
__int64 v52; // [rsp+0h] [rbp-80h]
__int64 v53; // [rsp+0h] [rbp-80h]
__int64 v54; // [rsp+0h] [rbp-80h]
__int64 v55; // [rsp+0h] [rbp-80h]
__int64 v56; // [rsp+0h] [rbp-80h]
__int64 v57; // [rsp+0h] [rbp-80h]
__int64 v58; // [rsp+0h] [rbp-80h]
__int64 v59; // [rsp+0h] [rbp-80h]
__int64 v60; // [rsp+0h] [rbp-80h]
__int64 v61; // [rsp+0h] [rbp-80h]
__int64 v62; // [rsp+0h] [rbp-80h]
__int64 v63; // [rsp+0h] [rbp-80h]
__int64 v64; // [rsp+0h] [rbp-80h]
__int64 v65; // [rsp+0h] [rbp-80h]
__int64 v66; // [rsp+8h] [rbp-78h]
__int64 v67; // [rsp+10h] [rbp-70h]
_BYTE v68[32]; // [rsp+20h] [rbp-60h] BYREF
_BYTE v69[32]; // [rsp+40h] [rbp-40h] BYREF
_BYTE v70[32]; // [rsp+60h] [rbp-20h] BYREF
_BYTE v71[44]; // [rsp+80h] [rbp+0h] BYREF
char v72; // [rsp+ACh] [rbp+2Ch] BYREF
char v73; // [rsp+ADh] [rbp+2Dh] BYREF
char v74; // [rsp+AEh] [rbp+2Eh] BYREF
char v75; // [rsp+AFh] [rbp+2Fh] BYREF
_BYTE v76[32]; // [rsp+B0h] [rbp+30h] BYREF
_BYTE v77[32]; // [rsp+D0h] [rbp+50h] BYREF
_BYTE v78[32]; // [rsp+F0h] [rbp+70h] BYREF
_BYTE v79[32]; // [rsp+110h] [rbp+90h] BYREF
char *v80; // [rsp+130h] [rbp+B0h]
char *v81; // [rsp+138h] [rbp+B8h]
char *v82; // [rsp+140h] [rbp+C0h]
char *v83; // [rsp+148h] [rbp+C8h]
sub_140001D70();
v83 = &v72;
sub_140004130(a1, a2, "E:\\WorkSpace\\Lab\\0xGame 2025", v71, &v72, v2);
sub_140003580(a1, a2, v3, &v72, v4, v5);
v82 = &v73;
sub_140004130(a1, a2, "flag.bmp", v70, &v73, v6);
sub_140003580(a1, a2, v7, &v73, v8, v9);
v81 = &v74;
sub_140004130(a1, a2, "enc.bmp", v69, &v74, v10);
sub_140003580(a1, a2, v11, &v74, v12, v13);
v80 = &v75;
sub_140004130(a1, a2, "114514puiqneravcixpbhqn;afnv-92piwgspdifjv", v68, &v75, v14);
sub_140003580(a1, a2, v15, &v75, v16, v17);
sub_140004670(a1, a2, v71, v77, "\\", v18, v51);
sub_140004620(a1, a2, v77, v76, v70, v19, v52);
sub_140004210(a1, a2, v76, v70, v20, v21, v53);
sub_1400041E0(a1, a2, v22, v76, v23, v24, v54);
sub_1400041E0(a1, a2, v25, v77, v26, v27, v55);
sub_140004670(a1, a2, v71, v79, "\\", v28, v56);
sub_140004620(a1, a2, v79, v78, v69, v29, v57);
sub_140004210(a1, a2, v78, v69, v30, v31, v58);
sub_1400041E0(a1, a2, v32, v78, v33, v34, v59);
sub_1400041E0(a1, a2, v35, v79, v36, v37, v60);
sub_14000164D(a1, a2, v69, v70, v68, 1, v61, v66, v67);
sub_1400041E0(a1, a2, v38, v68, v39, v40, v62);
sub_1400041E0(a1, a2, v41, v69, v42, v43, v63);
sub_1400041E0(a1, a2, v44, v70, v45, v46, v64);
sub_1400041E0(a1, a2, v47, v71, v48, v49, v65);
return 0LL;
}借助AI可以优化得到:
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__int64 __fastcall sub_140001936(char *input, char *output)
{
int v2; // r9d
int v3; // r9d
int v4; // r9d
int v5; // r9d
int v6; // r9d
int v7; // r9d
int v8; // r8d
int v9; // r9d
int v10; // edx
int v11; // r8d
int v12; // r9d
int v13; // edx
int v14; // r8d
int v15; // r9d
int v16; // r9d
int v17; // r9d
int v18; // r8d
int v19; // r9d
int v20; // edx
int v21; // r8d
int v22; // r9d
int v23; // edx
int v24; // r8d
int v25; // r9d
int v26; // edx
int v27; // r8d
int v28; // r9d
int v29; // edx
int v30; // r8d
int v31; // r9d
int v32; // edx
int v33; // r8d
int v34; // r9d
int v35; // edx
int v36; // r8d
int v37; // r9d
_BYTE v39[32]; // [rsp+20h] [rbp-60h] BYREF
_BYTE outputFilename[32]; // [rsp+40h] [rbp-40h] BYREF
_BYTE v41[32]; // [rsp+60h] [rbp-20h] BYREF
_BYTE v42[44]; // [rsp+80h] [rbp+0h] BYREF
char v43; // [rsp+ACh] [rbp+2Ch] BYREF
char v44; // [rsp+ADh] [rbp+2Dh] BYREF
char v45; // [rsp+AEh] [rbp+2Eh] BYREF
char v46; // [rsp+AFh] [rbp+2Fh] BYREF
_BYTE v47[32]; // [rsp+B0h] [rbp+30h] BYREF
_BYTE v48[32]; // [rsp+D0h] [rbp+50h] BYREF
_BYTE v49[32]; // [rsp+F0h] [rbp+70h] BYREF
_BYTE v50[32]; // [rsp+110h] [rbp+90h] BYREF
char *v51; // [rsp+130h] [rbp+B0h]
char *v52; // [rsp+138h] [rbp+B8h]
char *v53; // [rsp+140h] [rbp+C0h]
char *v54; // [rsp+148h] [rbp+C8h]
sub_140001D70();
v54 = &v43;
constructString(input, output, "E:\\WorkSpace\\Lab\\0xGame 2025", v42, &v43, v2);
nothing(); // 什么也没有的一个函数
v53 = &v44;
constructString(input, output, "flag.bmp", v41, &v44, v3);
nothing();
v52 = &v45;
constructString(input, output, "enc.bmp", outputFilename, &v45, v4);
nothing();
v51 = &v46;
constructString(input, output, "114514puiqneravcixpbhqn;afnv-92piwgspdifjv", v39, &v46, v5);
nothing();
makePath1(input, output, v42, v48, "\\", v6);
makePath2(input, output, v48, v47, v41, v7);
makePath3(input, output, v47, v41, v8, v9);
clearup(input, output, v10, v47, v11, v12);
clearup(input, output, v13, v48, v14, v15);
makePath1(input, output, v42, v50, "\\", v16);
makePath2(input, output, v50, v49, outputFilename, v17);
makePath3(input, output, v49, outputFilename, v18, v19);
clearup(input, output, v20, v49, v21, v22);
clearup(input, output, v23, v50, v24, v25);
encrypt(input, output, outputFilename, v41, v39, 1);
clearup(input, output, v26, v39, v27, v28);
clearup(input, output, v29, outputFilename, v30, v31);
clearup(input, output, v32, v41, v33, v34);
clearup(input, output, v35, v42, v36, v37);
return 0LL;
}这里实际上是在初始化文件相关的信息,程序应该是从E:\WorkSpace\Lab\0xGame 2025下获取flag.bmp,加密后输出为enc.bmp。那一串字符串应该是encrypt函数使用的密钥。encpyt函数优化后是这样的:
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__int64 __fastcall encrypt(
std::istream *input,
char *output,
__int64 outputFilename,
__int64 inputfilename,
__int64 a5,
char a6)
{
__int64 v6; // rdx
__int64 v7; // rdx
__int64 v9; // rax
__int64 v10; // rdx
unsigned int v11; // ebx
int v12; // eax
__int64 v13; // rdx
__int64 v14; // rdx
__int64 v15; // rdx
__int64 v17; // [rsp+20h] [rbp-60h] BYREF
int v18; // [rsp+28h] [rbp-58h]
unsigned __int16 v19; // [rsp+2Eh] [rbp-52h]
__int64 v20; // [rsp+52h] [rbp-2Eh] BYREF
_BYTE output_file_buffer[224]; // [rsp+60h] [rbp-20h] BYREF
__int64 v22; // [rsp+140h] [rbp+C0h] BYREF
_BYTE input_file_buffer[232]; // [rsp+250h] [rbp+1D0h] BYREF
__int64 v24; // [rsp+338h] [rbp+2B8h] BYREF
__int64 outputBuffer; // [rsp+448h] [rbp+3C8h]
__int64 v26; // [rsp+450h] [rbp+3D0h]
int v27; // [rsp+45Ch] [rbp+3DCh]
__int64 header_buffer; // [rsp+460h] [rbp+3E0h]
__int64 header_buffer_size; // [rsp+468h] [rbp+3E8h]
std::ifstream::basic_ifstream(input, output, inputfilename, input_file_buffer, 4LL);
std::ofstream::basic_ofstream(input, output, outputFilename, output_file_buffer, 4LL);
if ( std::ios::operator!(input, output, v6, &v24) || std::ios::operator!(input, output, v7, &v22) )
{ // 错误处理
v9 = std::operator<<<std::char_traits<char>>(input, output, &unk_7FF770D06000, &std::cerr);
std::ostream::operator<<(input, output, &std::endl<char,std::char_traits<char>>, v9);
v11 = 0;
}
else
{
header_buffer_size = 1024LL;
header_buffer = operator new[](input);
std::istream::read(input, output, &v20); // 获取bitmap_file_header
std::istream::read(input, output, &v17); // 获取bitmap_info_header
std::istream::read(input, output, header_buffer);
std::ostream::write(input, output, &v20);
std::ostream::write(input, output, &v17);
std::ostream::write(input, output, header_buffer);
if ( header_buffer )
operator delete[](input);
v12 = HIDWORD(v17) * v19 + 31;
if ( v12 < 0 )
v12 = HIDWORD(v17) * v19 + 62;
v27 = 4 * (v12 >> 5);
v26 = (v27 * abs32(v18));
outputBuffer = operator new[](input);
std::istream::read(input, output, outputBuffer);
std::ifstream::close(input, output, v13, input_file_buffer);
if ( a6 )
modifiedRC4(input, output, v26, outputBuffer, a5);
std::ostream::write(input, output, outputBuffer);
std::ofstream::close(input, output, v14, output_file_buffer);
if ( outputBuffer )
operator delete[](input);
v11 = 1;
}
std::ofstream::~ofstream(input, output, v10, output_file_buffer);
std::ifstream::~ifstream(input, output, v15, input_file_buffer);
return v11;
}
//modifiedRC4
__int64 __fastcall modifiedRC4(const char *a1, __int64 a2, int numOfDataBytes, __int64 outputBuffer, __int64 a5)
{
unsigned __int64 v5; // rax
_BYTE *v6; // rax
char v7; // dl
__int64 result; // rax
int i; // [rsp+24h] [rbp-Ch]
int j; // [rsp+24h] [rbp-Ch]
int k; // [rsp+24h] [rbp-Ch]
int j_sbox; // [rsp+28h] [rbp-8h]
int j_; // [rsp+28h] [rbp-8h]
int i_sbox; // [rsp+2Ch] [rbp-4h]
int i_; // [rsp+2Ch] [rbp-4h]
for ( i = 0; i <= 255; ++i )
S[i] = i;
i_sbox = 0;
j_sbox = 0;
for ( j = 0; j <= 255; ++j )
{
i_sbox = (i_sbox + 1) % 256;
a2 = S[i_sbox] + j_sbox;
v5 = sub_7FF770D032B0(a1, a2, j_sbox, a5);
v6 = sub_7FF770D033D0(a1, a2, i_sbox % v5, a5);
v7 = a2 + *v6;
LODWORD(v6) = ((a2 + *v6) >> 31) >> 24;
j_sbox = (v6 + v7) - v6;
swapbytes(a1, a2, &S[j_sbox], &S[i_sbox]);
}
i_ = 0;
j_ = 0;
for ( k = 0; ; ++k )
{
result = ((numOfDataBytes / 256) << 8);
if ( k >= result )
break;
i_ = (i_ + 1) % 256;
j_ = (S[i_] + j_) % 256;
swapbytes(a1, a2, &S[j_], &S[i_]);
swapBytes(
a1,
a2,
(((k / 256) << 8) + j_ + outputBuffer), // outputBuffer[((k/256)<<8)+j_]
(outputBuffer + ((k / 256) << 8) + i_)); // outputBuffer[((k/256)<<8)+i_]
}
return result;
}从IDA的反编译结果里可以得到一些大致的印象:encrypt函数刚开始对文件进行了三次读与写,采用的加密方式是类似RC4的算法,但是相较于标准RC4,此处只是单纯的置换,没有异或操作。为了进一步理解程序,接下来先研究读写文件的操作逻辑。
简单了解一下bmp文件格式,可以知道文件开头前0x36h是文件头,这一部分肯定是不会怎么变的,否则就会改变文件的格式了。在电脑里复刻一个一摸一样的路径,自己创建一个flag.bmp运行img.exe可以得到enc.bmp,此时用十六进制编辑器打开两个文件可以发现它们之间差了1024bytes,更准确的说,是加密后的文件比加密前多了1024bytes。
下面转用xdbg。对照IDA可以找到读写文件的位置,分析结果如下:
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
16500007FF6077B164D | 55 | PUSH RBP | encrypt
00007FF6077B164E | 53 | PUSH RBX |
00007FF6077B164F | 48:81EC 78040000 | SUB RSP,478 |
00007FF6077B1656 | 48:8DAC24 80000000 | LEA RBP,QWORD PTR SS:[RSP+80] | [rsp+80]:_register_onexit_function+210
00007FF6077B165E | 48:898D 10040000 | MOV QWORD PTR SS:[RBP+410],RCX |
00007FF6077B1665 | 48:8995 18040000 | MOV QWORD PTR SS:[RBP+418],RDX |
00007FF6077B166C | 4C:8985 20040000 | MOV QWORD PTR SS:[RBP+420],R8 |
00007FF6077B1673 | 44:89C8 | MOV EAX,R9D |
00007FF6077B1676 | 8885 28040000 | MOV BYTE PTR SS:[RBP+428],AL |
00007FF6077B167C | 48:8B95 10040000 | MOV RDX,QWORD PTR SS:[RBP+410] |
00007FF6077B1683 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B168A | 41:B8 04000000 | MOV R8D,4 |
00007FF6077B1690 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1693 | E8 E0050000 | CALL <JMP.&std::basic_ifstream<char, std::char_traits<ch | 错误处理
00007FF6077B1698 | 48:8B95 18040000 | MOV RDX,QWORD PTR SS:[RBP+418] |
00007FF6077B169F | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B16A3 | 41:B8 04000000 | MOV R8D,4 |
00007FF6077B16A9 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B16AC | E8 AF050000 | CALL <JMP.&std::basic_ofstream<char, std::char_traits<ch |
00007FF6077B16B1 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B16B8 | 48:05 E8000000 | ADD RAX,E8 | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B16BE | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B16C1 | E8 DA050000 | CALL <JMP.&std::basic_ios<char, std::char_traits<char> > |
00007FF6077B16C6 | 84C0 | TEST AL,AL |
00007FF6077B16C8 | 75 16 | JNE img.7FF6077B16E0 |
00007FF6077B16CA | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B16CE | 48:05 E0000000 | ADD RAX,E0 | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B16D4 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B16D7 | E8 C4050000 | CALL <JMP.&std::basic_ios<char, std::char_traits<char> > |
00007FF6077B16DC | 84C0 | TEST AL,AL |
00007FF6077B16DE | 74 07 | JE img.7FF6077B16E7 |
00007FF6077B16E0 | B8 01000000 | MOV EAX,1 |
00007FF6077B16E5 | EB 05 | JMP img.7FF6077B16EC |
00007FF6077B16E7 | B8 00000000 | MOV EAX,0 |
00007FF6077B16EC | 84C0 | TEST AL,AL |
00007FF6077B16EE | 74 32 | JE img.7FF6077B1722 |
00007FF6077B16F0 | 48:8D15 09490000 | LEA RDX,QWORD PTR DS:[7FF6077B6000] | rdx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\enc.bmp", 00007FF6077B6000:u8"无法打开文件!"ame 2025\\enc.bmp"
00007FF6077B16F7 | 48:8B05 024F0000 | MOV RAX,QWORD PTR DS:[<&std::cerr>] | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B16FE | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1701 | E8 22050000 | CALL <JMP.&std::basic_ostream<char, std::char_traits<cha |
00007FF6077B1706 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1709 | 48:8B05 004F0000 | MOV RAX,QWORD PTR DS:[<&JMP.&std::basic_ostream<char, st | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1710 | 48:89C2 | MOV RDX,RAX | rdx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\enc.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1713 | E8 70050000 | CALL <JMP.&std::ostream::operator<<(std::ostream& (*)(st |
00007FF6077B1718 | BB 00000000 | MOV EBX,0 |
00007FF6077B171D | E9 BD010000 | JMP img.7FF6077B18DF |
00007FF6077B1722 | 48:C785 E8030000 00 | MOV QWORD PTR SS:[RBP+3E8],400 |
00007FF6077B172D | B9 00040000 | MOV ECX,400 |
00007FF6077B1732 | E8 D9040000 | CALL <JMP.&operator new[](unsigned long long)> |
00007FF6077B1737 | 48:8985 E0030000 | MOV QWORD PTR SS:[RBP+3E0],RAX |
00007FF6077B173E | 48:8D55 D2 | LEA RDX,QWORD PTR SS:[RBP-2E] | RBP-2E:输入流缓冲区1(存放header)
00007FF6077B1742 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B1749 | 41:B8 0E000000 | MOV R8D,E | 读取长度:Eh
00007FF6077B174F | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1752 | E8 41050000 | CALL <JMP.&std::istream::read(char*, long long)> | 读入header
00007FF6077B1757 | 48:8D55 A0 | LEA RDX,QWORD PTR SS:[RBP-60] | RBP-60:输入流缓冲区2(存放header info)
00007FF6077B175B | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B1762 | 41:B8 28000000 | MOV R8D,28 | 读取长度:28h
00007FF6077B1768 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B176B | E8 28050000 | CALL <JMP.&std::istream::read(char*, long long)> | 读入header info
00007FF6077B1770 | 48:8B95 E0030000 | MOV RDX,QWORD PTR SS:[RBP+3E0] | [rbp-3E0]:输入流缓冲区3(存放文件头后的1024bytes)
00007FF6077B1777 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B177E | 41:B8 00040000 | MOV R8D,400 | 读取长度:400h
00007FF6077B1784 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1787 | E8 0C050000 | CALL <JMP.&std::istream::read(char*, long long)> | 读入文件头后的1024bytes
00007FF6077B178C | 48:8D55 D2 | LEA RDX,QWORD PTR SS:[RBP-2E] | [rbp-2E]:write参数(header)
00007FF6077B1790 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B1794 | 41:B8 0E000000 | MOV R8D,E |
00007FF6077B179A | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B179D | E8 EE040000 | CALL <JMP.&std::ostream::write(char const*, long long)> |
00007FF6077B17A2 | 48:8D55 A0 | LEA RDX,QWORD PTR SS:[RBP-60] | [rbp-60]:"114514puiqneravcixpbhqn;afnv-92piwgspdifjv"
00007FF6077B17A6 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B17AA | 41:B8 28000000 | MOV R8D,28 | 28:'('
00007FF6077B17B0 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B17B3 | E8 D8040000 | CALL <JMP.&std::ostream::write(char const*, long long)> |
00007FF6077B17B8 | 48:8B95 E0030000 | MOV RDX,QWORD PTR SS:[RBP+3E0] |
00007FF6077B17BF | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B17C3 | 41:B8 00040000 | MOV R8D,400 |
00007FF6077B17C9 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B17CC | E8 BF040000 | CALL <JMP.&std::ostream::write(char const*, long long)> |
00007FF6077B17D1 | 48:83BD E0030000 00 | CMP QWORD PTR SS:[RBP+3E0],0 |
00007FF6077B17D9 | 74 0F | JE img.7FF6077B17EA |
00007FF6077B17DB | 48:8B85 E0030000 | MOV RAX,QWORD PTR SS:[RBP+3E0] |
00007FF6077B17E2 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B17E5 | E8 36040000 | CALL <JMP.&operator delete[](void*)> |
00007FF6077B17EA | 8B55 A4 | MOV EDX,DWORD PTR SS:[RBP-5C] |
00007FF6077B17ED | 0FB745 AE | MOVZX EAX,WORD PTR SS:[RBP-52] |
00007FF6077B17F1 | 0FB7C0 | MOVZX EAX,AX |
00007FF6077B17F4 | 0FAFC2 | IMUL EAX,EDX |
00007FF6077B17F7 | 83C0 1F | ADD EAX,1F |
00007FF6077B17FA | 8D50 1F | LEA EDX,QWORD PTR DS:[RAX+1F] |
00007FF6077B17FD | 85C0 | TEST EAX,EAX |
00007FF6077B17FF | 0F48C2 | CMOVS EAX,EDX |
00007FF6077B1802 | C1F8 05 | SAR EAX,5 |
00007FF6077B1805 | C1E0 02 | SHL EAX,2 |
00007FF6077B1808 | 8985 DC030000 | MOV DWORD PTR SS:[RBP+3DC],EAX |
00007FF6077B180E | 8B45 A8 | MOV EAX,DWORD PTR SS:[RBP-58] |
00007FF6077B1811 | 99 | CDQ |
00007FF6077B1812 | 31D0 | XOR EAX,EDX |
00007FF6077B1814 | 29D0 | SUB EAX,EDX |
00007FF6077B1816 | 0FAF85 DC030000 | IMUL EAX,DWORD PTR SS:[RBP+3DC] |
00007FF6077B181D | 48:98 | CDQE |
00007FF6077B181F | 48:8985 D0030000 | MOV QWORD PTR SS:[RBP+3D0],RAX |
00007FF6077B1826 | 48:8B85 D0030000 | MOV RAX,QWORD PTR SS:[RBP+3D0] |
00007FF6077B182D | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1830 | E8 DB030000 | CALL <JMP.&operator new[](unsigned long long)> |
00007FF6077B1835 | 48:8985 C8030000 | MOV QWORD PTR SS:[RBP+3C8],RAX | 111
00007FF6077B183C | 48:8B8D D0030000 | MOV RCX,QWORD PTR SS:[RBP+3D0] |
00007FF6077B1843 | 48:8B95 C8030000 | MOV RDX,QWORD PTR SS:[RBP+3C8] | 111
00007FF6077B184A | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B1851 | 49:89C8 | MOV R8,RCX | r8:&"114514puiqneravcixpbhqn;afnv-92piwgspdifjv", rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B1854 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1857 | E8 3C040000 | CALL <JMP.&std::istream::read(char*, long long)> |
00007FF6077B185C | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B1863 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1866 | E8 15040000 | CALL <JMP.&std::basic_ifstream<char, std::char_traits<ch |
00007FF6077B186B | 80BD 28040000 00 | CMP BYTE PTR SS:[RBP+428],0 |
00007FF6077B1872 | 74 24 | JE img.7FF6077B1898 |
00007FF6077B1874 | 48:8B85 D0030000 | MOV RAX,QWORD PTR SS:[RBP+3D0] |
00007FF6077B187B | 89C1 | MOV ECX,EAX |
00007FF6077B187D | 48:8B95 20040000 | MOV RDX,QWORD PTR SS:[RBP+420] |
00007FF6077B1884 | 48:8B85 C8030000 | MOV RAX,QWORD PTR SS:[RBP+3C8] | 关注此处[rbp+3C8],疑似存放处理后的数据的缓冲区
00007FF6077B188B | 49:89D0 | MOV R8,RDX | r8:&"114514puiqneravcixpbhqn;afnv-92piwgspdifjv", rdx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\enc.bmp"v"
00007FF6077B188E | 89CA | MOV EDX,ECX |
00007FF6077B1890 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1893 | E8 A8FBFFFF | CALL img.7FF6077B1440 | modifiedRC4
00007FF6077B1898 | 48:8B8D D0030000 | MOV RCX,QWORD PTR SS:[RBP+3D0] |
00007FF6077B189F | 48:8B95 C8030000 | MOV RDX,QWORD PTR SS:[RBP+3C8] | [rbp+3C8]:输出的图像数据
00007FF6077B18A6 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B18AA | 49:89C8 | MOV R8,RCX | R8:数据长度
00007FF6077B18AD | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B18B0 | E8 DB030000 | CALL <JMP.&std::ostream::write(char const*, long long)> | 写入加密后的数据
00007FF6077B18B5 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B18B9 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B18BC | E8 A7030000 | CALL <JMP.&std::basic_ofstream<char, std::char_traits<ch |
00007FF6077B18C1 | 48:83BD C8030000 00 | CMP QWORD PTR SS:[RBP+3C8],0 |
00007FF6077B18C9 | 74 0F | JE img.7FF6077B18DA |
00007FF6077B18CB | 48:8B85 C8030000 | MOV RAX,QWORD PTR SS:[RBP+3C8] |
00007FF6077B18D2 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B18D5 | E8 46030000 | CALL <JMP.&operator delete[](void*)> |
00007FF6077B18DA | BB 01000000 | MOV EBX,1 |
00007FF6077B18DF | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B18E3 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B18E6 | E8 6D030000 | CALL <JMP.&std::basic_ofstream<char, std::char_traits<ch |
00007FF6077B18EB | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B18F2 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B18F5 | E8 76030000 | CALL <JMP.&std::basic_ifstream<char, std::char_traits<ch |
00007FF6077B18FA | 89D8 | MOV EAX,EBX |
00007FF6077B18FC | EB 2E | JMP img.7FF6077B192C |
00007FF6077B18FE | 48:89C3 | MOV RBX,RAX | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1901 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
00007FF6077B1905 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1908 | E8 4B030000 | CALL <JMP.&std::basic_ofstream<char, std::char_traits<ch |
00007FF6077B190D | EB 03 | JMP img.7FF6077B1912 |
00007FF6077B190F | 48:89C3 | MOV RBX,RAX | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1912 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
00007FF6077B1919 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B191C | E8 4F030000 | CALL <JMP.&std::basic_ifstream<char, std::char_traits<ch |
00007FF6077B1921 | 48:89D8 | MOV RAX,RBX | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1924 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
00007FF6077B1927 | E8 74140000 | CALL <JMP.&_Unwind_Resume> |
00007FF6077B192C | 48:81C4 78040000 | ADD RSP,478 |
00007FF6077B1933 | 5B | POP RBX |
00007FF6077B1934 | 5D | POP RBP |
00007FF6077B1935 | C3 | RET |通过跟踪三次read的参数(查看读入内存的数据)可以发现,程序除了读取文件头之外还读取了随后的1024bytes。这刚开始其实只是一个猜想,但是通过修改自己创建的flag.bmp文件头后的部分数据可以验证猜想


继续运行程序,可以发现生成的enc.bmp中保留了这一部分修改后的数据,于是猜想程序在加密结果中保留了输入流缓冲区3的1024bytes,可以通过扩大修改的数据范围来验证猜想(这里选择进一步修改flag.bmp里1024bytes的结尾,这样开头结尾都有被修改的特征了)



于是加密结果中多出来的1024bytes来源就显而易见了。至此,程序读写文件的逻辑基本可以明确了:先保留文件头(0-0x36h)和文件头后的1024bytes,然后读取文件头之外的数据(0x36h-0x3589A9h)进行加密,加密完成后先向enc.bmp写入事先保留的文件头和1024bytes,再拼接上加密后的数据
1
2flag.bmp:文件头(大小0x36h)+图片数据(大小0x358974h)
enc.bmp:文件头(大小0x36h)+图片数据前1024bytes(大小0x400h)+加密后的图片数据(大小0x358974h)解决完文件读写逻辑分析后,接下来着手解决算法逆向。下面是modefiedRC4的详细分析
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__int64 __fastcall modifiedRC4(const char *input, __int64 a2, int numOfDataBytes, __int64 outputBuffer, __int64 key)
{
unsigned __int64 v5; // rax
_BYTE *v6; // rax
char v7; // dl
__int64 numOfEncBytes; // rax
int i; // [rsp+24h] [rbp-Ch]
int j; // [rsp+24h] [rbp-Ch]
int k; // [rsp+24h] [rbp-Ch]
int j_sbox; // [rsp+28h] [rbp-8h]
int j_; // [rsp+28h] [rbp-8h]
int i_sbox; // [rsp+2Ch] [rbp-4h]
int i_; // [rsp+2Ch] [rbp-4h]
for ( i = 0; i <= 255; ++i )
S[i] = i;
i_sbox = 0;
j_sbox = 0;
for ( j = 0; j <= 255; ++j )
{
i_sbox = (i_sbox + 1) % 256;
a2 = S[i_sbox] + j_sbox;
v5 = sub_7FF7688232B0(input, a2, j_sbox, key);// 实际上return key[8],调试可以发现v5即key[8]是0x2A,对应密钥长度
v6 = sub_7FF7688233D0(input, a2, i_sbox % v5, key);// if(key_len >= i_sbox%key_len ) return key[i_sbox%key_len]
v7 = a2 + *v6; // v7=S[i_sbox] + j_sbox + key[i_sbox%key_len]
LODWORD(v6) = ((a2 + *v6) >> 31) >> 24;
j_sbox = (v6 + v7) - v6; // 这两步实际上等价为j_sbox = j_sboxv7%256
swapbytes(input, a2, &S[j_sbox], &S[i_sbox]);// 实际上是swapbytes(&S[j_sbox],&S[i_sbox]),前两个参数没有使用
}
i_ = 0;
j_ = 0;
for ( k = 0; ; ++k )
{
numOfEncBytes = ((numOfDataBytes / 256) << 8);// 总数据量对256向下取整,保证加密数据量为256倍数
if ( k >= numOfEncBytes ) // k从0开始直到最大加密数据量
break;
i_ = (i_ + 1) % 256;
j_ = (S[i_] + j_) % 256;
swapbytes(input, a2, &S[j_], &S[i_]);
swapBytes(
input,
a2,
(((k / 256) << 8) + j_ + outputBuffer), // outputBuffer[((k/256)<<8)+j_],此处(k/256)<<8即取每块256bytes的基址,因此交换范围是当前所在的256bytes块
(outputBuffer + ((k / 256) << 8) + i_)); // outputBuffer[((k/256)<<8)+i_]
}
return numOfEncBytes;
}算法中第一、二个循环在初始化S盒,没有对输出数据outputBuffer进行处理,第三个循环才利用S盒的数据来加密outputBuffer。加密outputBuffer的时候,记录了加密的数据量,加密轮数和加密数据量一致。每轮加密同步交换了 i_ 和 j_ 处的S盒数据以及outputBuffer数据,需要注意的是outputBuffer在交换的时候被分成了若干个256bytes块,交换在每个256bytes块内进行。最直观的解密算法是记录下每轮加密使用的 i_ 和 j_ ,然后逆序使用这些索引对交换数据。解密脚本如下:
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
48def decrypt(input,output):
with open(input,'rb') as f:
enc=f.read()
bmp_header=enc[:0x36]#保留文件头
enc_data=bytearray(enc[0x436:])#加密数据为文件头+1024bytes后的数据,起始偏移即为0x436
data_len=len(enc_data)
aligned_len=(data_len//256)*256#总数据量向下对256取整,即保证数据量为256的倍数
key="114514puiqneravcixpbhqn;afnv-92piwgspdifjv"
key_len=len(key)
#初始化S盒
S=list(range(256))
j_sbox=0
i_sbox=0
for i in range(256):
i_sbox=(i_sbox+1)%256
j_sbox=(j_sbox+S[i_sbox]+ord(key[i_sbox%key_len]))%256
S[i_sbox],S[j_sbox]=S[j_sbox],S[i_sbox]
#解密
#生成交换对
i_=0
j_=0
swap_couples=[]
for k in range(aligned_len):
i_=(i_+1)%256
j_=(j_+S[i_])%256
S[i_],S[j_]=S[j_],S[i_]
base=(k//256)*256#取每块256bytes的基址
pos1=base+i_
pos2=base+j_
if pos1<aligned_len and pos2<aligned_len:
swap_couples.append((pos1,pos2))
#逆序使用交换对,解密数据
for pos1,pos2 in reversed(swap_couples):
enc_data[pos1],enc_data[pos2]=enc_data[pos2],enc_data[pos1]
with open(output,'wb') as f:
f.write(bmp_header)
f.write(enc_data)
print("[+]decrypt done!")
decrypt("enc.bmp","flag.bmp")解密可以得到下图(没有完全解密成功,但是不影响拿到flag)

flag:0xGame{5d0e6f18-ebc0-4231-b0e6-a5821327aefd}
蔷薇地狱
考点:复杂程序分析,RSA解密
难点还是在于复杂
附件是一个Int.exe,一个流量包,还有两个enc文件。运行程序会显示connect failed然后打印一些数据后闪退,同时同一目录下的文件会被加密生成加密文件


用IDA打开程序,shift+F12可以在字符串窗口找到运行程序时显示的”connect failed”(以及其他一些看起来和网络通信有关的字符串,如”127.0.0.1”,”socket failed”)

然后可以跟踪到下面这样一个很像网络通信的函数
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__int64 __fastcall sub_140003D46(__int64 a1, __int64 a2, u_short a3, int a4)
{
__int64 v4; // rbx
unsigned int Error; // eax
__int64 v6; // rax
unsigned int v7; // ebx
__int64 v8; // rbx
unsigned int v9; // eax
__int64 v10; // rax
int v11; // r8d
int v12; // r9d
__int64 v13; // rbx
unsigned int v14; // eax
__int64 v15; // rax
int v16; // edx
int v17; // r8d
int v18; // r9d
int v19; // ebx
int v20; // edx
int v21; // r8d
int v22; // r9d
const char *v23; // rax
__int64 v24; // rbx
unsigned int v25; // eax
__int64 v26; // rax
__int64 v27; // rax
__int64 v28; // rax
__int64 v29; // rax
int v30; // edx
int v31; // r8d
int v32; // r9d
__int64 v34[4]; // [rsp+0h] [rbp-80h] BYREF
_BYTE v35[32]; // [rsp+20h] [rbp-60h] BYREF
sockaddr name; // [rsp+40h] [rbp-40h] BYREF
unsigned int v37; // [rsp+1F4h] [rbp+174h]
SOCKET s; // [rsp+1F8h] [rbp+178h]
if ( WSAStartup(0x202u, (LPWSADATA)&v34[10]) )
{
v4 = std::operator<<<std::char_traits<char>>(a1, a2, "WSAStartup failed: ", &std::cerr);
Error = WSAGetLastError();
v6 = std::ostream::operator<<(a1, a2, Error, v4);
std::operator<<<std::char_traits<char>>(a1, a2, "\n", v6);
return 1;
}
else
{
s = socket(2, 1, 0);
if ( s == -1LL )
{
v8 = std::operator<<<std::char_traits<char>>(a1, a2, "socket failed: ", &std::cerr);
v9 = WSAGetLastError();
v10 = std::ostream::operator<<(a1, a2, v9, v8);
std::operator<<<std::char_traits<char>>(a1, a2, "\n", v10);
WSACleanup();
return 1;
}
else
{
name.sa_family = 2;
*(_WORD *)name.sa_data = htons(a3);
*(_DWORD *)&name.sa_data[2] = inet_addr("127.0.0.1");
if ( connect(s, &name, 16) == -1 )
{
v13 = std::operator<<<std::char_traits<char>>(a1, a2, "connect failed: ", &std::cerr);
v14 = WSAGetLastError();
v15 = std::ostream::operator<<(a1, a2, v14, v13);
std::operator<<<std::char_traits<char>>(a1, a2, "\n", v15);
closesocket(s);
WSACleanup();
return 1;
}
else
{
sub_140001760(a1, a2, a4, (unsigned int)v35, v11, v12, v34[0], v34[1]);
v19 = sub_1400087B0(a1, a2, v16, (unsigned int)v35, v17, v18, v34[0]);
v23 = (const char *)sub_1400087F0(a1, a2, v20, (unsigned int)v35, v21, v22, v34[0]);
v37 = send(s, v23, v19, 0);
if ( v37 == -1 )
{
v24 = std::operator<<<std::char_traits<char>>(a1, a2, "send failed: ", &std::cerr);
v25 = WSAGetLastError();
v26 = std::ostream::operator<<(a1, a2, v25, v24);
}
else
{
v27 = std::operator<<<std::char_traits<char>>(a1, a2, "Sent ", &std::cout);
v28 = std::ostream::operator<<(a1, a2, v37, v27);
v29 = std::operator<<<std::char_traits<char>>(a1, a2, " bytes: ", v28);
v26 = std::operator<<<char>(a1, a2, v35, v29);
}
std::operator<<<std::char_traits<char>>(a1, a2, "\n", v26);
closesocket(s);
WSACleanup();
v7 = 0;
sub_14000B400(a1, a2, v30, (unsigned int)v35, v31, v32, v34[0]);
}
}
}
return v7;
}回溯到上级函数,可以得到一个快700行代码的函数。代码量太大,人工分析很有难度,于是借助AI来分析大致逻辑。在关键地方加上注释后的函数如下
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692__int64 __fastcall sub_140005769(__int64 a1, __int64 a2, _QWORD *a3, int a4)
{
int v4; // r9d
char v5; // bl
int v6; // esi
int v7; // edx
int v8; // r8d
int v9; // r9d
char v10; // bl
int v11; // esi
int v12; // edx
int v13; // r8d
int v14; // r9d
char v15; // bl
__int64 v16; // rsi
int v17; // edx
int v18; // r8d
int v19; // r9d
int v20; // edx
int v21; // r8d
int v22; // r9d
unsigned int v23; // ebx
int v24; // r8d
int v25; // r9d
int v26; // r8d
int v27; // r9d
int v28; // edx
int v29; // r8d
int v30; // r9d
int v31; // r8d
int v32; // r9d
int v33; // r8d
int v34; // r9d
int v35; // edx
int v36; // r8d
int v37; // r9d
int v38; // r8d
int v39; // r9d
__int64 v40; // rax
int v41; // edx
int v42; // r8d
int v43; // r9d
int v44; // edx
int v45; // r8d
int v46; // r9d
int v47; // r8d
int v48; // r9d
__int64 v49; // rax
int v50; // edx
int v51; // r8d
int v52; // r9d
int v53; // edx
int v54; // r8d
int v55; // r9d
int v56; // r8d
int v57; // r9d
int v58; // edx
int v59; // r8d
int v60; // r9d
int v61; // edx
int v62; // r8d
int v63; // r9d
int v64; // r8d
int v65; // r9d
int v66; // edx
int v67; // r8d
int v68; // r9d
int v69; // edx
int v70; // r8d
int v71; // r9d
__int64 v72; // rbx
int v73; // r8d
int v74; // r9d
__int64 v75; // rax
int v76; // edx
int v77; // r8d
int v78; // r9d
int v79; // edx
int v80; // r8d
int v81; // r9d
__int64 v82; // rbx
int v83; // r8d
int v84; // r9d
__int64 v85; // rax
int v86; // edx
int v87; // r8d
int v88; // r9d
int v89; // edx
int v90; // r8d
int v91; // r9d
__int64 v92; // rdx
int v93; // r8d
int v94; // r9d
int v95; // edx
int v96; // r8d
int v97; // r9d
int v98; // r8d
int v99; // r9d
int v100; // r8d
int v101; // r9d
int v102; // edx
int v103; // r8d
int v104; // r9d
int v105; // r8d
int v106; // r9d
int v107; // r8d
int v108; // r9d
int v109; // edx
int v110; // r8d
int v111; // r9d
int v112; // r8d
int v113; // r9d
int v114; // edx
int v115; // r8d
int v116; // r9d
__int64 v117; // rdx
int v118; // r8d
int v119; // r9d
int v120; // eax
int v121; // r8d
int v122; // r9d
char v123; // r12
char v124; // r13
char v125; // r14
char v126; // r15
int v127; // r8d
int v128; // r9d
int v129; // r8d
int v130; // r9d
int v131; // r8d
int v132; // r9d
int v133; // edx
int v134; // r8d
int v135; // r9d
int v136; // r8d
int v137; // r9d
int v138; // r8d
int v139; // r9d
int v140; // r8d
int v141; // r9d
int v142; // r8d
int v143; // r9d
int v144; // r8d
int v145; // r9d
int v146; // r8d
int v147; // r9d
int v148; // edx
int v149; // r8d
int v150; // r9d
int v151; // edx
int v152; // r8d
int v153; // r9d
int v154; // r8d
int v155; // r9d
int v156; // r8d
int v157; // r9d
char v158; // bl
int v159; // edx
int v160; // r8d
int v161; // r9d
int v162; // edx
int v163; // r8d
int v164; // r9d
__int64 v165; // rbx
int v166; // r8d
int v167; // r9d
__int64 v168; // rax
int v169; // edx
int v170; // r8d
int v171; // r9d
int v172; // r8d
int v173; // r9d
int v174; // r8d
int v175; // r9d
int v176; // r9d
int v177; // r9d
int v178; // r9d
__int64 v179; // rdx
__int64 v180; // rdx
int v181; // edx
int v182; // r8d
int v183; // r9d
int v184; // r8d
int v185; // r9d
int v186; // r8d
int v187; // r9d
char v188; // bl
int v189; // edx
int v190; // r8d
int v191; // r9d
int v192; // edx
int v193; // r8d
int v194; // r9d
__int64 v195; // rbx
int v196; // r8d
int v197; // r9d
__int64 v198; // rax
int v199; // edx
int v200; // r8d
int v201; // r9d
int v202; // r8d
int v203; // r9d
int v204; // r9d
int v205; // edx
int v206; // r8d
int v207; // r9d
int v208; // r9d
int v209; // r9d
int v210; // r9d
__int64 v211; // rdx
__int64 v212; // rdx
int v213; // edx
int v214; // r8d
int v215; // r9d
__int64 v216; // rdx
int v217; // r8d
int v218; // r9d
int v219; // edx
int v220; // r8d
int v221; // r9d
int v222; // edx
int v223; // r8d
int v224; // r9d
int v225; // edx
int v226; // r8d
int v227; // r9d
int v228; // edx
int v229; // r8d
int v230; // r9d
__int64 v232; // [rsp+0h] [rbp-80h]
__int64 v233; // [rsp+0h] [rbp-80h]
__int64 v234; // [rsp+0h] [rbp-80h]
__int64 v235; // [rsp+0h] [rbp-80h]
__int64 v236; // [rsp+0h] [rbp-80h]
__int64 v237; // [rsp+0h] [rbp-80h]
__int64 v238; // [rsp+0h] [rbp-80h]
__int64 v239; // [rsp+0h] [rbp-80h]
__int64 v240; // [rsp+0h] [rbp-80h]
__int64 v241; // [rsp+0h] [rbp-80h]
__int64 v242; // [rsp+0h] [rbp-80h]
__int64 v243; // [rsp+0h] [rbp-80h]
__int64 v244; // [rsp+0h] [rbp-80h]
__int64 v245; // [rsp+0h] [rbp-80h]
__int64 v246; // [rsp+0h] [rbp-80h]
__int64 v247; // [rsp+0h] [rbp-80h]
__int64 v248; // [rsp+0h] [rbp-80h]
__int64 v249; // [rsp+0h] [rbp-80h]
__int64 v250; // [rsp+0h] [rbp-80h]
__int64 v251; // [rsp+0h] [rbp-80h]
__int64 v252; // [rsp+0h] [rbp-80h]
__int64 v253; // [rsp+0h] [rbp-80h]
__int64 v254; // [rsp+0h] [rbp-80h]
__int64 v255; // [rsp+0h] [rbp-80h]
__int64 v256; // [rsp+0h] [rbp-80h]
__int64 v257; // [rsp+0h] [rbp-80h]
__int64 v258; // [rsp+0h] [rbp-80h]
__int64 v259; // [rsp+0h] [rbp-80h]
__int64 v260; // [rsp+0h] [rbp-80h]
__int64 v261; // [rsp+0h] [rbp-80h]
__int64 v262; // [rsp+0h] [rbp-80h]
__int64 v263; // [rsp+0h] [rbp-80h]
__int64 v264; // [rsp+0h] [rbp-80h]
__int64 v265; // [rsp+0h] [rbp-80h]
__int64 v266; // [rsp+0h] [rbp-80h]
__int64 v267; // [rsp+0h] [rbp-80h]
__int64 v268; // [rsp+0h] [rbp-80h]
__int64 v269; // [rsp+0h] [rbp-80h]
__int64 v270; // [rsp+0h] [rbp-80h]
__int64 v271; // [rsp+0h] [rbp-80h]
__int64 v272; // [rsp+0h] [rbp-80h]
__int64 v273; // [rsp+0h] [rbp-80h]
__int64 v274; // [rsp+0h] [rbp-80h]
__int64 v275; // [rsp+0h] [rbp-80h]
__int64 v276; // [rsp+0h] [rbp-80h]
__int64 v277; // [rsp+0h] [rbp-80h]
__int64 v278; // [rsp+0h] [rbp-80h]
__int64 v279; // [rsp+0h] [rbp-80h]
__int64 v280; // [rsp+0h] [rbp-80h]
__int64 v281; // [rsp+0h] [rbp-80h]
__int64 v282; // [rsp+0h] [rbp-80h]
__int64 v283; // [rsp+0h] [rbp-80h]
HCRYPTKEY v284; // [rsp+0h] [rbp-80h]
__int64 v285; // [rsp+0h] [rbp-80h]
__int64 v286; // [rsp+0h] [rbp-80h]
__int64 v287; // [rsp+0h] [rbp-80h]
__int64 v288; // [rsp+0h] [rbp-80h]
__int64 v289; // [rsp+0h] [rbp-80h]
__int64 v290; // [rsp+0h] [rbp-80h]
__int64 v291; // [rsp+0h] [rbp-80h]
__int64 v292; // [rsp+0h] [rbp-80h]
__int64 v293; // [rsp+0h] [rbp-80h]
__int64 v294; // [rsp+0h] [rbp-80h]
__int64 v295; // [rsp+0h] [rbp-80h]
HCRYPTKEY v296; // [rsp+0h] [rbp-80h]
__int64 v297; // [rsp+0h] [rbp-80h]
__int64 v298; // [rsp+0h] [rbp-80h]
__int64 v299; // [rsp+0h] [rbp-80h]
__int64 v300; // [rsp+0h] [rbp-80h]
__int64 v301; // [rsp+0h] [rbp-80h]
__int64 v302; // [rsp+0h] [rbp-80h]
__int64 v303; // [rsp+8h] [rbp-78h]
__int64 v304; // [rsp+8h] [rbp-78h]
__int64 v305; // [rsp+8h] [rbp-78h]
__int64 v306; // [rsp+8h] [rbp-78h]
__int64 v307; // [rsp+8h] [rbp-78h]
__int64 v308; // [rsp+8h] [rbp-78h]
__int64 v309; // [rsp+8h] [rbp-78h]
__int64 v310; // [rsp+8h] [rbp-78h]
__int64 v311; // [rsp+8h] [rbp-78h]
__int64 v312; // [rsp+8h] [rbp-78h]
__int64 v313; // [rsp+8h] [rbp-78h]
__int64 v314; // [rsp+8h] [rbp-78h]
__int64 v315; // [rsp+8h] [rbp-78h]
__int64 v316; // [rsp+8h] [rbp-78h]
__int64 v317; // [rsp+8h] [rbp-78h]
__int64 v318; // [rsp+8h] [rbp-78h]
__int64 v319; // [rsp+8h] [rbp-78h]
__int64 v320; // [rsp+8h] [rbp-78h]
__int64 v321; // [rsp+8h] [rbp-78h]
__int64 v322; // [rsp+8h] [rbp-78h]
__int64 v323; // [rsp+8h] [rbp-78h]
__int64 v324; // [rsp+8h] [rbp-78h]
__int64 v325; // [rsp+8h] [rbp-78h]
__int64 v326; // [rsp+8h] [rbp-78h]
__int64 v327; // [rsp+8h] [rbp-78h]
__int64 v328; // [rsp+8h] [rbp-78h]
__int64 v329; // [rsp+8h] [rbp-78h]
__int64 v330; // [rsp+8h] [rbp-78h]
__int64 v331; // [rsp+8h] [rbp-78h]
__int64 v332; // [rsp+8h] [rbp-78h]
__int64 v333; // [rsp+8h] [rbp-78h]
__int64 v334; // [rsp+8h] [rbp-78h]
__int64 v335; // [rsp+8h] [rbp-78h]
__int64 v336; // [rsp+8h] [rbp-78h]
__int64 v337; // [rsp+8h] [rbp-78h]
__int64 v338; // [rsp+8h] [rbp-78h]
__int64 v339; // [rsp+8h] [rbp-78h]
int v340; // [rsp+10h] [rbp-70h]
int v341; // [rsp+10h] [rbp-70h]
__int64 v342; // [rsp+10h] [rbp-70h]
int v343; // [rsp+10h] [rbp-70h]
int v344; // [rsp+10h] [rbp-70h]
__int64 v345; // [rsp+10h] [rbp-70h]
char v346; // [rsp+30h] [rbp-50h]
char v347; // [rsp+38h] [rbp-48h]
_BYTE v348[48]; // [rsp+40h] [rbp-40h] BYREF
_BYTE v349[48]; // [rsp+70h] [rbp-10h] BYREF
int v350[124]; // [rsp+A0h] [rbp+20h] BYREF
int v351[124]; // [rsp+290h] [rbp+210h] BYREF
_BYTE v352[16]; // [rsp+480h] [rbp+400h] BYREF
_BYTE v353[16]; // [rsp+490h] [rbp+410h] BYREF
BYTE v354[28]; // [rsp+4A0h] [rbp+420h] BYREF
BYTE v355[4]; // [rsp+4BCh] [rbp+43Ch] BYREF
BYTE pbData[2]; // [rsp+4C0h] [rbp+440h] BYREF
__int16 v357; // [rsp+4C2h] [rbp+442h]
int v358; // [rsp+4C4h] [rbp+444h]
int v359; // [rsp+4C8h] [rbp+448h]
__int64 v360; // [rsp+4CCh] [rbp+44Ch]
__int64 v361; // [rsp+4D4h] [rbp+454h]
__int64 v362; // [rsp+4DCh] [rbp+45Ch]
__int64 v363; // [rsp+4E4h] [rbp+464h]
_BYTE IV[32]; // [rsp+4F0h] [rbp+470h] BYREF IV
_BYTE key[32]; // [rsp+510h] [rbp+490h] BYREF 密钥
BYTE MODE[32]; // [rsp+530h] [rbp+4B0h] BYREF 模式参数
BYTE v367[16]; // [rsp+550h] [rbp+4D0h] BYREF
BYTE pbBuffer[8]; // [rsp+560h] [rbp+4E0h] BYREF
__int64 v369; // [rsp+568h] [rbp+4E8h]
__int64 v370; // [rsp+570h] [rbp+4F0h]
__int64 v371; // [rsp+578h] [rbp+4F8h]
HCRYPTKEY hKey; // [rsp+588h] [rbp+508h] BYREF
HCRYPTPROV phProv; // [rsp+590h] [rbp+510h] BYREF
char v374; // [rsp+59Ah] [rbp+51Ah] BYREF
char v375; // [rsp+59Bh] [rbp+51Bh] BYREF
char v376; // [rsp+59Ch] [rbp+51Ch] BYREF
char v377; // [rsp+59Dh] [rbp+51Dh] BYREF
char v378; // [rsp+59Eh] [rbp+51Eh] BYREF
char v379; // [rsp+59Fh] [rbp+51Fh] BYREF
_BYTE v380[32]; // [rsp+5A0h] [rbp+520h] BYREF
_BYTE v381[32]; // [rsp+5C0h] [rbp+540h] BYREF
_BYTE v382[32]; // [rsp+5E0h] [rbp+560h] BYREF
_BYTE v383[16]; // [rsp+600h] [rbp+580h] BYREF
_BYTE v384[32]; // [rsp+610h] [rbp+590h] BYREF
_BYTE v385[16]; // [rsp+630h] [rbp+5B0h] BYREF
_BYTE v386[16]; // [rsp+640h] [rbp+5C0h] BYREF
_BYTE v387[16]; // [rsp+650h] [rbp+5D0h] BYREF
_BYTE v388[16]; // [rsp+660h] [rbp+5E0h] BYREF
_BYTE v389[16]; // [rsp+670h] [rbp+5F0h] BYREF
_BYTE v390[32]; // [rsp+680h] [rbp+600h] BYREF
_BYTE v391[16]; // [rsp+6A0h] [rbp+620h] BYREF
_BYTE v392[32]; // [rsp+6B0h] [rbp+630h] BYREF
_BYTE v393[16]; // [rsp+6D0h] [rbp+650h] BYREF
_BYTE v394[48]; // [rsp+6E0h] [rbp+660h] BYREF
_BYTE v395[16]; // [rsp+710h] [rbp+690h] BYREF
_BYTE v396[16]; // [rsp+720h] [rbp+6A0h] BYREF
_BYTE v397[32]; // [rsp+730h] [rbp+6B0h] BYREF
_BYTE v398[48]; // [rsp+750h] [rbp+6D0h] BYREF
_BYTE v399[32]; // [rsp+780h] [rbp+700h] BYREF
_BYTE v400[48]; // [rsp+7A0h] [rbp+720h] BYREF
_BYTE v401[32]; // [rsp+7D0h] [rbp+750h] BYREF
_BYTE v402[48]; // [rsp+7F0h] [rbp+770h] BYREF
_BYTE v403[32]; // [rsp+820h] [rbp+7A0h] BYREF
_BYTE v404[48]; // [rsp+840h] [rbp+7C0h] BYREF
_BYTE v405[32]; // [rsp+870h] [rbp+7F0h] BYREF
_BYTE v406[48]; // [rsp+890h] [rbp+810h] BYREF
_BYTE v407[32]; // [rsp+8C0h] [rbp+840h] BYREF
_BYTE v408[32]; // [rsp+8E0h] [rbp+860h] BYREF
_BYTE v409[48]; // [rsp+900h] [rbp+880h] BYREF
_BYTE v410[32]; // [rsp+930h] [rbp+8B0h] BYREF
_BYTE v411[40]; // [rsp+950h] [rbp+8D0h] BYREF
char *v412; // [rsp+978h] [rbp+8F8h]
char *v413; // [rsp+980h] [rbp+900h]
char *v414; // [rsp+988h] [rbp+908h]
char *v415; // [rsp+990h] [rbp+910h]
char *v416; // [rsp+998h] [rbp+918h]
char *v417; // [rsp+9A0h] [rbp+920h]
__int64 v418; // [rsp+9A8h] [rbp+928h]
BYTE *v419; // [rsp+9B0h] [rbp+930h]
char v420; // [rsp+9BFh] [rbp+93Fh]
sub_140006CC0();
v420 = 0;
v5 = 0;
v6 = 0;
if ( a4 <= 1 ) // 此处开始解析命令行参数,这是第一个参数的处理
{
v416 = &v375;
v6 = 1;
sub_14000B350(a1, 1, (unsigned int)&unk_14000E1D0, (unsigned int)MODE, (unsigned int)&v375, v4);
}
else
{
v417 = &v374;
v5 = 1;
sub_14000B350(a1, 0, a3[1], (unsigned int)MODE, (unsigned int)&v374, v4);
}
if ( (_BYTE)v6 )
sub_140009C30(a1, v6, v7, (unsigned int)&v375, v8, v9);
if ( v5 )
sub_140009C30(a1, v6, v7, (unsigned int)&v374, v8, v9);
v10 = 0;
v11 = 0;
if ( a4 <= 2 ) // 这是第二个参数的处理
{
v414 = &v377;
v11 = 1;
sub_14000B350(a1, 1, (unsigned int)&unk_14000E1D0, (unsigned int)key, (unsigned int)&v377, v9);
}
else
{
v415 = &v376;
v10 = 1;
sub_14000B350(a1, 0, a3[2], (unsigned int)key, (unsigned int)&v376, v9);
}
if ( (_BYTE)v11 )
sub_140009C30(a1, v11, v12, (unsigned int)&v377, v13, v14);
if ( v10 )
sub_140009C30(a1, v11, v12, (unsigned int)&v376, v13, v14);
v15 = 0;
v16 = 0LL;
if ( a4 <= 3 ) // 这是第三个参数的处理
{
v412 = &v379;
v16 = 1LL;
sub_14000B350(a1, 1, (unsigned int)&unk_14000E1D0, (unsigned int)IV, (unsigned int)&v379, v14);
}
else
{
v413 = &v378;
v15 = 1;
sub_14000B350(a1, 0, a3[3], (unsigned int)IV, (unsigned int)&v378, v14);
}
if ( (_BYTE)v16 )
sub_140009C30(a1, v16, v17, (unsigned int)&v379, v18, v19);
if ( v15 )
sub_140009C30(a1, v16, v17, (unsigned int)&v378, v18, v19);
if ( CryptAcquireContextA(&phProv, 0LL, 0LL, 0x18u, 0xF0000000) )// 使用Microsoft CryptoAPI,0X18表示PROV_RSA_AES,程序使用RSA加密密钥和IV发送到服务器
{
if ( a4 <= 3 )
{
if ( !CryptGenRandom(phProv, 0x20u, pbBuffer) )// 生成32字节随机密钥
{
v23 = 1;
goto LABEL_63;
}
if ( !CryptGenRandom(phProv, 0x10u, v367) )// 生成16字节随机IV
{
v23 = 1;
goto LABEL_63;
}
sub_1400028FE(a1, v16, (__int64)pbBuffer, (__int64)v387, 32);
sub_140005071(a1, v16, (unsigned int)v387, (unsigned int)v386, v56, v57, v232, v308);// 疑似加密函数
sub_140003D46(a1, v16, 0x1F90u, (int)v386);// 疑似网络通信的函数
sub_1400029A2(a1, v16, v58, (unsigned int)v386, v59, v60, v243);
sub_1400029A2(a1, v16, v61, (unsigned int)v387, v62, v63, v244);
sub_1400028FE(a1, v16, (__int64)v367, (__int64)v389, 16);
sub_140005071(a1, v16, (unsigned int)v389, (unsigned int)v388, v64, v65, v245, v309);
sub_140003D46(a1, v16, 0x1F90u, (int)v388);
sub_1400029A2(a1, v16, v66, (unsigned int)v388, v67, v68, v246);
sub_1400029A2(a1, v16, v69, (unsigned int)v389, v70, v71, v247);
}
else
{
if ( sub_14000C650(a1, v16, (int)"-d", (int)MODE, v21, v22) )
v420 = 1; // 命令行参数包含"-d"的时候,v449被设为1,进入解密模式
sub_14000B130(a1, v16, (unsigned int)key, (unsigned int)v380, v24, v25, v232, v303);// 从命令行获取key
sub_1400051EC(a1, v16, (__int64)pbBuffer, (int)v380, v26, v27);// 处理key
sub_14000B400(a1, v16, v28, (unsigned int)v380, v29, v30, v233);
sub_14000B130(a1, v16, (unsigned int)IV, (unsigned int)v381, v31, v32, v234, v304);// 从命令行获取IV
sub_1400051EC(a1, v16, (__int64)v367, (int)v381, v33, v34);// 处理IV
sub_14000B400(a1, v16, v35, (unsigned int)v381, v36, v37, v235);
sub_1400028FE(a1, v16, (__int64)pbBuffer, (__int64)v383, 32);
sub_140001534(a1, v16, (unsigned int)v383, (unsigned int)v382, v38, v39, v236, v305);
v40 = std::operator<<<char>(a1, v16, v382, &std::cout);
std::ostream::operator<<(a1, v16, &std::endl<char,std::char_traits<char>>, v40);
sub_14000B400(a1, v16, v41, (unsigned int)v382, v42, v43, v237);
sub_1400029A2(a1, v16, v44, (unsigned int)v383, v45, v46, v238);
sub_1400028FE(a1, v16, (__int64)v367, (__int64)v385, 16);
sub_140001534(a1, v16, (unsigned int)v385, (unsigned int)v384, v47, v48, v239, v306);
v49 = std::operator<<<char>(a1, v16, v384, &std::cout);
std::ostream::operator<<(a1, v16, &std::endl<char,std::char_traits<char>>, v49);
sub_14000B400(a1, v16, v50, (unsigned int)v384, v51, v52, v240);
sub_1400029A2(a1, v16, v53, (unsigned int)v385, v54, v55, v241);
}
v72 = std::operator<<<std::char_traits<char>>(a1, v16, "Key: ", &std::cout);
sub_1400028FE(a1, v16, (__int64)pbBuffer, (__int64)v391, 32);
sub_140001534(a1, v16, (unsigned int)v391, (unsigned int)v390, v73, v74, v242, v307);
v75 = std::operator<<<char>(a1, v16, v390, v72);
std::ostream::operator<<(a1, v16, &std::endl<char,std::char_traits<char>>, v75);
sub_14000B400(a1, v16, v76, (unsigned int)v390, v77, v78, v248);
sub_1400029A2(a1, v16, v79, (unsigned int)v391, v80, v81, v249);
v82 = std::operator<<<std::char_traits<char>>(a1, v16, "IV: ", &std::cout);
sub_1400028FE(a1, v16, (__int64)v367, (__int64)v393, 16);
sub_140001534(a1, v16, (unsigned int)v393, (unsigned int)v392, v83, v84, v250, v310);
v85 = std::operator<<<char>(a1, v16, v392, v82);
std::ostream::operator<<(a1, v16, &std::endl<char,std::char_traits<char>>, v85);
sub_14000B400(a1, v16, v86, (unsigned int)v392, v87, v88, v251);
sub_1400029A2(a1, v16, v89, (unsigned int)v393, v90, v91, v252);
pbData[0] = 8;
pbData[1] = 2;
v357 = 0;
v358 = 26128;
v359 = 32;
v360 = *(_QWORD *)pbBuffer;
v361 = v369;
v362 = v370;
v363 = v371;
*(_DWORD *)v355 = 2;
CryptImportKey(phProv, pbData, 0x2Cu, 0LL, 0, &hKey);// 导入密钥
CryptSetKeyParam(hKey, 4u, v355, 0); // 设置密钥参数
CryptSetKeyParam(hKey, 1u, v367, 0); // 设置IV
std::filesystem::current_path[abi:cxx11](a1, v16, v92, v394);// 获取当前路径
sub_140008E50(a1, v16, (unsigned int)v394, (unsigned int)v354, v93, v94, v253, v311);
v419 = v354;
sub_1400091B0(a1, v16, v95, (unsigned int)v394, v96, v97, v254);
sub_140008E90(a1, v16, (_DWORD)v419, (unsigned int)v395, v98, v99, v255, v312);
sub_140009230(a1, v16, (unsigned int)v395, (unsigned int)v353, v100, v101, v256, v313);
sub_140008EE0(a1, v16, v102, (unsigned int)v395, v103, v104, v257);
sub_140008E90(a1, v16, (_DWORD)v419, (unsigned int)v396, v105, v106, v258, v314);
sub_140008F00(a1, v16, (unsigned int)v396, (unsigned int)v352, v107, v108, v259, v315);
sub_140008EE0(a1, v16, v109, (unsigned int)v396, v110, v111, v260);
while ( (unsigned __int8)sub_1400092E0(a1, v16, (unsigned int)v352, (unsigned int)v353, v112, v113, v261, v316) )// 遍历当前路径下的所有文件
{
v418 = std::filesystem::__cxx11::directory_iterator::operator*(a1, v16, v216, v353);
if ( (unsigned __int8)sub_1400082F0(a1, v16, v114, v418, v115, v116, v298) == 1 )
{
v120 = sub_140008320(a1, v16, v117, v418, v118, v119, v261);
sub_140009030(a1, v16, v120, (unsigned int)v348, v121, v122, v262, v316);
a1 = 0LL;
v123 = 0;
v124 = 0;
v125 = 0;
v126 = 0;
v347 = 0;
sub_140008440(0, 0, (unsigned int)v348, (unsigned int)v398, v127, v128, v263, v317);
sub_1400083D0(0, 0, (unsigned int)v398, (unsigned int)v397, v129, v130, v264, v318);
v16 = 1LL;
if ( (unsigned __int8)sub_14000C6D0(0, 1, (unsigned int)L".exe", (unsigned int)v397, v131, v132, v265, v319) )// 跳过exe文件
goto LABEL_39;
sub_140008440(0, 1, (unsigned int)v348, (unsigned int)v400, v134, v135, v266, v316);
a1 = 1LL;
sub_1400083D0(1, 1, (unsigned int)v400, (unsigned int)v399, v136, v137, v267, v320);
v123 = 1;
if ( (unsigned __int8)sub_14000C6D0(1, 1, (unsigned int)".", (unsigned int)v399, v138, v139, v268, v321) )
goto LABEL_39;
sub_140008440(1, 1, (unsigned int)v348, (unsigned int)v402, v134, v135, v266, v316);
v124 = 1;
sub_1400083D0(1, 1, (unsigned int)v402, (unsigned int)v401, v140, v141, v269, v322);
v125 = 1;
if ( (unsigned __int8)sub_14000C6D0(1, 1, (unsigned int)L".dec", (unsigned int)v401, v142, v143, v270, v323)// 跳过dec文件
|| (sub_140008440(1, 1, (unsigned int)v348, (unsigned int)v404, v134, v135, v266, v316),
v126 = 1,
sub_1400083D0(1, 1, (unsigned int)v404, (unsigned int)v403, v144, v145, v271, v324),
v347 = 1,
(unsigned __int8)sub_14000C6D0(1, 1, (unsigned int)".", (unsigned int)v403, v146, v147, v272, v325)) )// 跳过当前目录的.文件
{
LABEL_39:
v346 = 1;
}
else
{
v346 = 0;
}
if ( v347 )
sub_14000C290(a1, 1, v133, (unsigned int)v403, v134, v135, v266);
if ( v126 )
sub_1400091B0(a1, 1, v133, (unsigned int)v404, v134, v135, v266);
if ( v125 )
sub_14000C290(a1, 1, v133, (unsigned int)v401, v134, v135, v266);
if ( v124 )
sub_1400091B0(a1, 1, v133, (unsigned int)v402, v134, v135, v266);
if ( v123 )
sub_14000C290(a1, 1, v133, (unsigned int)v399, v134, v135, v266);
if ( (_BYTE)a1 )
sub_1400091B0(a1, 1, v133, (unsigned int)v400, v134, v135, v266);
sub_14000C290(a1, 1, v133, (unsigned int)v397, v134, v135, v266);
sub_1400091B0(a1, 1, v148, (unsigned int)v398, v149, v150, v273);
if ( !v346 )
{
if ( v420 != 1 ) // 加密模式
{
sub_140008440(a1, 1, (unsigned int)v348, (unsigned int)v406, v152, v153, v274, v316);
sub_1400083D0(a1, 1, (unsigned int)v406, (unsigned int)v405, v154, v155, v275, v326);
v158 = sub_14000C6D0(a1, 1, (unsigned int)".", (unsigned int)v405, v156, v157, v276, v327);
sub_14000C290(a1, 1, v159, (unsigned int)v405, v160, v161, v277);
sub_1400091B0(a1, 1, v162, (unsigned int)v406, v163, v164, v278);
if ( !v158 )
{
v165 = std::operator<<<wchar_t,std::char_traits<wchar_t>>(a1, 1LL, L"Encrypting file: ", &std::wcout);
sub_1400083D0(a1, 1, (unsigned int)v348, (unsigned int)v407, v166, v167, v274, v316);
v168 = std::operator<<<wchar_t>(a1, 1LL, v407, v165);
std::wostream::operator<<(a1, 1LL, &std::endl<wchar_t,std::char_traits<wchar_t>>, v168);
sub_14000C290(a1, 1, v169, (unsigned int)v407, v170, v171, v279);
sub_140009030(a1, 1, (unsigned int)v348, (unsigned int)v349, v172, v173, v280, v328);
sub_1400091E0(a1, 1, (unsigned int)".", (unsigned int)v349, v174, v175, v281, v329);
sub_140009B20(a1, 1, (unsigned int)v348, (unsigned int)v351, 4, v176, v282, v330, v340);// 读取输入文件
sub_140009B60(a1, 1, (unsigned int)v349, (unsigned int)v350, 4, v177, v283, v331, v341);// 准备输出文件
sub_1400052D0(a1, 1, (int)v351, hKey, (int)v350, v178, v284, v332, v342);// 执行加密
std::ofstream::~ofstream(a1, 1LL, v179, v350);
std::ifstream::~ifstream(a1, 1LL, v180, v351);
sub_1400091B0(a1, 1, v181, (unsigned int)v349, v182, v183, v285);
}
}
else // 解密模式
{
sub_140008440(a1, 1, (unsigned int)v348, (unsigned int)v409, v152, v153, v274, v316);
sub_1400083D0(a1, 1, (unsigned int)v409, (unsigned int)v408, v184, v185, v286, v333);
v188 = sub_14000C750(a1, 1, (unsigned int)".", (unsigned int)v408, v186, v187, v287, v334);
sub_14000C290(a1, 1, v189, (unsigned int)v408, v190, v191, v288);
sub_1400091B0(a1, 1, v192, (unsigned int)v409, v193, v194, v289);
if ( !v188 )
{
v195 = std::operator<<<wchar_t,std::char_traits<wchar_t>>(a1, 1LL, L"Decrypting file: ", &std::wcout);
sub_1400083D0(a1, 1, (unsigned int)v348, (unsigned int)v410, v196, v197, v274, v316);
v198 = std::operator<<<wchar_t>(a1, 1LL, v410, v195);
std::wostream::operator<<(a1, 1LL, &std::endl<wchar_t,std::char_traits<wchar_t>>, v198);
sub_14000C290(a1, 1, v199, (unsigned int)v410, v200, v201, v290);
sub_140009030(a1, 1, (unsigned int)v348, (unsigned int)v349, v202, v203, v291, v335);
sub_1400090C0(a1, 1, (unsigned int)L"dec", (unsigned int)v411, 2, v204, v292, v336, v340);
std::filesystem::__cxx11::path::replace_extension(
(std::filesystem::__cxx11::path *)a1,
(const std::filesystem::__cxx11::path *)1);
sub_1400091B0(a1, 1, v205, (unsigned int)v411, v206, v207, v293);
sub_140009B20(a1, 1, (unsigned int)v348, (unsigned int)v351, 4, v208, v294, v337, v343);// 读取加密文件
sub_140009B60(a1, 1, (unsigned int)v349, (unsigned int)v350, 4, v209, v295, v338, v344);// 准备输出文件
sub_140005522(a1, 1, (int)v351, hKey, (int)v350, v210, v296, v339, v345);// 执行解密
std::ofstream::~ofstream(a1, 1LL, v211, v350);
std::ifstream::~ifstream(a1, 1LL, v212, v351);
sub_1400091B0(a1, 1, v213, (unsigned int)v349, v214, v215, v297);
}
}
}
sub_1400091B0(a1, 1, v151, (unsigned int)v348, v152, v153, v274);
}
std::filesystem::__cxx11::directory_iterator::operator++(a1, v16, v117, v353);
}
sub_140008EE0(a1, v16, v216, (unsigned int)v352, v217, v218, v298);
sub_140008EE0(a1, v16, v219, (unsigned int)v353, v220, v221, v299);
sub_140008EE0(a1, v16, v222, (unsigned int)v354, v223, v224, v300);
CryptDestroyKey(hKey);
CryptReleaseContext(phProv, 0);
v23 = 0;
}
else
{
v23 = 1;
}
LABEL_63:
sub_14000B400(a1, v16, v20, (unsigned int)IV, v21, v22, v232);
sub_14000B400(a1, v16, v225, (unsigned int)key, v226, v227, v301);
sub_14000B400(a1, v16, v228, (unsigned int)MODE, v229, v230, v302);
return v23;
}下面是疑似的加密函数的内容
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_140005071(int a1, int a2, int a3, __int64 a4, __int64 a5, int a6)
{
int v6; // r8d
int v7; // r9d
int v8; // edx
int v9; // r8d
int v10; // r9d
int v11; // edx
int v12; // r8d
int v13; // r9d
int v14; // r9d
int v15; // r8d
int v16; // r9d
int v17; // edx
int v18; // r8d
int v19; // r9d
int v20; // edx
int v21; // r8d
int v22; // r9d
int v23; // edx
int v24; // r8d
int v25; // r9d
int v26; // edx
int v27; // r8d
int v28; // r9d
__int64 v30; // [rsp+0h] [rbp-B0h] BYREF
__int64 v31; // [rsp+8h] [rbp-A8h]
__int64 v32; // [rsp+10h] [rbp-A0h]
__int64 v33; // [rsp+18h] [rbp-98h]
_BYTE v34[16]; // [rsp+20h] [rbp-90h] BYREF
_BYTE v35[16]; // [rsp+30h] [rbp-80h] BYREF
_BYTE v36[47]; // [rsp+40h] [rbp-70h] BYREF
char v37; // [rsp+6Fh] [rbp-41h] BYREF
_BYTE v38[47]; // [rsp+70h] [rbp-40h] BYREF
char v39; // [rsp+9Fh] [rbp-11h] BYREF
char *v40; // [rsp+A0h] [rbp-10h]
char *v41; // [rsp+A8h] [rbp-8h]
v41 = (char *)&v30 + 111;
sub_14000B350(
a1,
a2,
(unsigned int)"1294787702630841806287693875819503767329411267154281759595683996937344294354199",
(unsigned int)(&v30 - 14) + 176,
(unsigned int)&v30 - 65 + 176,
a6);
sub_1400024EE(a1, a2, (unsigned int)v36, (unsigned int)v35, v6, v7, v30, v31);
sub_14000B400(a1, a2, v8, (unsigned int)v36, v9, v10, v30);
sub_140009C30(a1, a2, v11, (unsigned int)&v37, v12, v13);
v40 = &v39;
sub_14000B350(a1, a2, (unsigned int)"65537", (unsigned int)v38, (unsigned int)&v39, v14);// 65537是典型RSA加密的特征,因此推测此函数为RSA加密
sub_1400024EE(a1, a2, (unsigned int)v38, (unsigned int)v34, v15, v16, v30, v31);
sub_14000B400(a1, a2, v17, (unsigned int)v38, v18, v19, v30);
sub_140009C30(a1, a2, v20, (unsigned int)&v39, v21, v22);
sub_1400031A0(a1, a2, a3, a4, (unsigned int)v34, (unsigned int)v35, v30, v31, v32, v33);
sub_1400029A2(a1, a2, v23, (unsigned int)v34, v24, v25, v30);
sub_1400029A2(a1, a2, v26, (unsigned int)v35, v27, v28, v30);
return a4;
}可以得到如下的印象点:
- 程序通过参数个数判断使用加密模式还是解密模式;
- 程序在加密模式下会遍历同一路径下的文件并进行加密,输出.enc文件,同时程序会将加密使用的key和IV进行RSA加密后发送到服务器(此外由于网络通信函数出现了127.0.0.1,推测是本地服务器);
- 程序在解密模式下只需要使用Int.exe -d key IV就可以解密文件。
结合附件的形式(附件包含一个流量包),可以推测本题的突破口在于分析流量包提取出RSA加密后的key和IV,然后就可以解密出key和IV,使用程序自带的解密功能解密文件。
安全起见,第一步先验证猜想。借助AI搭建一个本地服务器来接收程序的通信数据
1
2
3
4
5
6
7
8
9
10
11
12# 本地服务器脚本
# fake_server.py
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', 8080))
sock.listen(1)
print("Listening on 127.0.0.1:8080...")
while True:
conn, addr = sock.accept()
data = conn.recv(1024)
print(f"Received {len(data)} bytes: {data.hex()}")
conn.close()启用服务器后,通过命令行运行Int.exe可以看到本次加密使用的key和IV,以及发送的十进制数据

服务器会接收到如下数据(服务器接收的数据是发送的数据的十六进制形式)


然后参考RSA加密解密原理深度剖析可以写出一个脚本来解密
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
29import gmpy2
#求解d
p =gmpy2.mpz(973424135910675582062565817184635606523)
q =gmpy2.mpz( 1330137249390799512563582841814876337813)
e =gmpy2.mpz(65537)
phi_n= (p - 1) * (q - 1)
d = gmpy2.invert(e, phi_n)
print("d is:")
print (d)
#解密key和IV
n = gmpy2.mpz(1294787702630841806287693875819503767329411267154281759595683996937344294354199)
#key_raw是服务器接收的第一个数据,即key加密数据的十六进制数据流
key_raw="31323532333634383535303738343134373534303233363633363831313039303038333936313636303538303434333331323331383735303239343634393636353236373133353939343233363539"
#iv_raw是服务器接收的第二个数据,即iv加密数据的十六进制数据流
iv_raw="393235323537333232303933353132333132353838343232323434353732343930303235353236393838363134393132323438313237363234363634333434323034333234353839393230383333"
#十六进制解码
key_hexdec=bytes.fromhex(key_raw)
iv_hexdec=bytes.fromhex(iv_raw)
key_enc = gmpy2.mpz(key_hexdec)
IV_enc = gmpy2.mpz(iv_hexdec)
#RSA解密
key_dec = pow(key_enc, d, n)
IV_dec = pow(IV_enc, d, n)
print("key (hex):", hex(key_dec))
print("IV (hex):", hex(IV_dec))运行解密脚本得到如下,key和iv与Int.exe的输出一致

至此,可以确定发送的数据为key和IV的RSA加密结果,服务器接收的数据是加密结果的十六进制编码数据。
接下来分析流量包提取加密数据流(需要用到WireShark)。由于程序将数据发送到本地8080端口,且数据长度大约为78bytes,根据这个特征可以筛选出key和IV的流量


将找到的两串数据复制到脚本的key_raw和iv_raw,可以解密出key和IV
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
29import gmpy2
#求解d
p =gmpy2.mpz(973424135910675582062565817184635606523)
q =gmpy2.mpz( 1330137249390799512563582841814876337813)
e =gmpy2.mpz(65537)
phi_n= (p - 1) * (q - 1)
d = gmpy2.invert(e, phi_n)
print("d is:")
print (d)
#解密key和IV
n = gmpy2.mpz(1294787702630841806287693875819503767329411267154281759595683996937344294354199)
#key_raw是服务器接收的第一个数据,即key加密数据的十六进制数据流
key_raw="393430353632303933303434363731383332313634363031353936303437303036303837353338373330383137373132363136313931393431323130333436353936333530353534323336333632"
#iv_raw是服务器接收的第二个数据,即iv加密数据的十六进制数据流
iv_raw="323636373435303139343936383836343938363534323735373736393036303037343539343139323635373931333033373337353137333038363234343631363938313438363430393939363639"
#十六进制解码
key_hexdec=bytes.fromhex(key_raw)
iv_hexdec=bytes.fromhex(iv_raw)
key_enc = gmpy2.mpz(key_hexdec)
IV_enc = gmpy2.mpz(iv_hexdec)
#RSA解密
key_dec = pow(key_enc, d, n)
IV_dec = pow(IV_enc, d, n)
print("key (hex):", hex(key_dec))
print("IV (hex):", hex(IV_dec))
然后就可以使用Int.exe -d key iv解密文件了(注意key和IV不要带0x)


打开txt.dec文件就可以找到flag了

flag:0xGame{I_4m_3o_S1eepy_(´~`)zzZ_zzZ_zzZ_zzZ}
0xGame2025Week4WriteUp
https://sydzi.github.io/2025/11/16/0xGame2025Week4ReverseWriteUp/