BlockCTF 2024 Reverse


BlockCTF 2024 Reverse

Nothin But Stringz

Someone sent me this as a test of friendship, but I can’t make heads or tails out of it. Can you help?

Download the nothin_but_stringz.c.o

(刚学到,感恩yuro!)

image-20241116151909757

image-20241116151741826

1
nothin_but_stringz.c.o: LLVM bitcode, wrapper

Judging by the file name, we strings the file:

image-20241116152044599

1
2
3
4
5
6
7
8
9
0$JY
P$v`
f$c0
fLg0
r2H #
(d<12B
SDK Versionwchar_sizePIC Leveluwtableframe-pointerApple clang version 15.0.0 (clang-1500.3.9.4)
A00 )`
.strflag.str.1mainprintf18.1.8arm64-apple-ios7.0.0nothin_but_stringz.c_main_printfL_.str_flagL_.str.1

Was not it. Doing some research online you can find to decompile the LLVM bitcode you need the llvm-dis. And then it would output a ll file:

学个新东西:

llvm-dis 是 LLVM 工具链中的一个工具,用于将二进制格式的 LLVM IR(Intermediate Representation,中间表示)文件(即 .bc 文件,bitcode 文件)反汇编成人类可读的 LLVM IR 文本格式(即 .ll 文件)。

将结果输出到标准输出:

1
llvm-dis nothin_but_stringz.c.o  -o -
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
┌──(kali㉿kali)-[~/Desktop]
└─$ llvm-dis nothin_but_stringz.c.o -o -
; ModuleID = 'nothin_but_stringz.c.o'
source_filename = "nothin_but_stringz.c"
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
target triple = "arm64-apple-ios7.0.0"

@.str = private unnamed_addr constant [40 x i8] c"flag{al1_th3_h0miez_l0v3_llvm_643e5f4a}\00", align 1
@flag = global ptr @.str, align 8
@.str.1 = private unnamed_addr constant [25 x i8] c"The flag begins with %c\0A\00",

; Function Attrs: noinline nounwind optnone ssp uwtable(sync)
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, ptr %1, align 4
%2 = load ptr, ptr @flag, align 8
%3 = getelementptr inbounds i8, ptr %2, i64 0
%4 = load volatile i8, ptr %3, align 1
%5 = sext i8 %4 to i32
%6 = call i32 (ptr, ...) @printf(ptr noundef @.str.1, i32 noundef %5)
ret i32 0
}

declare i32 @printf(ptr noundef, ...) #1

attributes #0 = { noinline nounwind optnone ssp uwtable(sync) "frame-pointer"="non-lal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "taple-a7" "target-features"="+aes,+crypto,+fp-armv8,+neon,+sha2,+v8a,+zcm,+zcz" }
attributes #1 = { "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protecze"="8" "target-cpu"="apple-a7" "target-features"="+aes,+crypto,+fp-armv8,+neon,+shazcz" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 14, i32 4]}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 8, !"PIC Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 1}
!4 = !{i32 7, !"frame-pointer", i32 1}
!5 = !{!"Apple clang version 15.0.0 (clang-1500.3.9.4)"}

flag: flag{al1_th3_h0miez_l0v3_llvm_643e5f4a}

Red Flags

I made a video game, its really hard!

linux.zip win.zip osx.zip

又是新东西:godot游戏,

Godot 的主要编程语言是 GDScript,同时也支持 C#C++ 和其他语言。对于初学者,推荐从 GDScript 开始;对于需要复杂功能或生态支持的项目,可以选择 C# 或其他绑定语言。

工具:recover project

https://github.com/bruvzg/gdsdecomp

保存到本地,发现flag.tscn:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func _process(delta):
if is_transitioning:
%FlagSprite.material.set_shader_parameter(\"state\", current_state)
if target_state:
current_state += 1.0/TRANSITION_TICKS
if current_state >= 1:
current_state = 1
is_transitioning = false
else:
current_state -= 1.0/TRANSITION_TICKS
if current_state <= 0:
current_state = 0
is_transitioning = false

flag.tscn全文:

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
[gd_scene load_steps=7 format=3]  # 声明场景的格式和加载步骤。

# 资源声明,加载一个着色器和一个纹理
[ext_resource type="Shader" path="res://flag.gdshader" id="1"] # 加载一个自定义着色器文件
[ext_resource type="Texture2D" uid="uid://c2m78fell0vq7" path="res://flag_enabled.png" id="2"] # 加载一个纹理文件

# 声明一个脚本,控制标志的行为
[sub_resource type="GDScript" id="GDScript_0la3o"]
script/source = """
extends StaticBody2D
class_name Flag

const TRANSITION_TICKS = 100 # 每次状态变更的过渡帧数
var target_state = true # 目标状态,初始为 true(标志的开启状态)
var is_transitioning = false # 是否正在进行过渡
@export var current_state = 1.0 # 当前状态,默认值为 1.0 (标志为开启)

# Called when the node enters the scene tree for the first time.
func _ready():
# 复制并设置材质的着色器参数,以便在运行时修改
%FlagSprite.material = %FlagSprite.material.duplicate()
%FlagSprite.material.set_shader_parameter("state", current_state)
pass # 替换为实际的函数体

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
# 如果标志正在过渡状态
if is_transitioning:
%FlagSprite.material.set_shader_parameter("state", current_state) # 更新着色器的状态参数
if target_state:
# 如果目标状态是开启,则逐渐增加 current_state
current_state += 1.0 / TRANSITION_TICKS
if current_state >= 1:
current_state = 1 # 最大为 1
is_transitioning = false # 过渡完成
else:
# 如果目标状态是关闭,则逐渐减小 current_state
current_state -= 1.0 / TRANSITION_TICKS
if current_state <= 0:
current_state = 0 # 最小为 0
is_transitioning = false # 过渡完成

# 当其他物体进入 Area2D 时切换标志的状态
func _on_area_2d_body_entered(body):
is_transitioning = true # 开始过渡
target_state = not target_state # 切换目标状态
pass
"""

# 定义一个着色器材质,并将其绑定到 FlagSprite
[sub_resource type="ShaderMaterial" id="ShaderMaterial_fvcap"]
shader = ExtResource("1") # 使用之前加载的着色器
shader_parameter/state = 0.0 # 初始状态为关闭(0.0)

# 另一个脚本,控制 Area2D 的行为
[sub_resource type="GDScript" id="GDScript_f80tv"]
script/source = """
extends Area2D

# Called when the node enters the scene tree for the first time.
func _ready():
pass # 替换为实际的函数体

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass # 替换为实际的函数体
"""

# 定义一个矩形形状作为碰撞体
[sub_resource type="RectangleShape2D" id="RectangleShape2D_l6lhs"]
size = Vector2(3933, 4005) # 设置矩形的大小

# 定义一个 StaticBody2D 节点,代表标志对象
[node name="Flag" type="StaticBody2D"]
script = SubResource("GDScript_0la3o") # 绑定之前定义的脚本

# 定义一个 Sprite2D 节点,作为标志的显示图像
[node name="FlagSprite" type="Sprite2D" parent="."]
unique_name_in_owner = true # 确保该 Sprite 在其父节点中唯一
material = SubResource("ShaderMaterial_fvcap") # 使用自定义材质
texture = ExtResource("2") # 使用之前加载的纹理

# 定义一个 Area2D 节点,用于检测进入该区域的物体
[node name="Area2D" type="Area2D" parent="."]
script = SubResource("GDScript_f80tv") # 绑定之前定义的脚本

# 定义一个 CollisionShape2D,绑定矩形碰撞形状到 Area2D
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
position = Vector2(22.5, -65.5) # 设置碰撞体的位置
shape = SubResource("RectangleShape2D_l6lhs") # 绑定矩形碰撞形状

# 连接 Area2D 的信号,当有物体进入时调用 Flag 的方法
[connection signal="body_entered" from="Area2D" to="." method="_on_area_2d_body_entered"]

和arena.tscn:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var flags
# Called when the node enters the scene tree for the first time.
func _ready():
flags = get_children().filter(func(child): return child.name.match(\"Flag_*\"))

func hex_byte_to_int(c):
if c >= 0x30 && c <= 0x39:
return c - 0x30
else:
return c - 0x37

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
var states = []
for flag in flags:
states.append(int(flag.target_state))
var flaggregate = \"\".join(states)
var sha = flaggregate.sha1_text().to_upper()
sha += flaggregate.md5_text().to_upper()
var chars = %FlagText.get_children()
for i in chars.size():
chars[i].target_x = hex_byte_to_int(sha.unicode_at(i * 2)) - 8
chars[i].target_y = hex_byte_to_int(sha.unicode_at((i * 2) + 1)) - 8

看**.;,;.** 团队关于这个题目的解法:

There are 1024 unique possible states since there are 10 “Flag_*” objects, so we can brute force all possible states and pick the one where the characters lie close together on the Y axis.

由于有 10 个 “Flag_*”对象,因此有 1024 种可能的状态,我们可以对所有可能的状态进行暴力破解,选出字符在 Y 轴上靠拢的状态。

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
from hashlib import sha1, md5

def hex_byte_to_int(c):
if c >= 0x30 and c <= 0x39:
return c - 0x30
else:
return c - 0x37

chars = []
arena = open('arena.tscn', 'r').read().split('\n')[151:]
for i in range(30):
chunk = arena[i*9:i*9+9]
x = float(chunk[1].split()[-1])
y = float(chunk[2].split()[-1])
text = eval(chunk[5].split()[-1])
# print(x, y, text)
chars.append((x, y, text))

S = 50
for i in range(2**10):
b = bin(i)[2:].zfill(10)
sha = sha1(b.encode()).hexdigest().upper().encode()
sha += md5(b.encode()).hexdigest().upper().encode()

X, Y = [], []
for i in range(30):
X.append(hex_byte_to_int(sha[i * 2]) - 8)
Y.append(hex_byte_to_int(sha[i * 2 + 1]) - 8)

chars_moved = []
for i, (x, y, text) in enumerate(chars):
chars_moved.append((x + X[i] * S, y + Y[i] * S, text))

chars_moved_y = [y for x, y, text in chars_moved]
if max(chars_moved_y) - min(chars_moved_y) < 100:
# print(b)
chars_moved.sort(key=lambda x: x[0]) # left to right
print(''.join([text for x, y, text in chars_moved]))

还有yuro和BMK的思路:

没太懂。。问问

An Elf on a Shelf

What’s going on here?

elf

“这是什么misc题放到rev中了吗”


文章作者: W3nL0u
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 W3nL0u !
  目录