这场比赛最大的感受大概是累……逆向算是一题一个语言架构,对于我来说是个不小的挑战。
因为学的比较多所以赛后整理了一下,stm32、uwp都比较偏门,资料也比较少,索性题目本身逻辑比较简单,入门ing…
D3MUG
u3d手游题
il2cppdumper跑一遍,导入符号后简单分析
按键回调事件:NoteObject__OnClicked
- 无效事件:
GameManager__NoteMissed
- 击中事件:
GameManager__NoteHit
地图加载:GameManager__loadBeatmap_d__8__MoveNext
读取地图数据:BeatScroller__ParseBeatTampoMap
- 生成点位:
BeatScroller__GenerateNote
assetstudio查看资源文件,有多张地图
但我们只加载了一张图:Beatmaps/ChromeVOX
结合之前地图解析函数,分析数据
hitpoints
- 格式:int,int (出现在第n列, 出现在n毫秒)
- 示例: 4,0
timepoints
- 格式:int: float
- 示例:0: 292.6829268292683
寻找到最后的check点ScoreScene__Start
ScoreScene
在MusicController
控制下加载
ScoreScene__get
调用了libd3mug3.so
中的get
GameManager__NoteHit
调用了GameManager__update
实际上调用的是libd3mug3.so
中的update
,看起来是修改过的MT19937
分析check逻辑
调用get
得到的数组,强转为string,开头为D3CTF
则成功
尝试hook update
能发现不管是hit还是missed,值都是0
分析调用链,其中的值来自于MusicController
大概是arm64不支持,所以没获取到时间,最后会显示arch is not support
查阅了下:Unity – Scripting API: Time.fixedDeltaTime (unity3d.com)
MonoBehaviour-FixedUpdate() – Unity 脚本 API (unity3d.com)
推测_CurrentTime_k__BackingField
就是音乐播放的时间
回到地图给的指示:Play this and AP to get FLAG.
踩时间点通关,利用hitpoints去跑一遍即可
const hitdata = ``; // beatmaps/chromevox/hitpoints const timeList = hitdata.split("\n").map((v, idx, arr) => { return parseInt(v.split(",")[1], 10); }); function awaitHook(){ var ptr = Module.findBaseAddress("libd3mug.so"); console.log(ptr); const update = new NativeFunction(ptr.add(0x0000780), "pointer", ["char"]); const instance = ptr.add(0x02D18); // running instance.writePointer(new NativePointer(0)); // init. for (const t of timeList) { update(t); } console.log(instance.readPointer().readCString()); } setImmediate(function(){ setTimeout(awaitHook,10); })
d3arm
特征对应项目
给了一个bin文件,先将bin转换为hex
BINARY to Motorola S-Record Converter Utility (keil.com)
srec_cat.exe 815b45cf84.bin -Binary -o test_hex_out.hex -intel -Output-Block-Size=16 -Disable_Sequence_Warnings
IDA强制arm little endian载入
根据ARM M的启动流程,计算得到基址0x8000000
和stack基址0x20010000
手动创建stack segment 0x20010000 ~ 0x20030000
调用链0x800951C -> 0x8009620 -> 0x80091A0
执行流程
check点
难度需要选择为hard
游戏逻辑
链表结构
struct list { _DWORD X; _DWORD Y; list *next; };
关注函数set_flag
和insertNode
可疑异或
异或值来源
思路:通关得到flag(
# -*- coding:utf-8 -*- """ @Author: Mas0n @File: d3dctf_1.py @Time: 2022/3/4 23:41 @Desc: It's all about getting better. """ check_arr = [0x00000020, 0x0000006D, 0x00000050, 0x00000030, 0x00000038, 0x00000048, 0x00000026, 0x00000067, 0x00000057, 0x0000007C, 0x0000006C, 0x00000005, 0x00000020, 0x0000003A, 0x00000052, 0x00000072, 0x0000003C, 0x0000000B, 0x00000025, 0x0000006D, 0x00000052, 0x00000074, 0x0000006A, 0x00000055, 0x00000070, 0x0000006A, 0x00000005, 0x00000076, 0x0000003A, 0x00000004, 0x0000007D, 0x0000006D, 0x00000007, 0x00000077, 0x0000006E, 0x00000057, 0x00000077, 0x0000006F, 0x00000004, 0x00000020, 0x00000038, 0x0000004E] flag = bytearray(b'\x00' * 42) xorkey = 68 flag[0] = xorkey ^ check_arr[0] for i in range(1, 42): v1 = i % 3 xorkey = (0x335E44 >> (8 * v1)) & 0xff flag[i] = xorkey ^ check_arr[i] print(flag)
d3hotel
u3d + wasm
命名风格挺像n1(狗头)
BuildWebGL.wasm.br
、BuildWebGL.data.br
解压后拿到BuildWebGL.wasm
、BuildWebGL.data
assetstudio解出global-metadata.dat
顺道看到了两个luac
extract出来unluac跑一遍
main.lua
local L0_1, L1_1, L2_1, L3_1, L4_1, L5_1, L6_1, L7_1, L8_1, L9_1, L10_1, L11_1 matrix = require("matrix") m = matrix(5, 5, 0) L2_1 = {} L2_1[1] = 6422944 L2_1[2] = -7719232 L2_1[3] = 41640292 L2_1[4] = -1428488 L2_1[5] = -36954388 L3_1 = {} L3_1[1] = 43676120 L3_1[2] = -26534136 L3_1[3] = -31608964 L3_1[4] = -20570796 L3_1[5] = 22753040 L4_1 = {} L5_1 = -6066184 L6_1 = 30440152 L7_1 = 5229916 L8_1 = -16857572 L9_1 = -16335464 L4_1[1] = L5_1 L4_1[2] = L6_1 L4_1[3] = L7_1 L4_1[4] = L8_1 L4_1[5] = L9_1 L5_1 = {} L6_1 = -8185648 L7_1 = -17254720 L8_1 = -22800152 L9_1 = -8484728 L10_1 = 44642816 L5_1[1] = L6_1 L5_1[2] = L7_1 L5_1[3] = L8_1 L5_1[4] = L9_1 L5_1[5] = L10_1 L6_1 = {} L6_1[1] = -35858512 L6_1[2] = 10913104 L6_1[3] = -4165844 L6_1[4] = 37696936 L6_1[5] = -10061980 L1_1 = {} L1_1[1] = L2_1 L1_1[2] = L3_1 L1_1[3] = L4_1 L1_1[4] = L5_1 L1_1[5] = L6_1 n = matrix(L1_1) function L0_1(A0_2) local L1_2, L2_2, L3_2, L4_2, L5_2, L6_2, L7_2, L8_2, L9_2, L10_2, L11_2, L12_2 L1_2 = #A0_2 if L1_2 ~= 25 then L1_2 = 0 return L1_2 end for i = 1, 5, 1 do for j = 1, 5, 1 do m[i][j] = string.byte(A0_2, (i - 1) * 5 + j) end end L4_2 = matrix.det(m) L2_2 = string.format("%.5f", L4_2) det = tonumber(L2_2) L1_2 = matrix(5, "I") * det L2_2 = m * n if L1_2 == L2_2 then return 1 else return 0 end end f = L0_1
matrix.lua
找了队里密码师傅解了下
n = [[6422944, -7719232, 41640292, -1428488, -36954388], [43676120, -26534136, -31608964, -20570796, 22753040], [-6066184, 30440152, 5229916, -16857572, -16335464], [-8185648, -17254720, -22800152, -8484728, 44642816], [-35858512, 10913104, -4165844, 37696936, -10061980]] n = matrix(ZZ,n) b = -2337879088*n^(-1) for i in b: for j in i: print(chr(j),end='') # d3ctf{W3ba5m_1s_Awe5oom3}
交不上,偷懒失败……
il2cppDumper跑一遍
{ "Address": 7581, "Name": "D3Checker$$Check", "Signature": "void D3Checker__Check (D3Checker_o* __this, const MethodInfo* method);", "TypeSignature": "vii" }
offset为0
void zh(int param0, int param1, int param2) { *(unsigned int*)(param0 * 4 + (int)&gvar_40000000)(param2, param1); }
var dynCall_vii = Module["dynCall_vii"] = function() { return (dynCall_vii = Module["dynCall_vii"] = Module["asm"]["zh"]).apply(null, arguments) }
webassembly tables确定函数位置,分析下
挺坑……
# -*- coding:utf-8 -*- """ @Author: Mas0n @File: d3ctf_2.py @Time: 2022/3/5 21:38 @Desc: It's all about getting better. """ fake = bytearray(b'd3ctf{W3ba5m_1s_Awe5oom3}') flag = fake.copy() flag[9] -= 33 print(flag)
D3Re
UWP逆向,分析了一部分,后面被队里师傅直接干完了。
.NET Native and Compilation – UWP applications | Microsoft Docs
dump了下内存
ce trace
check点0x011BA80
正确格式:d^3ctf{11111111-2222-2222-1111-333321111111}
字符串结构长这样
以下结合队里师傅的分析:
0x010C730
:-
0x010C190
:+
0x010EF90
:ModPow
0x010CB90
:*
0x010E370
:%
flag为16进制,转为16字节
将前8字节位移9得到data1
data1 = 0 for i in range(8): data1 = (data1 << 9) + flag[i]
第一部分check
m = 904559654629185507076703 n = 757726435240880506850652 # (flag_part * n) % m == 820856551661154796608770 flag_part = 820856551661154796608770*n^(-1) % m print(flag_part) _unpack = lambda v: bytearray([(v >> 9*(7-i)) & 0xff for i in range(8)]) print(_unpack(flag_part).hex()) # 5d799a23ed488340
-
分隔的每一段都需要逆序,得到前半段flag d^3ctf{239a795d-48ed-4083
而后就是EncryptStringToBytes_Aes
对整个flag加密
dump出key、iv、data,解密
from Crypto.Cipher import AES data = bytearray([0x8F, 0x7C, 0x12, 0x6B, 0x07, 0xD4, 0x98, 0x77, 0x3B, 0x5C, 0x62, 0x0B, 0xAC, 0x96, 0x13, 0x96]) key = b''.fromhex('C51A4B2841E627D47D72C34079BE1F6C') iv = b''.fromhex('9CA57A2B88214607345DD2A3A0591EFF') ctx = AES.new(key=key, mode=AES.MODE_CBC, IV=iv) flag = ctx.decrypt(data) print(flag.hex()) # 5d799a23ed48834090d3e7528ca0776b
拼起来得到d^3ctf{239a795d-48ed-4083-90d3-e7528ca0776b}
发表回复