GeekGame 2025 re wp

签到

image-20251020200147600

在线网站把所有帧提出来,第一个帧是背景可以写个妙妙小脚本

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
from PIL import Image, ImageChops
import numpy as np

# 文件名
background_path = "1.png"
layers = [f"{i}.png" for i in range(2, 10)]

# 读取背景图
background = Image.open(background_path).convert("L") # 转灰度

# 初始化一个全白背景
result = Image.new("L", background.size, 255)

# 处理每一层
for path in layers:
img = Image.open(path).convert("L")
# 与背景做差
diff = ImageChops.difference(img, background)
diff_np = np.array(diff)
# 阈值分割:差异大的设为黑(0),其余设为白(255)
mask = np.where(diff_np > 0, 0, 255).astype(np.uint8)
# 与结果图按位与(取黑色部分)
result_np = np.minimum(np.array(result), mask)
result = Image.fromarray(result_np)

# 保存结果
result.save("decoded.png")
print("✅ 已生成 decoded.png (白底黑码)")

输出

image-20251020215237690

然后在线网站DataMatrix提取flag

flag{see!!the-wind-of-miss-yooou-around-finally-blowwws-to-geek-game~~~}

二阶段提示:

image-20251024172251071

测了下ChatGPT白色背景没识别,可能要透明

夸克可以扫出来

MISC

人机大战

flag1

image-20251020204639945

1
flag{dont-laugh-you-try-you-also-cant-beat-the-second-level}

RE

团结引擎

flag13可以通过patch dll来,encodeText种有个揭秘字符串的操作,直接patch右键添加这一个日志操作,打印出来,编译然后左上角生成dll

image-20251018150745803

替换原先的,运行dll就行了

image-20251018150840562

  • flag{gam4_ed2tor_pro}
  • flag{T2me_M0GIC5him}

猜测也能通过飞天来找这两个flag

还有个没找到估计在墙里

flag1可以修改Unity.StarterAssets的ThirdPersonController

当然更简单的做法是直接让门升起来,我这么做只是好玩.

如door可以直接加start方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Door2
// Token: 0x06000162 RID: 354
private void Start()
{
this._openingTriggered = true;
if (this.countdownText)
{
this.countdownText.text = "Opening";
}
if (this._mountedObject)
{
this._mountedObject.localPosition = this._mountedTargetPos;
}
}

patch成飞天

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
using System;
using UnityEngine;
using UnityEngine.InputSystem;

namespace StarterAssets
{
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(PlayerInput))]
public class ThirdPersonController : MonoBehaviour
{
private bool IsCurrentDeviceMouse
{
get { return this._playerInput.currentControlScheme == "KeyboardMouse"; }
}

private void Awake()
{
if (this._mainCamera == null)
{
this._mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
}
}

private void Start()
{
this._cinemachineTargetYaw = this.CinemachineCameraTarget.transform.rotation.eulerAngles.y;
this._hasAnimator = base.TryGetComponent(out this._animator);
this._controller = base.GetComponent<CharacterController>();
this._input = base.GetComponent<StarterAssetsInputs>();
this._playerInput = base.GetComponent<PlayerInput>();
this.AssignAnimationIDs();
this._jumpTimeoutDelta = this.JumpTimeout;
this._fallTimeoutDelta = this.FallTimeout;

Debug.Log("[CHEAT] ThirdPersonController loaded");
}

private void Update()
{
this._hasAnimator = base.TryGetComponent(out this._animator);
this.JumpAndGravity();
this.GroundedCheck();
this.Move();
}

private void LateUpdate()
{
this.CameraRotation();
}

private void AssignAnimationIDs()
{
this._animIDSpeed = Animator.StringToHash("Speed");
this._animIDGrounded = Animator.StringToHash("Grounded");
this._animIDJump = Animator.StringToHash("Jump");
this._animIDFreeFall = Animator.StringToHash("FreeFall");
this._animIDMotionSpeed = Animator.StringToHash("MotionSpeed");
}

private void GroundedCheck()
{
Vector3 origin = new Vector3(transform.position.x, transform.position.y - this.GroundedOffset, transform.position.z);
this.Grounded = Physics.CheckSphere(origin, this.GroundedRadius, this.GroundLayers, QueryTriggerInteraction.Ignore);
if (this._hasAnimator)
{
this._animator.SetBool(this._animIDGrounded, this.Grounded);
}
}

private void CameraRotation()
{
if (this._input.look.sqrMagnitude >= 0.01f && !this.LockCameraPosition)
{
float multiplier = this.IsCurrentDeviceMouse ? 1f : Time.deltaTime;
this._cinemachineTargetYaw += this._input.look.x * multiplier;
this._cinemachineTargetPitch += this._input.look.y * multiplier;
}
this._cinemachineTargetYaw = ThirdPersonController.ClampAngle(this._cinemachineTargetYaw, float.MinValue, float.MaxValue);
this._cinemachineTargetPitch = ThirdPersonController.ClampAngle(this._cinemachineTargetPitch, this.BottomClamp, this.TopClamp);
this.CinemachineCameraTarget.transform.rotation = Quaternion.Euler(this._cinemachineTargetPitch + this.CameraAngleOverride, this._cinemachineTargetYaw, 0f);
}

private void Move()
{
float targetSpeed = this._input.sprint ? this.SprintSpeed : this.MoveSpeed;
if (this._input.move == Vector2.zero)
{
targetSpeed = 0f;
}

float currentHorizontalSpeed = new Vector3(this._controller.velocity.x, 0f, this._controller.velocity.z).magnitude;
float speedOffset = 0.1f;
float inputMagnitude = this._input.analogMovement ? this._input.move.magnitude : 1f;

if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
{
this._speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * this.SpeedChangeRate);
this._speed = Mathf.Round(this._speed * 1000f) / 1000f;
}
else
{
this._speed = targetSpeed;
}

this._animationBlend = Mathf.Lerp(this._animationBlend, targetSpeed, Time.deltaTime * this.SpeedChangeRate);
if (this._animationBlend < 0.01f)
{
this._animationBlend = 0f;
}

Vector3 inputDirection = new Vector3(this._input.move.x, 0f, this._input.move.y).normalized;
if (this._input.move != Vector2.zero)
{
this._targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + this._mainCamera.transform.eulerAngles.y;
float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, this._targetRotation, ref this._rotationVelocity, this.RotationSmoothTime);
transform.rotation = Quaternion.Euler(0f, rotation, 0f);
}

Vector3 targetDirection = Quaternion.Euler(0f, this._targetRotation, 0f) * Vector3.forward;
Vector3 move = targetDirection.normalized * (this._speed * Time.deltaTime);
move += new Vector3(0f, this._verticalVelocity * Time.deltaTime, 0f);
this._controller.Move(move);

if (this._hasAnimator)
{
this._animator.SetFloat(this._animIDSpeed, this._animationBlend);
this._animator.SetFloat(this._animIDMotionSpeed, inputMagnitude);
}
}

private void JumpAndGravity()
{
const float flySpeed = 12f;

bool ascend = false;
bool descend = false;

Keyboard keyboard = Keyboard.current;
if (keyboard != null)
{
if (keyboard.spaceKey != null && keyboard.spaceKey.isPressed)
{
ascend = true;
}
if ((keyboard.leftCtrlKey != null && keyboard.leftCtrlKey.isPressed) ||
(keyboard.rightCtrlKey != null && keyboard.rightCtrlKey.isPressed))
{
descend = true;
}
}

if (ascend || descend)
{
this._verticalVelocity = ascend ? flySpeed : -flySpeed;

this._input.jump = false;
this._jumpTimeoutDelta = this.JumpTimeout;
this._fallTimeoutDelta = this.FallTimeout;

if (this._hasAnimator)
{
this._animator.SetBool(this._animIDJump, false);
this._animator.SetBool(this._animIDFreeFall, false);
this._animator.SetBool(this._animIDGrounded, false);
}

return;
}

if (this.Grounded)
{
this._fallTimeoutDelta = this.FallTimeout;
if (this._hasAnimator)
{
this._animator.SetBool(this._animIDJump, false);
this._animator.SetBool(this._animIDFreeFall, false);
}
if (this._verticalVelocity < 0f)
{
this._verticalVelocity = -2f;
}
if (this._input.jump && this._jumpTimeoutDelta <= 0f)
{
this._verticalVelocity = Mathf.Sqrt(this.JumpHeight * -2f * this.Gravity);
if (this._hasAnimator)
{
this._animator.SetBool(this._animIDJump, true);
}
}
if (this._jumpTimeoutDelta >= 0f)
{
this._jumpTimeoutDelta -= Time.deltaTime;
}
}
else
{
this._jumpTimeoutDelta = this.JumpTimeout;
if (this._fallTimeoutDelta >= 0f)
{
this._fallTimeoutDelta -= Time.deltaTime;
}
else if (this._hasAnimator)
{
this._animator.SetBool(this._animIDFreeFall, true);
}
this._input.jump = false;
}

if (this._verticalVelocity < this._terminalVelocity)
{
this._verticalVelocity += this.Gravity * Time.deltaTime;
}
}

private static float ClampAngle(float angle, float min, float max)
{
if (angle < -360f) angle += 360f;
if (angle > 360f) angle -= 360f;
return Mathf.Clamp(angle, min, max);
}

private void OnDrawGizmosSelected()
{
Gizmos.color = this.Grounded ? new Color(0f, 1f, 0f, 0.35f) : new Color(1f, 0f, 0f, 0.35f);
Gizmos.DrawSphere(new Vector3(transform.position.x, transform.position.y - this.GroundedOffset, transform.position.z), this.GroundedRadius);
}

private void OnFootstep(AnimationEvent animationEvent)
{
if (animationEvent.animatorClipInfo.weight > 0.5f && this.FootstepAudioClips.Length != 0)
{
int index = UnityEngine.Random.Range(0, this.FootstepAudioClips.Length);
AudioSource.PlayClipAtPoint(this.FootstepAudioClips[index], transform.TransformPoint(this._controller.center), this.FootstepAudioVolume);
}
}

private void OnLand(AnimationEvent animationEvent)
{
if (animationEvent.animatorClipInfo.weight > 0.5f)
{
AudioSource.PlayClipAtPoint(this.LandingAudioClip, transform.TransformPoint(this._controller.center), this.FootstepAudioVolume);
}
}

[Header("Player")] public float MoveSpeed = 2f;
public float SprintSpeed = 5.335f;
[Range(0f, 0.3f)] public float RotationSmoothTime = 0.12f;
public float SpeedChangeRate = 10f;
public AudioClip LandingAudioClip;
public AudioClip[] FootstepAudioClips;
[Range(0f, 1f)] public float FootstepAudioVolume = 0.5f;

[Space(10f)] public float JumpHeight = 1.2f;
[Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
public float Gravity = -15f;

[Space(10f)] public float JumpTimeout = 0.5f;
[Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
public float FallTimeout = 0.15f;

[Header("Player Grounded")] public bool Grounded = true;
public float GroundedOffset = -0.14f;
public float GroundedRadius = 0.28f;
public LayerMask GroundLayers;

[Header("Cinemachine")] public GameObject CinemachineCameraTarget;
public float TopClamp = 70f;
public float BottomClamp = -30f;
public float CameraAngleOverride;
public bool LockCameraPosition;

private float _cinemachineTargetYaw;
private float _cinemachineTargetPitch;
private float _speed;
private float _animationBlend;
private float _targetRotation;
private float _rotationVelocity;
private float _verticalVelocity;
private float _terminalVelocity = 53f;
private float _jumpTimeoutDelta;
private float _fallTimeoutDelta;
private int _animIDSpeed;
private int _animIDGrounded;
private int _animIDJump;
private int _animIDFreeFall;
private int _animIDMotionSpeed;
private PlayerInput _playerInput;
private Animator _animator;
private CharacterController _controller;
private StarterAssetsInputs _input;
private GameObject _mainCamera;
private const float _threshold = 0.01f;
private bool _hasAnimator;
}
}

image-20251018165235177

flag2:

image-20251018150628235

flag{v2ew_beh2nd_the_scene}

7 岁的毛毛:我要写 Javabinary-java

flag1

利用 java.beans.Expression 代替我们手动调用 Class.getDeclaredField,从而绕过 Blocker 对 Class.* 的拦截;真正的反射动作发生在 JDK 类内部,我们自己的字节码里不出现受限调用。

得到 Field 之后,就可以正常 setAccessible(true) 并读取实例上的 flag 值,最后打印即可

image-20251018212318114

1
2
3
4
5
6
7
8
9
10
11
12
import java.beans.Expression;
import java.lang.reflect.Field;

public class Solution {
public static void solve(Object obj) throws Exception {
Expression expr = new Expression(obj.getClass(), "getDeclaredField", new Object[]{"flag"});
Field field = (Field) expr.getValue();
field.setAccessible(true);
String flag = (String) field.get(obj);
System.out.println(flag);
}
}

flag2

image-20251018214124943

1
2
3
4
5
6
7
8
9
10
11
12
import java.beans.Expression;

public class Solution {
public static Object solve(Object ignored) throws Exception {
Expression expr = new Expression(System.class, "getenv", new Object[]{"FLAG2"});
Object value = expr.getValue();
if (value instanceof String flag && !flag.isEmpty()) {
return flag;
}
return "notflag{}";
}
}

枚举高手的 bomblab 审判

flag1

sub_16B0 自修改:调用 mprotect 将 loc_1550 区域改写可执行并基于地址 Xor 复原,再改回只读执行。静态分析时手动解密或运行到 mprotect 之后 dump 代码即可绕过

image-20251020220621812

1
0x42 - base + (uintptr_t)p = 0x42 - base + (base + i) = 0x42 + i。
1
2
3
4
5
6
7
8
9
import ida_bytes

base = 0x1550
length = 339

for i in range(length):
original = ida_bytes.get_wide_byte(base + i)
decoded = original ^ (0x42 + i)
ida_bytes.patch_byte(base + i, decoded)

image-20251020220947642

可以看到有个check_debugger

1
2
3
4
5
6
7
8
9
10
11
12
enc = [
0xB4, 0x20, 0x95, 0x44, 0x0C, 0x4E, 0x33, 0x07, 0x94, 0xFB,
0xFB, 0x70, 0x94, 0x1A, 0xD0, 0xA3, 0x0A, 0x5C, 0x42, 0x93,
0x38, 0xE0, 0x4F, 0x61, 0x15, 0x1A, 0x00, 0x51, 0x38, 0xC2,
0x7D, 0x1D, 0x6C, 0xD1, 0xF1, 0x22, 0x71, 0xDE, 0xCB, 0xD3,
0x2F, 0x3C, 0x8B, 0x9F, 0x61, 0x00, 0x00, 0x00, 0x4C, 0x4B,
0x14, 0x71, 0x7A, 0x64, 0x57, 0x57, 0x65, 0x5C, 0x7A, 0x14,
0x76, 0x7A, 0x56, 0x15, 0x7A, 0x60, 0x65, 0x56, 0x5C
]

for i in range(48,len(enc)):
print(chr(enc[i] ^ 0x25),end='')

in1T_Arr@y_1S_s0_E@sy

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
seed = bytes.fromhex("b42095440c4e330794fbfb70941ad0a30a5c429338e04f61151a005138c27d1d6cd1f12271decbd32f3c8b9f61")
key = b"in1T_Arr@y_1S_s0_E@sy"

flag = bytearray()
prev = seed[0]
for i in range(len(seed)):
t = ((prev ^ key[i % len(key)] ^ 0x3C) & 0xFF)
rot = ((t << ((i & 3) + 1)) & 0xFF) | (t >> (8 - ((i & 3) + 1)))
flag.append(rot ^ 0xA5)
if i + 1 < len(seed):
prev = seed[i + 1]

print(flag.decode())

flag{iN1t_arR@Y_w1Th_sMc_@NTI_dBg_1s_S0_e@Sy}

flag2

是个虚拟机逆向

check_flag2_vm 要求输入长度 0x27;它将 “sneaky_key\n” 拼在 flag 之前放入工作区,再执行一个字节码虚拟机。

字节码常量见 g_vm_bytecode:

  1. 先对 VM 内存地址 0x100、0x101 设为 0;
  2. RC4 初始化(opcode 0x40)使用 0x100 处 key 区:由 “sneaky_key\n” 和用户输入拼接而成;
  3. RC4 生成 keystream 写入 0x400…;

0x40有个RC4

image-20251020221928541

opcode 0x40 分支里,把一段缓冲区初始化为 S[i] = i,然后循环对 j 进行 j += S[i] + key[i % key_len],随即交换 S[i] 与 S[j]——这正是 RC4 的 KSA

0x41加密:

image-20251020222003866

常量区里了有“sneaky_key\n” 作为 key 前缀,与用户输入拼起来给 RC4

低字节类(v4 <= 0x21):

  • 0x00 — 终止并比较:执行 memcmp(&v52, &g_vm_target_state, 0x27)
  • 0x01 — push 一个 1 字节立即数(下一个字节)。v3 += 2v47[v2++ + 8] = imm.
  • 0x03 — push 一个 32 位常量(从字节流 + table 组合出来)。v3 += 5v47[v2++ + 8] = const32。这是把复杂常量放到栈上的方法。
  • 0x04 — pop(--v2)。
  • 0x05 — 复制顶元素:v47[v2 + 8] = v47[v2 + 7] 然后 v2++(把栈顶复制一份)。
  • 0x20 — 从 v47[528 + idx] 读取一个字节并 push(idx 由栈值决定)。如果 idx > 0x3FFF 则返回比较失败。
  • 0x21 — 把栈顶写入 v47[528 + idx](即写到 keystream/key 区)。如果 idx > 0x3FFF 则返回比较失败。

高字节类(v4 > 0x21):在代码里只出现两种

  • 0x40 — 初始化 S-box(类似 RC4 的 KSA):

    从栈弹出若干参数(偏移、key 长度、key 偏移等),在 v48[offset + 0..0xFF] 填入 0..255,然后根据 v47[528 + key_off ..] 的 key 做 256 次交换,最终将置换结果保存在 v48[offset ..]

  • 0x41 — 产生/混合 keystream(RC4 的 PRGA)

因此可以把 VM 中的 0x40 + 0x41 看成“用存在于 v47[528...] 的 key 初始化 RC4,然后用 RC4 生成一个 keystream并 XOR 到 v47[528 + ...] 的某些位置”。

可能生成对s盒进行了变换

只能动调了,把反调试的jz改成jnz后

image-20251024204002172

运行到这

image-20251024204035479

向上跟踪到s盒处,把S盒替换了就行

还有几种可能的思路

  1. 模拟运行
  2. 识别修改处
  3. 把密文patch进去,跑一遍直接看输出
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
content = [
0x1C, 0x5B, 0xE6, 0xC0, 0xE1, 0x1C, 0xC8, 0x9E, 0xD3, 0xB0,
0xB4, 0xE2, 0x87, 0x8C, 0x10, 0x99, 0x84, 0xBF, 0x88, 0xF5,
0x23, 0x40, 0x5B, 0x76, 0xBE, 0xA1, 0x9D, 0xEE, 0x3B, 0x90,
0x41, 0xD4, 0x42, 0x65, 0xEB, 0xD7, 0x61, 0xF5, 0xBF
]

rc4number = 256
flag = ''

def rc4_endecode(s, content, rc4number):
i = 0
j = 0
for k in range(len(content)):
i = (i + 1) % rc4number
j = (j + s[i]) % rc4number
s[i],s[j] = s[j],s[i]
t = (s[i] + s[j]) % rc4number
content[k] = chr(content[k] ^ s[t])
content = ''.join(content)
print(content)


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

flag{EAsy_vm_usINg_rC4_ALGo_1S_S0_e@SY}

又回去看了遍生成S盒没有变动,那我应该就是key搞错了,key是sneaky_key,而非’/nsneaky_key’,因为我发现生成S盒的%0xA而非0xB,所以猜测第一个不是密钥😂

image-20251025112011467

关于本题还有其他的反反调试手段这里记录下。程序读取的是这里的TraceID来实现检测

image-20251024212657134

我们把它hook掉就行

看TracePid说明当前进程已经被 PID 9467 跟踪 —— 的确存在调试/ptrace。

让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
// hide_tracer.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef FILE *(*fopen_t)(const char *, const char *);

static fopen_t real_fopen = NULL;

/* 生成一个假的 /proc/self/status 内容,其中保证包含 TracerPid: 0 */
static const char fake_status[] =
"Name:\tmyproc\n"
"State:\tR (running)\n"
"Pid:\t1234\n"
"PPid:\t1\n"
"TracerPid:\t0\n" /* <-- 关键:返回 0 表示未被调试 */
"Uid:\t1000\t1000\t1000\t1000\n"
"VmSize:\t0 kB\n"
;

/* 替换 fopen,只在 path == "/proc/self/status" 时返回内存 FILE* */
FILE *fopen(const char *path, const char *mode) {
if (!real_fopen) {
real_fopen = (fopen_t)dlsym(RTLD_NEXT, "fopen");
}
if (path && strcmp(path, "/proc/self/status") == 0) {
/* fmemopen 在 glibc 可用:把字符串当作 FILE* 返回 */
/* 注意:fmemopen 需要可写缓冲区,因此复制一份到 malloc 的区域 */
char *buf = malloc(sizeof(fake_status));
if (!buf) return NULL;
memcpy(buf, fake_status, sizeof(fake_status));
return fmemopen(buf, sizeof(fake_status) - 1, "r");
}
/* 其他文件按真实 fopen 处理 */
return real_fopen(path, mode);
}

这个最大的坑点是,远程调试需要用这个命令去设置环境变量启动调试器,在Parameters里设置没有用!!!:

1
2
gcc -shared -fPIC -o hide_tracer.so hide_tracer.c -ldl
LD_PRELOAD=/home/matriy/RE/TSCTF/hide_tracer.so ./linux_server64

这里还有个没注意到的点

test指令并不是跟网上说的那样用来比较的,而是一个位操作的指令

用于将寄存器 edx 与自身进行按位与操作。这其实是一个快捷方式,用来测试寄存器的内容是否为零

test 指令的执行结果会影响 CPU 的标志位,特别是 ZF(零标志位)。

如t

image-20251025200129278

若 edx 的值是零,test edx, edx 的结果也会是零,进而设置 ZF 标志。如果 edx 的值不是零,ZF 标志不会被设置。

jne 0040BCA3 仅在 ZF 未被设置时(即 edx 不为零时)执行跳转。如果 edx 是零,则跳

简单的来说就是,当运算的结果值为 0 时,ZF = 1

edx = 0 zf = 1 不执行跳转(因为zf = 1时je跳转 jne不跳)