第一届OpenHarmony CTF专题赛

本文最后更新于 2025年7月17日 下午

REVERSE

easyre

将 hap 文件解压,把 ets 目录下的 modules.abc 字节码文件丢入 abc-decompiler 反编译

image-20250714163817762

在 pages/Index 拿到密文 hint1,往下翻看到加密逻辑

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
public Object #~@1>@2*^2*#(Object functionObject, Object newTarget, Index this) {
i = "";
for (i2 = 0; (i2 < _lexenv_0_1_.hint1.length ? 1 : 0) != 0; i2 = tonumer(i2) + 1) {
obj = String.fromCharCode;
obj2 = _lexenv_0_1_.hint1;
i += obj(obj2.charCodeAt(i2) + _lexenv_0_1_.hint1.length);
}
ldlexvar = _lexenv_0_1_;
reverseStr = ldlexvar.reverseStr(i);
i3 = "";
for (i4 = 0; (i4 < _lexenv_0_1_.hint1.length ? 1 : 0) != 0; i4 = tonumer(i4) + 1) {
i3 += String.fromCharCode(reverseStr.charCodeAt(i4) - i4);
}
ldlexvar2 = _lexenv_0_1_;
reverseStr2 = ldlexvar2.reverseStr(i3);
obj3 = createobjectwithbuffer(["hint1", 0]);
obj3.hint1 = reverseStr2;
router = import { default as router } from "@ohos:router";
obj4 = router.pushUrl;
obj5 = createobjectwithbuffer(["url", "pages/Flag", "params", 0]);
obj5.params = obj3;
callthisN = obj4(obj5);
then = callthisN.then();
then.catch(#~@1>@2*^2**#);
return null;
}


public Object #~@1>#reverseStr(Object functionObject, Object newTarget, Index this, Object arg0) {
obj = "";
for (i = arg0.length - 1; (i >= 0 ? 1 : 0) != 0; i = tonumer(i) - 1) {
obj = (obj == true ? 1 : 0) + arg0[i];
}
return obj;
}

先将 hint1 Reverse,然后加 Length 再减 Index,最后再 Reverse,最终结果 finalHint 被赋值给 obj3.hint1,作为参数传给 pages/Flag

在 Flag 中发现 magic 的值,第二段 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
public Object #~@0=#Flag(Object functionObject, Object newTarget, Flag this, Object arg0, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
obj = arg3;
obj2 = arg4;
if ((0 == obj ? 1 : 0) != 0) {
obj = -1;
}
if ((0 == obj2 ? 1 : 0) != 0) {
obj2 = null;
}
obj3 = super(arg0, arg2, obj, arg5);
if (("function" == typeof(obj2) ? 1 : 0) != 0) {
obj3.paramsGenerator_ = obj2;
}
obj3.__message = ObservedPropertySimplePU("Click 1000000 times to get the flag", obj3, "message");
obj3.count = 0;
obj3.magic = "ODg0ZjMxNWYxMDJiMGI4ZGI1NjgwNWYzNGJkYzgxY2ZlYzI";
obj3.setInitiallyProvidedValue(arg1);
obj3.finalizeConstruction();
return obj3;
}

public Object #~@0>#getH2(Object functionObject, Object newTarget, Flag this, Object arg0) {
return import { decodeToString } from "@normalized:N&&&entry/src/main/ets/utils/Coder&"(arg0);
} // base解密



public Object #~@0>@1*^2*#(Object functionObject, Object newTarget, Flag this) {
ldlexvar = _lexenv_0_1_;
ldlexvar.count = tonumer(ldlexvar.count) + 1;
router = import { default as router } from "@ohos:router";
r14 = router.getParams().hint1;
if ((1000000 == _lexenv_0_1_.count ? 1 : 0) == 0) {
promptAction = import { default as promptAction } from "@ohos:promptAction";
obj = promptAction.showToast;
obj2 = createobjectwithbuffer(["message", 0, "duration", 2000]);
obj2.message = _lexenv_0_1_.count + "";
obj(obj2);
return null;
}
ldlexvar2 = _lexenv_0_1_;
getH2 = r14 + ldlexvar2.getH2(_lexenv_0_1_.magic);
promptAction2 = import { default as promptAction } from "@ohos:promptAction";
obj3 = promptAction2.showToast;
obj4 = createobjectwithbuffer(["message", 0, "duration", 2000]);
obj4.message = "The flag is flag{" + getH2 + "}";
obj3(obj4);
return null;
}

最后 flag = r14 + reverse(decode_magic) ,所以可以得出逆向脚本

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
import base64


def decode_hint1(cipher: str) -> str:
length = len(cipher)
# charCode + length,Revese
step1 = ''.join([chr(ord(c) + length) for c in cipher])
reverse1 = step1[::-1]
# charCode - index
step3 = ''.join([chr(ord(reverse1[i]) - i) for i in range(len(reverse1))])
# Reverse
decrypted = step3[::-1]
return decrypted


def decode_magic(magic):
padding = 4 - len(magic) % 4
if padding != 4:
magic += '=' * padding
decrypted = base64.b64decode(magic).decode()
# Reverse
decrypted = decrypted[::-1]
return decrypted


def get_flag():
hint1 = "opfj^_mgekc]iWccXbf"
magic = "ODg0ZjMxNWYxMDJiMGI4ZGI1NjgwNWYzNGJkYzgxY2ZlYzI"

decrypt_hint1 = decode_hint1(hint1)
decrypt_magic = decode_magic(magic)

flag = f"flag{{{decrypt_hint1}{decrypt_magic}}}"
return flag


if __name__ == "__main__":
print(get_flag())

拿到 flag:

1
flag{princetonuniversity2cefc18cdb43f50865bd8b0b201f513f488}

arkts

分析源码大概可以知道 enc 先 rc4 再 rsa 然后 base64

1
2
3
4
5
6
7
8
9
10
11
12
public Object #~@0>#enc(Object functionObject, Object newTarget, Index this, Object arg0) {
rsaEncrypt = this.rsaEncrypt(this.rc4Encrypt(this.secretKey, arg0));
objArr = [Object];
for (i = 0; (i < rsaEncrypt.length ? 1 : 0) != 0; i = tonumer(i) + 1) {
obj = this.customBase64;
obj2 = this.stringToUint8Array;
ldobjbyvalue = rsaEncrypt[i];
objArr[i] = obj(obj2(ldobjbyvalue.toString()));
}
return objArr;
}

可以从中提取到密文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Object #~@0=#Index(Object functionObject, Object newTarget, Index this, Object arg0, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
obj = arg3;
obj2 = arg4;
if ((0 == obj ? 1 : 0) != 0) {
obj = -1;
}
if ((0 == obj2 ? 1 : 0) != 0) {
obj2 = null;
}
obj3 = super(arg0, arg2, obj, arg5);
if (("function" == typeof(obj2) ? 1 : 0) != 0) {
obj3.paramsGenerator_ = obj2;
}
obj3.__inputText = ObservedPropertySimplePU("", obj3, "inputText");
obj3.__isDialogShow = ObservedPropertySimplePU(null, obj3, "isDialogShow");
obj3.__dialogMessage = ObservedPropertySimplePU("", obj3, "dialogMessage");
obj3.targetCipher = createarraywithbuffer(["ndG5nZa=", "nte3ndK=", "nJy2nJi=", "mtK0mJG=", "nde5mZK=", "mJK5nJK=", "ntaXnJu=", "ndG5nZa=", "mZC4mtC=", "nZa5mZe=", "nJC1nZi=", "mJK0ntq=", "mta4nta=", "mZm5nW==", "mZG0mJq=", "ntCZnZi=", "nJyYmJe=", "mJy5ntq=", "mtK0nJa=", "ndK2nJm=", "ndyXmJe=", "ntmWnZi=", "mJK5nJK=", "nZe0nq==", "ndaZmJu=", "ndqXndm=", "mtiWnda=", "nJy2nJi=", "ndqXndm=", "mtyZodq=", "mtK0mJG=", "ndy5ndu=", "ndiZndC=", "mZK3mJe=", "ndmYmZG=", "mJi0nte=", "ndK2nJm=", "mtK0nJa="]);
obj3.secretKey = "OHCTF2025";
obj3.__isInitialized = ObservedPropertySimplePU(null, obj3, "isInitialized");
obj3.setInitiallyProvidedValue(arg1);
obj3.finalizeConstruction();
return obj3;
}

key 为 OHCTF2026,将 OHCTF2025 覆盖了

1
2
3
4
5
6
7
8
9
public Object #~@0>#onPageShow(Object functionObject, Object newTarget, Index this) {
if (isfalse(istrue(this.isInitialized) == null ? 1 : 0) != null) {
return null;
}
this.secretKey = "OHCTF2026";
this.isInitialized = 1;
return null;
}

rc 4 的 s 盒进行了魔改,对 keystream 进行了相加而不是异或

rsa 加密中 mod 75067,指数为 7

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
public Object #~@0>#rc4Encrypt(Object functionObject, Object newTarget, Index this, Object arg0, Object arg1) {
objArr = [Object];
i = 0;
for (i2 = 0; (i2 < 256 ? 1 : 0) != 0; i2 = tonumer(i2) + 1) {
objArr.push(i2);
}
for (i3 = 0; (i3 < 256 ? 1 : 0) != 0; i3 = tonumer(i3) + 1) {
i = ((i + objArr[i]) + arg0.charCodeAt(i % arg0.length)) % 256;
ldobjbyvalue = objArr[i3];
objArr[i3] = objArr[i];
objArr[i] = ldobjbyvalue;
}
i4 = 0;
i5 = 0;
newobjrange = Uint8Array(arg1.length);
for (i6 = 0; (i6 < arg1.length ? 1 : 0) != 0; i6 = tonumer(i6) + 1) {
i4 = (i4 + 1) % 256;
i5 = (i5 + objArr[i4]) % 256;
ldobjbyvalue2 = objArr[i4];
objArr[i4] = objArr[i5];
objArr[i5] = ldobjbyvalue2;
newobjrange[i6] = (arg1.charCodeAt(i6) + objArr[(objArr[i4] + objArr[i5]) % 256]) % 256;
}
return newobjrange;
}

/* JADX WARN: Type inference failed for: r21v25, types: [int] */
public Object #~@0>#rsaEncrypt(Object functionObject, Object newTarget, Index this, Object arg0) {
objArr = [Object];
for (i = 0; (i < arg0.length ? 1 : 0) != 0; i = tonumer(i) + 1) {
objArr[i] = this.modPow(arg0[i], 7, 75067);
}
return objArr;
}

public Object #~@0>#customBase64(Object functionObject, Object newTarget, Index this, Object arg0) {
newobjrange = import { default as util } from "@ohos:util".Base64Helper();
encodeToStringSync = newobjrange.encodeToStringSync(arg0);
from = Array.from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/");
obj = "";
obj2 = getiterator(encodeToStringSync);
obj3 = obj2.next;
i = 0;
while (true) {
callthisN = obj3();
throw.ifnotobject(callthisN);
if (istrue(callthisN.done) != null) {
return obj;
}
r27 = callthisN.value;
try {
indexOf = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(r27);
r27 = ((-1) != indexOf ? 1 : 0);
obj = (obj == true ? 1 : 0) + (r27 != 0 ? from[indexOf] : "=");
} catch (ExceptionI0 unused) {
z = r27;
if (istrue(i) == null) {
i = 1;
obj4 = null;
r272 = hole;
try {
obj5 = obj2.return;
obj3 = obj5;
r272 = (0 == obj5 ? 1 : 0);
} catch (ExceptionI0 unused2) {
}
if (r272 == 0) {
obj4 = obj3();
throw(z);
throw.ifnotobject(obj4);
}
}
throw(z);
}
}
}

因此加密流程为:enc -> rc4 -> 每个元素 rsa -> 对每个结果 toString -> stringToUint8Array() -> base64 encode

可以写出逆向脚本:base64 -> rsa -> rc4

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
import base64
from gmpy2 import invert

STANDARD_TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'


def base64_decode_change_table(c, new_table):
new_c = [STANDARD_TABLE[new_table.index(ch)] if ch != '=' else ch for ch in c]
try:
return base64.b64decode(''.join(new_c)).decode()
except:
return base64.b64decode(''.join(new_c))


s = ["ndG5nZa=", "nte3ndK=", "nJy2nJi=", "mtK0mJG=", "nde5mZK=", "odqXmG==", "nJa1mJK=", "nZe0nq==", "ntK0nda=",
"mJK5nJK=", "nJiYndG=", "mZyYndC=", "nJy5", "mJqWodC=", "nZe4nJK=", "nJiXnJG=", "ndK2nJm=", "nJC3odu=", "mtiWnda=",
"ndK2nJm=", "nte4ma==", "ntuYodC=", "ndq0odK=", "nJiYndG=", "mJaYnZK=", "nJaZmq==", "mJK2ndu=", "mta5oti=",
"mJu3ntC=", "nZaZndm=", "mJeXodi=", "mtGXmti=", "mtqZnW==", "ndm1nW==", "nJm3ody=", "odG4na==", "nJy5", "mtK0nJa="]

target = [int(base64_decode_change_table(i, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/")) for i
in s]


def mod_pow(base, exp, mod):
result = 1
base %= mod
while exp:
if exp & 1:
result = (result * base) % mod
base = (base * base) % mod
exp >>= 1
return result


def rsa_decrypt_array(cipher_arr):
n = 75067
phi = 270 * 276
d = invert(7, phi)
return [mod_pow(c, d, n) for c in cipher_arr]


plaintext_bytes = rsa_decrypt_array(target)


def KSA(key):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[j] + key[j % len(key)]) % 256
S[i], S[j] = S[j], S[i]
return S


def PRGA(S):
i = j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
yield S[(S[i] + S[j]) % 256]


def RC4(key, text):
S = KSA(key)
keystream = PRGA(S)
return bytes((char - next(keystream)) & 0xff for char in text)


key = b"OHCTF2026"
print(RC4(key, plaintext_bytes))

拿到 flag

1
flag{43c70db57b7ddb1ae6adb20f9b87ebf2}

oh~baby

1
2
你了解鸿蒙的HDF和HCS吗?
hcs客户端在/vendor/

把 sh 转成 bat 脚本启动 qemu,有图形化,但是其实没啥用

image-20250715015358210

可以看命令行,大概了解了一下提示的 HDF 和 HCS

1
2
HDF 是鸿蒙的驱动框架
HCS(HDF Configuration Source)是 HDF 驱动框架的配置描述源码,内容以 Key-Value 为主要形式,和json格式差不多,HCS 主要分为属性(Attribute)和节点(Node)两种结构

/vendor/bin 有一个 chall

image-20250716154052180

运行 chall 后会和 chall_driver 驱动交互进行 AES 加密,输入长度必须是16的倍数

把 chall 文件挂载下来分析

用 mount 查看挂载点

image-20250716160616345

把 userdata.img 挂载到 WSL 中

1
2
mkdir -p /mnt/userdata
sudo mount -o loop '/mnt/f/CTF question/OpenHarmonyCTF/Reverse/oh~baby/images/userdata.img' /mnt/userdata

在 /mnt/userdata 拿到 chall

image-20250716163939589

main 函数中发现绑定的 chall_service

但是这个文件也没啥用,binwalk 看 bzImage,发现有一个压缩文件

image-20250715011828530

分析这个 ELF 文件,根据字符串定位到函数,找到了比较的地方

image-20250716185935811

密文值为 8BE07936ADBB8728D93BB1E0AB715353

找了很久都没找到加密函数,放弃了,烂尾了。。。

HardWare

A Mysterious Card

1
2
3
Few days ago, I found a card on the street. I trying to analysis the card, but failed to do so.
Can you try to retrieve the content for me? UwU
The content does not hold the word flag{}, please add the word flag{ and } between the content you found inside the card.

是一个 NFC 文件

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
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare Plus, Mifare DESFire, SLIX, ST25TB
Device type: Mifare Classic
# UID is common for all formats
UID: 6D 79 5F 63 61 72 64
# ISO14443-3A specific data
ATQA: 00 44
SAK: 08
# Mifare Classic specific data
Mifare Classic type: 1K
Data format version: 2
# Mifare Classic blocks, '??' means unknown data
Block 0: 6D 79 5F 63 61 72 64 08 44 00 FF FF FF FF FF FF
Block 1: C0 01 03 E1 00 00 00 00 00 00 00 00 00 00 00 00
Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3: A0 A1 A2 A3 A4 A5 78 77 88 C1 73 64 49 65 41 51
Block 4: 03 30 D1 01 2C 54 02 65 6E 6d 61 35 74 33 72 31
Block 5: 6e 67 5f 73 37 72 75 63 37 75 72 33 5f 30 66 5f
Block 6: 6d 31 66 61 72 33 5f 63 61 72 64 5f 70 58 4c 46
Block 7: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 8: 4f 74 46 4a 75 69 4b 43 6b 62 50 68 4e 50 7a 56
Block 9: FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 11: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 14: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 15: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 17: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 18: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 19: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 21: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 22: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 23: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 24: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 25: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 26: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 27: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 28: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 29: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 31: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 32: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 33: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 34: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 35: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 36: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 37: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 38: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 39: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 41: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 42: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 43: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 44: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 45: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 46: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 47: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 48: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 49: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 51: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 52: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 53: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 54: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 55: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 56: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 57: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 58: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 59: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51
Block 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 61: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 62: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 63: 72 76 69 4c 67 45 70 6a 69 4e 73 64 49 65 41 51

脚本转化为 ASCII 字符

脚本

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
import re

def hex_to_ascii(hex_line):
try:
bytes_data = bytes.fromhex(hex_line)
ascii_str = ''.join(chr(b) if 32 <= b <= 126 else '.' for b in bytes_data)
return ascii_str
except ValueError:
return ''

def parse_nfc_blocks(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()

ascii_blocks = {}
for line in lines:
match = re.match(r'Block (\d+): ([0-9A-Fa-f ]+)', line)
if match:
block_num = int(match.group(1))
hex_data = match.group(2).strip()
ascii_str = hex_to_ascii(hex_data)
ascii_blocks[block_num] = ascii_str

return ascii_blocks

def main():
input_file = 'Card.nfc'
blocks = parse_nfc_blocks(input_file)

for block_num in sorted(blocks):
print(f"Block {block_num:02d}: {blocks[block_num]}")

if __name__ == "__main__":
main()

每 4 个 block 一组,第 4-8 个 block中的明文就是 flag,第 7 个 block 没用

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
Block 00: my_card.D.......
Block 01: ................
Block 02: ................
Block 03: ......xw..sdIeAQ
Block 04: .0..,T.enma5t3r1
Block 05: ng_s7ruc7ur3_0f_
Block 06: m1far3_card_pXLF
Block 07: rviLgEpjiNsdIeAQ
Block 08: OtFJuiKCkbPhNPzV
Block 09: ................
Block 10: ................
Block 11: rviLgEpjiNsdIeAQ
Block 12: ................
Block 13: ................
Block 14: ................
Block 15: rviLgEpjiNsdIeAQ
Block 16: ................
Block 17: ................
Block 18: ................
Block 19: rviLgEpjiNsdIeAQ
Block 20: ................
Block 21: ................
Block 22: ................
Block 23: rviLgEpjiNsdIeAQ
Block 24: ................
Block 25: ................
Block 26: ................
Block 27: rviLgEpjiNsdIeAQ
Block 28: ................
Block 29: ................
Block 30: ................
Block 31: rviLgEpjiNsdIeAQ
Block 32: ................
Block 33: ................
Block 34: ................
Block 35: rviLgEpjiNsdIeAQ
Block 36: ................
Block 37: ................
Block 38: ................
Block 39: rviLgEpjiNsdIeAQ
Block 40: ................
Block 41: ................
Block 42: ................
Block 43: rviLgEpjiNsdIeAQ
Block 44: ................
Block 45: ................
Block 46: ................
Block 47: rviLgEpjiNsdIeAQ
Block 48: ................
Block 49: ................
Block 50: ................
Block 51: rviLgEpjiNsdIeAQ
Block 52: ................
Block 53: ................
Block 54: ................
Block 55: rviLgEpjiNsdIeAQ
Block 56: ................
Block 57: ................
Block 58: ................
Block 59: rviLgEpjiNsdIeAQ
Block 60: ................
Block 61: ................
Block 62: ................
Block 63: rviLgEpjiNsdIeAQ

因此拼接后flag是

1
flag{enma5t3r1ng_s7ruc7ur3_0f_m1far3_card_pXLFOtFJuiKCkbPhNPzV}

总结

第一次接触关于鸿蒙的,复现也还是学到了挺多东西

鸿蒙反编译工具: https://github.com/ohos-decompiler/abc-decompiler


第一届OpenHarmony CTF专题赛
http://example.com/2025/07/17/第一届OpenHarmony-CTF专题赛/
作者
butt3rf1y
发布于
2025年7月17日
许可协议