ByteCTF2024

news2024/12/16 6:11:54

wp参考:

2024 ByteCTF wp

2024 ByteCTF WP- Nepnep

ByteCTF 2024 writeup by Arr3stY0u

五冠王!ByteCTF 2024 初赛WriteUp By W&M

ByteCTF 2024 By W&M - W&M Team

ByteCTF Re WP - 吾爱破解 - 52pojie.cn

2024 ByteCTF - BediveRe_RE - 博客园 (cnblogs.com)

Rev

babyapk

DIE扫出来是一个flutter架构的apk,考虑用blutter来反编译

我之前在ubuntu上配置成功过,后来因为虚拟机坏了全删了。重新配置时kali、ubuntu

学习孤恒师傅在win上又装了一遍,参考下面的文章

[原创]《安卓逆向这档事》番外实战篇3-拨云见日之浅谈Flutter逆向-Android安全-看雪-安全社区|安全招聘|kanxue.com

出现上面的报错就是网络问题(全局代理)

cmake相关的报错就是缺少环境,一定要检查VS这个模块有没有安装

成功之后就快乐启动

得到输出

根据教程利用里面的文件可以恢复符号表,把addName的脚本跑一遍(我跑一遍之后符号表还是缺,结果又跑了一遍一样的脚本就好了,匪夷所思)

搜一下apk名字段可以找到一些相关的函数

babyapk_main__MyHomePageState::test_264c0c里藏着大量逻辑,不过需要修复一下函数结构。对识别成数据的部分,我们采取“先U再C再P”的原则,对代码反复调教,最终可以恢复出能看的伪代码

看到一些字符串匹配的函数名,猜测是对字符串开头的检测;35行45应该是字符串长度

我们用frida hook看看情况,一般的安卓模拟器跑不起来,没有arm的底层,推荐直接上真机

blutter提供了非常方便的hook脚本,只要改个地址就能hook

手机端启动frida-server

frida -U -f com.example.babyapk -l E:\Downloads\CTF\ByteCTF2024\babyapk_output\blutter_frida.js

注入后触发

可以看到是一个flag头检测

0x198d18的函数跑不出东西,0x198df8可以跑出上面的flag头

hook 264d88 则可以跑出flag尾部验证

所以flutter层只是个表面,对flag格式进行验证,加密部分还在rust部分

m3N4B5V6在librust_lib_babyapk.so里面有字符串

定位到这个函数里

这个函数里还有greet字段,wm的wp解释了这一点

sub_39B24里面翻,最终找到3AEE0看着像加密函数

这里的连接符验证,应该为flag的格式

最后的验证部分,一堆方程式,又是z3

from z3 import *
data = [0x1EE59, 0x22A, 0x1415, 0x40714, 0x13E0, 0x8B8, 0xFFFDCEA0, 0x313B,
        0x3D798, 0xFFFFFE6B, 0xC4E, 0x23884, 0x8D, 0x1DB4, 0xFFFC1328, 0x1EAC,
        0x43C64, 0x142B, 0xFFFFF622, 0x23941, 0xFFFFEF6D, 0x120C, 0xFFFBD30F,
        0x1EBE, 0x45158, 0xFFFFEF66, 0x1D3F, 0x4C46B, 0xFFFFF97A, 0x1BFD,
        0xFFFBA235, 0x1ED2
]
for i in range(4):
    s = Solver()
    v46, v47, v45, v44, v48, v49, v50, v51 = BitVecs("v46 v47 v45 v44 v48 v49 v50 v51", 8)
    s.add(And(48 <= v46, v46 <= 127))
    s.add(And(48 <= v47, v47 <= 127))
    s.add(And(48 <= v45, v45 <= 127))
    s.add(And(48 <= v44, v44 <= 127))
    s.add(And(48 <= v48, v48 <= 127))
    s.add(And(48 <= v49, v49 <= 127))
    s.add(And(48 <= v50, v50 <= 127))
    s.add(And(48 <= v51, v51 <= 127))
    s.add((v51 + v47 * v44 * v49 - (v46 + v50 + v45 * v48)) & 0xffffffff ==data[i * 8])
    s.add((v44 - v48 - v46 * v49 + v51 * v47 + v45 + v50) & 0xffffffff ==data[i * 8 + 1])
    s.add((v46 * v49 - (v48 + v51 * v47) + v45 + v50 * v44) & 0xffffffff ==data[i * 8 + 2])
    s.add((v47 + v48 * v46 - (v51 + v45) + v50 * v49 * v44) & 0xffffffff ==data[i * 8 + 3])
    s.add((v49 * v44 + v47 + v45 * v48 - (v50 + v51 * v46)) & 0xffffffff ==data[i * 8 + 4])
    s.add((v46 * v49 + v47 * v44 + v45 - (v50 + v48 * v51)) & 0xffffffff ==data[i * 8 + 5])
    s.add((v51 - v47 + v45 * v49 + v50 - v48 * v46 * v44) & 0xffffffff ==data[i * 8 + 6])
    s.add((v44 - v51 - (v47 + v49) + v48 * v46 + v50 * v45) & 0xffffffff ==data[i * 8 + 7])
    if s.check()==sat:
        print(s.model()[v46], end=",")
        print(s.model()[v47], end=",")
        print(s.model()[v45], end=",")
        print(s.model()[v44], end=",")
        print(s.model()[v48], end=",")
        print(s.model()[v49], end=",")
        print(s.model()[v50], end=",")
        print(s.model()[v51], end=",")
print()
byte = [51,50,101,55,53,48,99,56,102,98,50,49,52,53,54,50,97,102,50,50,57,55,51,102,98,53,49,55,54,98,57,99]
flag = ''
j = 0
for i in byte:
    flag += chr(i)
print(flag)

v45-v51八个变量,每个变量4字节,(4*8=32)+4(四个“-”)+9(“ByteCTF{}”)=45,可以推出四个连接符,但不知道加在哪儿

看了各个wp,可以通过经验猜测是GUID格式

GUID 的格式为“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,其中每个 x 是 0-9 或 a-f 范围内的一个十六进制数。例如:6F9619FF-8B86-D011-B42D-00C04FC964FF 即为有效的 GUID 值。

硬抠伪代码勉强能看出来一点,但是不太好看。不知有什么好方法


又研究了52的wp,感觉写的很好,提供了更多的思路

我们要充分利用blutter提供的资源

打开asm\babyapk\main.dart其实可以通过符号名猜测到很多逻辑

也是我们之前hook出来的数据

看到重要的m3N4B5V6函数,可以进行进一步定位

不过也没太多信息

不过IDA是能搜到simple的字符串的

顺着交叉引用就能找到加密函数位置

而且既然知道了加密函数,z3解完了不知道“-”加在哪里,也可以直接爆破其位置

#参考了rea1师傅的脚本!
from itertools import combinations
 
# 原始字符串
original_string = "32e750c8fb214562af22973fb5176b9c"
 
# 定义用于验证的 byte 数组(byte_18E46)
byte_18E46 = [ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
# 验证逻辑函数
def validate_hyphen_positions(input_str):
    byte = byte_18E46
    input_bytes = [ord(c) for c in input_str]
    
    # 模拟你提供的验证逻辑
    v2 = byte[input_bytes[0]]
    if v2 == 36:
        return False
    v3 = byte[input_bytes[v2]] + v2
    if v3 == 36:
        return False
    v4 = v3 + byte[input_bytes[v3]]
    if v4 == 36:
        return False
    v5 = v4 + byte[input_bytes[v4]]
    if v5 == 36:
        return False
    v6 = v5 + byte[input_bytes[v5]]
    if v6 == 36:
        return False
    v7 = v6 + byte[input_bytes[v6]]
    if v7 == 36:
        return False
    v8 = v7 + byte[input_bytes[v7]]
    if v8 == 36:
        return False
    v9 = v8 + byte[input_bytes[v8]]
    if v9 == 36:
        return False
    v10 = input_bytes[v9]
    if v10 != ord('-'):
        return False
    v12 = v9 + byte[input_bytes[v9]]
    if v12 == 36:
        return False
    v13 = v12 + byte[input_bytes[v12]]
    if v13 == 36:
        return False
    v14 = v13 + byte[input_bytes[v13]]
    if v14 == 36:
        return False
    v15 = v14 + byte[input_bytes[v14]]
    if v15 == 36:
        return False
    v16 = v15 + byte[input_bytes[v15]]
    if v16 == 36:
        return False
    v18 = input_bytes[v16]
    if v18 != ord('-'):
        return False
    v20 = v16 + byte[input_bytes[v16]]
    if v20 == 36:
        return False
    v21 = v20 + byte[input_bytes[v20]]
    if v21 == 36:
        return False
    v22 = v21 + byte[input_bytes[v21]]
    if v22 == 36:
        return False
    v23 = v22 + byte[input_bytes[v22]]
    if v23 == 36:
        return False
    v24 = v23 + byte[input_bytes[v23]]
    if v24 == 36:
        return False
    v25 = input_bytes[v24]
    if v25 != ord('-'):
        return False
    v27 = v24 + byte[input_bytes[v24]]
    if v27 == 36:
        return False
    v28 = v27 + byte[input_bytes[v27]]
    if v28 == 36:
        return False
    v29 = v28 + byte[input_bytes[v28]]
    if v29 == 36:
        return False
    v30 = v29 + byte[input_bytes[v29]]
    if v30 == 36:
        return False
    v31 = v30 + byte[input_bytes[v30]]
    if v31 == 36:
        return False
    v32 = input_bytes[v31]
    if v32 != ord('-'):
        return False
    
    return True
 
 
# 生成插入 '-' 的位置的组合
positions = list(combinations(range(len(original_string) + 1), 4))  # 选择 4 个插入位置
 
# 计数器
count = 0
valid_count = 0  # 成功验证的组合计数器
 
# 遍历所有组合
for pos in positions:
    temp_str = original_string
    # 插入时要注意位置的偏移,每次插入后,字符串长度增加
    for i, p in enumerate(pos):
        temp_str = temp_str[:p + i] + '-' + temp_str[p + i:]  # 插入 '-' 并调整位置索引
     
    # 验证插入的 '-' 是否符合条件
    if validate_hyphen_positions(temp_str):
        print(f"Valid combination: {temp_str}")
        valid_count += 1  # 计数通过验证的组合
     
    # 计数
    count += 1
 
# 打印总次数
print(f"Total combinations: {count}")
print(f"Total valid combinations: {valid_count}")
'''
Valid combination: 32e750c8-fb21-4562-af22-973fb5176b9c
Total combinations: 40920
Total valid combinations: 1
'''

ByteBuffer

010看到一堆点和边的字样,猜测是要画图

由于只给了个bin文件,没有太多的办法,只能对着结构硬猜,其实仔细观察是能发现一些端倪的

首先是Edge的部分

通过这几个字符的特征应该可以猜测,每个Edge #?字段的结构体数据应该是存在字符串前面的

用简单的脚本提取一下,可以发现,除了头尾的数据有点怪,中间部分的数据段都是很有规律的

结构体前部是一些很大的数字,01后面是两个四字节的整数,再往后是四字节的4,和四字节的9或者8

每个部分的含义看起来都不明所以,除了可以很勉强地看出最后一个数据段是字符串部分的长度

那先往下看一下dot的部分

结构也类似,不过既然是点,就可以猜测第2、3个四字节部分是x,y坐标。末尾的数据同上是字符串标识的长度

我们可以发现dot一共有120(0x78)个,而edge中这两个字段的值都不大于0x78

所以edge中这两个字段表示的是这条边两个端点的编号

至于前面的大数不知道什么意思,我们也不管了,上面那些数据足够我们画图

读取结构体数据的脚本如下,一些细节做了注释

# 参考了wm大佬的脚本,非常佩服wm的师傅!
import struct
import matplotlib.pyplot as plt

def u32(a):
    return struct.unpack('<I', a)[0] # 将一个字节序列解包为一个32位无符号整数(小端序)

with open(R"E:\Downloads\CTF\ByteCTF2024\ByteBuffer\ByteBuffer.bin", 'rb') as fp:
    buffer = fp.read()

begin_offset = 0x3AC

addr = begin_offset
edges = []
while addr <= 0x1203: #只读取Edge数据段倒数直到第二个结构体的内容
    #print(hex(addr))
    bts = bytes() # 创建空字节对象一枚
    id_length = u32(buffer[addr + 0x14:addr + 0x18]) #读取字符串长度部分
    #print(id_length)
    #不同的字符串长度 数据段末尾的空格长度都不一样
    if id_length == 9: #id段字符串长度为9时,数据末尾会空3个字节,其他的以此类推
        offset_in = 3
    elif id_length == 8:
        offset_in = 4
    elif id_length == 7:
        offset_in = 1
    
    bts += buffer[addr:addr + 0x14 + 4 + id_length + offset_in] #把一整段结构体保存为一个字节对象
    addr += 24 + id_length + offset_in #地址指针移动一个结构体的长度,到下一个结构体
    edges.append(bts)
    
bts = buffer[addr + 12:addr + 44] #特殊处理最后一个结构体,因为它的长度和前面的不一样
edges.append(bts)
addr += 0x2C
'''
for i in edges:
    print(len(i),i)
'''
#print(hex(addr))

dots = []
while addr <= 0x1f85: #读取到Dot数据段倒数第二个结构体
    #print(hex(addr))
    bts = bytes()
    bts += buffer[addr:addr + 16]
    id_length = u32(buffer[addr + 16:addr + 20])
    #print(id_length)
    if id_length == 8:
        offset_in = 4
    elif id_length == 7:
        offset_in = 1
    elif id_length == 6:
        offset_in = 2

    bts += buffer[addr + 0x10:addr + 0x10 + 4 + id_length + offset_in]
    addr += 0x14 + id_length + offset_in
    dots.append(bts)
#print(hex(addr))

bts = buffer[addr + 8:addr + 34] #最后一个结构体特殊处理
dots.append(bts)
'''
for i in dots:
    print(len(i),i)
'''
points = []
for i in dots:
    x_d = u32(i[4:8])
    y_d = u32(i[8:12]) 
    points.append((x_d, y_d)) #读取dot数据中的坐标值
    id3 = u32(i[12:16]) #结构体后面部分,其实没啥用,因为看不懂什么意思
    #print(x_d, y_d, id3, i[-8:])

points = points[:-2] + [points[-1], points[-2]] #读取的dot最后两个点数据弄反了,手动调整一下
#print(len(points))

for i in edges:
    p1 = u32(i[8:12])
    p2 = u32(i[12:16])
    print(p1, p2, id3, i[-8:]) #读取edge数据中每条边两个端点的编号
    p2p = [120-p1, 120-p2] #points列表中的数据是倒着存进去的,检索索引值时索引值也是反的
    x_coords = [points[i][0] for i in p2p] # 检索x坐标
    y_coords = [points[i][1] for i in p2p] # 检索y坐标
    plt.plot(x_coords, y_coords, '-o') 
all_x_coords, all_y_coords = zip(*points)
plt.scatter(all_x_coords, all_y_coords) #绘制
plt.gca().invert_yaxis() #翻转y轴
plt.show()

ByteKit

给了这些玩意儿

sh文件是启动qemu镜像的脚本,qcow2是镜像

bios.bin大概是固件

qemu启动脚本,打开一个虚拟机,用root可以登陆

有个getflag.sh脚本

是一个flag验证,似乎目标是$BYTECTF_OUTPUT_VAR_FILE也就是"/sys/firmware/efi/efivars/ByteCTFOut-93e91ed6-1a7a-46a1-b880-c5c281700ea2"

binwalk看一下里面是有efi文件的,不过GUID不是我们想要的,先试试

从bios.bin中提取文件有两种方法

  1. 7zip直接解压

然后可以找到Bytekit字样的可疑文件

  1. 使用uefi_retool工具

python uefi_retool.py get-images bin路径

有ByteCTFIn、KEY:的字符串,不过函数部分被ollvm混淆了

D810可以大致去掉混淆

然后去看ModuleEntryPoint函数

有几个可疑的点

65行的第二个地址,是一长串十六进制,应该是一个文件

83行出现了11位循环异或

key的地址看起来有点怪

观察加密的文件

出现了微妙的对应,应该是异或的结果。所以得到异或的密钥实际上是这一部分(2CE83EE0BC1A0956B9D994)

这个位置之后都是填充的BD,没啥用。脚本把文件解密导出来

异或后头部也出现了MZ的PE头

#参考wm佬的
from idaapi import *
startaddr=0x3A37
xoraddr=0x3A2C
size=0x6c4 #实际上密钥前面那个数表示的就是文件大小
for i in range(size):
    patch_byte(startaddr+i,get_byte(startaddr+i)^get_byte(xoraddr+i%11))
fp=open("E:\Downloads\CTF\ByteCTF2024\ByteKit\output","wb+")
fp.write(get_bytes(startaddr,size))
fp.close()

导出的文件再次拖入IDA

一个开头“KEY:”的验证

下面是异或加密和验证flag

异或相当于流密钥加密,解密只要把加密过程再实现一遍即可

enc = [0x4B, 0x27, 0x42, 0x55, 0x48, 0x6E, 0x41, 0x29, 0x1F, 0x5E, 0x04, 0x04, 0x6B, 0x3E, 0x57, 0x5F, 0x08, 0x07, 0x5F, 0x3A, 0x31, 0x17, 0x40, 0x30, 0x5F, 0x7A, 0x75, 0x67, 0x36, 0x36, 0x36, 0x36]
key = [0x0000000000000062, 0x0000000000000001, 0x000000000000000B, 0x0000000000000079, 
       0x0000000000000002, 0x0000000000000003, 0x0000000000000074, 0x0000000000000003, 
       0x0000000000000007, 0x0000000000000065, 0x0000000000000004, 0x000000000000000E, 
       0x0000000000000064, 0x0000000000000005, 0x000000000000000D, 0x0000000000000061, 
       0x0000000000000006, 0x000000000000000A, 0x000000000000006E, 0x0000000000000007, 
       0x000000000000000F, 0x0000000000000063, 0x0000000000000008, 0x000000000000000C, 
       0x0000000000000065, 0x0000000000000009, 0x000000000000000A, 0x0000000000000000]

for i in range(len(key)//3):
    v14 = key[i*3+1]
    v15 = v14 + key[i*3+2]
    while(v15 > v14):
        enc[v14] ^= key[i*3]
        v14 += 1

flag = ''    
for i in enc:
    flag += chr(i)
print(flag)
# KEY:By71d@nnc6_Wan77_y@0_zug6666

输回去就可以得到flag,但是识别有点问题(前面是notepad里一起粘贴进来的,后面是手打./getflag.sh 然后从python复制的key粘贴的,后面的识别不出来,纯手打也识别不出来),不知为啥

Mobile

极限逃脱

直接分析IPA,选择里面的ByteCTFDemo部分

有不少符号表

看到ViewController firsButtonClicked

void __cdecl -[ViewController firsButtonClicked:](ViewController *self, SEL a2, id a3)
{
  uint32_t rand; // w20
  UITextField *v5; // x21
  NSString *v6; // x22
  void *input; // x23
  UIAlertController *v8; // x20
  UIAlertAction *v9; // x21
  UITextField *v10; // x22
  UILabel *v11; // x22
  UIButton *v12; // x19

  rand = arc4random_uniform(0x1F4u);
  v5 = objc_retainAutoreleasedReturnValue(-[ViewController firstInput](self, "firstInput"));
  v6 = objc_retainAutoreleasedReturnValue(-[UITextField text](v5, "text"));
  input = -[NSString integerValue](v6, "integerValue");
  objc_release(v6);
  objc_release(v5);
  if ( input == rand )
  {
    v8 = objc_retainAutoreleasedReturnValue(
           +[UIAlertController alertControllerWithTitle:message:preferredStyle:](
             &OBJC_CLASS___UIAlertController,
             "alertControllerWithTitle:message:preferredStyle:",
             CFSTR("提示"),
             CFSTR("恭喜你拿到入场券"),
             1LL));
    v9 = objc_retainAutoreleasedReturnValue(
           +[UIAlertAction actionWithTitle:style:handler:](
             &OBJC_CLASS___UIAlertAction,
             "actionWithTitle:style:handler:",
             CFSTR("确定"),
             0LL,
             &__block_literal_global_58));
    -[UIAlertController addAction:](v8, "addAction:", v9);
    -[ViewController presentViewController:animated:completion:](
      self,
      "presentViewController:animated:completion:",
      v8,
      1LL,
      0LL);
    v10 = objc_retainAutoreleasedReturnValue(-[ViewController inputText](self, "inputText"));
    -[UITextField setHidden:](v10, "setHidden:", 0LL);
    objc_release(v10);
    v11 = objc_retainAutoreleasedReturnValue(-[ViewController noticeLabel](self, "noticeLabel"));
    -[UILabel setHidden:](v11, "setHidden:", 0LL);
    objc_release(v11);
    v12 = objc_retainAutoreleasedReturnValue(-[ViewController secondButton](self, "secondButton"));
    -[UIButton setHidden:](v12, "setHidden:", 0LL);
    objc_release(v12);
  }
  else
  {
    v8 = objc_retainAutoreleasedReturnValue(
           +[UIAlertController alertControllerWithTitle:message:preferredStyle:](
             &OBJC_CLASS___UIAlertController,
             "alertControllerWithTitle:message:preferredStyle:",
             CFSTR("提示"),
             CFSTR("咒语错误"),
             1LL));
    v9 = objc_retainAutoreleasedReturnValue(
           +[UIAlertAction actionWithTitle:style:handler:](
             &OBJC_CLASS___UIAlertAction,
             "actionWithTitle:style:handler:",
             CFSTR("确定"),
             0LL,
             &__block_literal_global_53));
    -[UIAlertController addAction:](v8, "addAction:", v9);
    -[ViewController presentViewController:animated:completion:](
      self,
      "presentViewController:animated:completion:",
      v8,
      1LL,
      0LL);
    NSLog(&stru_10000C408.isa);
  }
  objc_release(v9);
  objc_release(v8);
}

是要过一个随机数啥的

再看看ViewController secondButtonClicked(推荐使用ida9,反编译效果好)

这里有个正则,暗示了flag格式也是uuid的

flag格式验证

初始化一些数据,很恶心的一点是第一部分的数据用的实际上是fifth的数据,first部分的数据根本没用

接下来是加密部分

先是用初始化的数据拼了一个flag格式的字符串,然后算了个SHA256

之后每个部分分段进行替换

    CC_SHA256(v14, v15, md);
    v16 = objc_retainAutoreleasedReturnValue(+[NSMutableString string](&OBJC_CLASS___NSMutableString, "string"));
    for ( i = 0LL; i != 32; ++i )
      objc_msgSend(v16, "appendFormat:", CFSTR("%02x"), md[i]);
    v18 = objc_retainAutoreleasedReturnValue(objc_msgSend(v16, "substringWithRange:", 1LL, objc_msgSend(f1, "length")));
    v19 = objc_retainAutoreleasedReturnValue(objc_msgSend(v18, "stringByReplacingOccurrencesOfString:withString:", CFSTR("a"), CFSTR("b")));
    objc_release(v18);
    v20 = objc_retainAutoreleasedReturnValue(objc_msgSend(v16, "substringWithRange:", objc_msgSend(f1, "length") + 1, objc_msgSend(f2_, "length")));
    v38 = objc_retainAutoreleasedReturnValue(objc_msgSend(v20, "stringByReplacingOccurrencesOfString:withString:", CFSTR("b"), CFSTR("c")));
    objc_release(v20);
    v21 = objc_retainAutoreleasedReturnValue(objc_msgSend(v16, "substringWithRange:", objc_msgSend(f2_, "length") + 1, objc_msgSend(f3, "length")));
    v43 = objc_retainAutoreleasedReturnValue(objc_msgSend(v21, "stringByReplacingOccurrencesOfString:withString:", CFSTR("c"), CFSTR("d")));
    objc_release(v21);
    v22 = objc_retainAutoreleasedReturnValue(objc_msgSend(v16, "substringWithRange:", objc_msgSend(f3, "length") + 1, objc_msgSend(f4, "length")));
    v42 = objc_retainAutoreleasedReturnValue(objc_msgSend(v22, "stringByReplacingOccurrencesOfString:withString:", CFSTR("d"), CFSTR("e")));
    objc_release(v22);
    v23 = objc_retainAutoreleasedReturnValue(objc_msgSend(v16, "substringWithRange:", objc_msgSend(f4, "length") + 1, objc_msgSend(f5, "length")));
    v24 = objc_retainAutoreleasedReturnValue(objc_msgSend(v23, "stringByReplacingOccurrencesOfString:withString:", CFSTR("e"), CFSTR("f")));
    objc_release(v23);
    v25 = objc_retainAutoreleasedReturnValue(-[NSArray objectAtIndexedSubscript:](v10, "objectAtIndexedSubscript:", 0LL));

6c9838a3c6810bdb2633ed5910b8547c09a7a4c08bf69ae3a95c5c37f9e8f57e

enc = 'a67be199da4b-b092-bd3e-e777-a67be199da4b'
hash = '6c9838a3c6810bdb2633ed5910b8547c09a7a4c08bf69ae3a95c5c37f9e8f57e'
p1 = ''
for i in range(1,9):
    p1 += hash[i]
    p1 = p1.replace('a', 'b')
print(p1)

p2 = ''
for i in range(9,13):
    p2 += hash[i]
    p2 = p2.replace('b', 'c')
print(p2)

p3 = ''
for i in range(5,9):
    p3 += hash[i]
    p3 = p3.replace('c', 'd')
print(p3)

p4 = ''
for i in range(5,9):
    p4 += hash[i]
    p4 = p4.replace('d', 'e')
print(p4)

p5 = ''
for i in range(5,17):
    p5 += hash[i]
    p5 = p5.replace('e', 'f')
print(p5)

flag = 'ByteCTF{' + p1 + '-' + p2 + '-' + p3 + '-' + p4 + '-' + p5 + '}'
print(flag)
# ByteCTF{c9838b3c-6810-8a3d-8a3c-8a3c6810bdb2}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2260341.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Envoy 服务发现原理大揭秘与核心要点概述

1 Envoy动态配置介绍 动态资源&#xff0c;是指由envoy通过xDS协议发现所需要的各项配置的机制&#xff0c;相关的配置信息保存 于称之为管理服务器&#xff08;Management Server &#xff09;的主机上&#xff0c;经由xDS API向外暴露&#xff1b;下面是一个 纯动态资源的基…

转盘抽奖功能(附加代码)

写在开头 上期代码主要实现PC端电商网站商品放大效果&#xff0c;本期就来实现积分随机抽奖效果&#xff0c;开发久了很多功能都是通过框架组件库来完成&#xff0c;但是如果组件满足不了开发需求&#xff0c;还需要开发人员手动封装组件&#xff0c;专门出这样一期文章&#x…

【CSS in Depth 2 精译_075】12.2 Web 字体简介 + 12.3 谷歌字体的用法

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第四部分 视觉增强技术 ✔️【第 12 章 CSS 排版与间距】 ✔️ 12.1 间距设置 12.1.1 使用 em 还是 px12.1.2 对行高的深入思考12.1.3 行内元素的间距设置 12.2 Web 字体 ✔️12.3 谷歌字体 ✔️12.…

ARM嵌入式学习--第七天(GPT)

GPT -介绍 GPT有一个32位向上计数器&#xff0c;定时计数器值可以使用外部引脚上的事件捕获到寄存器中&#xff0c;捕获触发器可以被编程为上升沿和下降沿。GPT还可以在输出比较引脚上生成事件&#xff0c;并在定时器达到编程值时产生中断。GPT有一个12位预分频器&#xff0c;…

搭建Tomcat(一)---SocketServerSocket

目录 引入1 引入2--socket 流程 Socket&#xff08;应用程序之间的通讯保障&#xff09; 网卡(计算机之间的通讯保障) 端口 端口号 实例 client端 解析 server端 解析 相关方法 问题1&#xff1a;ServerSocket和Socket有什么关系&#xff1f; ServerSocket Soc…

SpringBoot快速使用

一些名词的碎碎念: 1> 俩种网络应用设计模式 C/S 客户端/服务器 B/S 浏览器/服务器 俩者对比: 2> 集群和分布式的概念 集群: 分布式: 例子: 一个公司有一个人身兼多职 集群: 招聘N个和上面这个人一样身兼多职 分布式: 招聘N个人,分担上面这个人的工作,进行工作的拆分. 工…

【含开题报告+文档+PPT+源码】基于SpringBoot的开放实验管理平台设计与实现

开题报告 设计开放实验管理平台的目的在于促进科学研究与教学的融合。传统实验室常常局限于特定地点和时间&#xff0c;而开放平台可以为学生、教师和研究人员提供一个便捷的交流与共享环境。通过在线平台&#xff0c;他们可以分享实验资源、交流经验&#xff0c;从而促进科学…

分布式 漏桶算法 总结

前言 相关系列 《分布式 & 目录》《分布式 & 漏桶算法 & 总结》《分布式 & 漏桶算法 & 问题》 概述 简介 LBA Leaky Bucket Algorithm 漏桶算法是一种流行于网络通信领域的流量控制/频率限制算法。漏桶算法的核心原理是通过一个概念上的“漏桶”来…

linux glances vs top

一、安装 apt-get install glances glances top显示效果&#xff1a;

CTF知识集-PHP特性

title: CTF知识集-PHP特性 写在开头可能会用到的提示 call_user_func 调用的函数可以不区分大小写preg_match过滤存在长度溢出&#xff0c;长度超过100w检测失效。str_repeat(‘show’,250000); 生成100w个字符preg_match是无法处理数组的&#xff0c;例如:preg_match( n u m…

Hadoop运行Mapreduce问题集锦——Ubuntu虚拟机配置

一、端口访问问题 问题描述 运行任务前一直重连。具体来说&#xff0c;错误发生在尝试从czs-virtual-machine虚拟机的127.0.1.1地址连接到同一台机器的8032端口时&#xff0c;连接被拒绝。 如下&#xff1a; 2024-11-17 23:05:45,800 INFO retry.RetryInvocationHandler: java…

【经验分享】搭建本地训练环境知识点及方法

最近忙于备考没关注&#xff0c;有次点进某小黄鱼发现首页出现了我的笔记还被人收费了 虽然我也卖了一些资源&#xff0c;但我以交流、交换为主&#xff0c;笔记都是免费给别人看的 由于当时刚刚接触写的并不成熟&#xff0c;为了避免更多人花没必要的钱&#xff0c;所以决定公…

流程引擎Activiti性能优化方案

流程引擎Activiti性能优化方案 基于关系型数据库层面优化 MySQL建表语句优化 Activiti在MySQL中创建默认字符集为utf8&#xff08;即utf8mb3&#xff09;格式&#xff0c;本文将默认字符集设置为utf8mb4&#xff0c;排序规则为utf8mb4_general_ci&#xff0c;并修改变量等类…

Unix 传奇 | 谁写了 Linux | Unix birthmark

注&#xff1a;本文为 “左耳听风”陈皓的 unix 相关文章合辑。 皓侠已走远&#xff0c;文章有点“年头”&#xff0c;但值得一阅。 文中部分超链已沉寂。 Unix 传奇 (上篇) 2010 年 04 月 09 日 陈皓 了解过去&#xff0c;我们才能知其然&#xff0c;更知所以然。总结过去…

TimerPickerDialog组件的用法

文章目录 1 概念介绍2 使用方法3 示例代码我们在上一章回中介绍了Snackbar Widget相关的内容,本章回中将介绍TimePickerDialog Widget.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在这里说的TimePickerDialog是一种弹出窗口,只不过窗口的内容固定显示为时间,它主…

大模型系列4--开源大模型本地部署到微调(WIP)

背景 一直想真正了解大模型对硬件资源的需求&#xff0c;于是准备详细看一篇视频&#xff0c;将核心要点总结记录下。本文内容参考视频&#xff1a;保姆级教程&#xff1a;6小时掌握开源大模型本地部署到微调&#xff0c;感谢up主 训练成本 训练 > 微调 > 推理训练GPT…

现代密码学总结(上篇)

现代密码学总结 &#xff08;v.1.0.0版本&#xff09;之后会更新内容 基本说明&#xff1a; ∙ \bullet ∙如果 A A A是随机算法&#xff0c; y ← A ( x ) y\leftarrow A(x) y←A(x)表示输入为 x x x ,通过均匀选择 的随机带运行 A A A,并且将输出赋给 y y y。 ∙ \bullet …

Python中opencv的一些函数及应用

Sobel 算子函数 功能&#xff1a; Sobel 算子用于计算图像的梯度&#xff08;变化率&#xff09;&#xff0c;常用于边缘检测。它通过对图像应用一个基于一阶导数的滤波器来强调图像中的边缘部分&#xff0c;特别是水平和垂直方向上的边缘。通过计算图像的梯度&#xff0c;可以…

【docker】springboot 服务提交至docker

准备docker &#xff08;不是docker hub或者harbor&#xff0c;就是可以运行docker run的服务&#xff09;&#xff0c;首先确保docker已经安装。 本文以linux下举例说明&#xff1a; systemctl stats docker ● docker.service - Docker Application Container EngineLoaded…

XDOJ 877 图的深度优先遍历

题目&#xff1a;图的深度优先遍历 问题描述 已知无向图的邻接矩阵&#xff0c;以该矩阵为基础&#xff0c;给出深度优先搜索遍历序列&#xff0c;并且给出该无向图的连通分量的个数。在遍历时&#xff0c;当有多个点可选时&#xff0c;优先选择编号小的顶点。&#xff08;即…