ByteCTF2024

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}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Node.js教程入门第一课:环境安装

对于一个程序员来说&#xff0c;每学习一个新东西的时候&#xff0c;第一步基本上都是先进行环境的搭建&#xff01; 从本章节开始让我们开始探索Node.js的世界吧! 什么是Node.js? 那么什么是Node.js呢&#xff1f;简单的说Node.js 就是运行在服务端的 JavaScript JavaScript…

弧形导轨的变形因素有哪些?

随着弧形导轨的应用日渐普遍&#xff0c;在日常使用中总会遇到很多各种各样的问题&#xff0c;其中变形是最常见的问题&#xff0c;但通过采取正确的预防和解决措施&#xff0c;可以避免其对设备性能和精度造成的影响&#xff0c;以下是一些主要的变形原因&#xff1a; 1、负载…

SSL证书部署(linux-nginx)

一、SSL证书的作用 HTTP协议无法加密数据,数据传输可能产生泄露、篡改或钓鱼攻击等问题。 SSL证书部署到Web服务器后,可帮助Web服务器和网站间建立可信的HTTPS协议加密链接,为网站安全加锁,保证数据安全传输。 二、前提条件 1.已通过数字证书管理服务控制台签发证书。 …

MATLAB引用矩阵元素的几种方法

引用矩阵元素可以通过索引&#xff0c;也可以通过逻辑值 索引 通过引用元素在矩阵中的位置来提取元素&#xff0c;例如&#xff1a; - 逻辑值 通过某种逻辑运算来使得要提取的值变为逻辑 1 1 1&#xff0c;用 A ( ) A() A()提取即可&#xff0c; A A A为原矩阵的名称。 例如&…

一些浅显易懂的IP小定义

IP归属地&#xff08;也叫IP地址&#xff0c;IP属地&#xff09; 互联网协议地址&#xff0c;每个设备上的唯一的网络身份证明。用于确保网络数据能够精准传送到你的设备上。 基于IP数据云全球IP归属地解析&#xff0c;示例Python代码 curl -X POST https://route.showapi.co…

Jupyter Notebook 切换虚拟环境

方法 切换到需要添加到Jupyter Notebook中的虚拟环境&#xff0c;执行&#xff1a; python -m ipykernel install --name Jupyter Notebook中显示的虚拟环境名称如果遇到 [Errno 13] Permission denied: /usr/local/share/jupyter类似的权限问题&#xff0c;可能是没有对应的…

Blue Ocean 在Jenkins上创建Pipeline使用详解

BlueOcean是Jenkins的一个插件,它提供了一套可视化操作界面来帮助用户创建、编辑Pipeline任务。以下是对BlueOcean中Pipeline操作的详细解释: 一、安装与启动BlueOcean 安装:在Jenkins的“系统管理”->“插件管理”->“可选插件”中搜索“BlueOcean”,然后点击“Ins…

厦门凯酷全科技有限公司正规吗靠谱吗?

随着短视频和直播电商的迅猛发展&#xff0c;越来越多的企业开始将目光投向抖音这一平台。作为国内领先的短视频社交平台&#xff0c;抖音凭借其庞大的用户基础和强大的算法推荐系统&#xff0c;成为众多品牌拓展市场、提升销售的重要渠道。厦门凯酷全科技有限公司&#xff08;…

【安卓开发】【Android Studio】启动时报错“Unable to access Android SDK add-on list”

一、问题描述 在启动Android Studio时&#xff0c;软件报错&#xff1a;Unable to access Android SDK add-on list&#xff0c;报错截图如下&#xff1a; 二、原因及解决方法 初步推测是由于网络节点延迟&#xff0c;无法接入谷歌导致的。点击Cancel取消即可。

KALI安装操作及过程

以下是在计算机上安装 Kali Linux 的详细教程&#xff1a;&#xff08;通常我直接使用虚拟机&#xff09; 解压虚拟机安装包&#xff0c;直接在虚拟机中打开KALI &#xff08;将内存改为4GB&#xff09; 初始密码账号&#xff1a;kali 一、准备工作 下载 Kali Linux 镜像文件…

磁盘空间占用分析工具-wiztree【推荐】

磁盘空间占用分析工具-wiztree【推荐】 如果你遇到过磁盘空间占满、找大文件困难、线上服务器空间飙升等一系列磁盘的问题&#xff0c;并且需要分析文件夹占用空间&#xff0c;传统的方法就是一个一个去看&#xff0c;属实太费劲&#xff0c;效率太低。 而“WizTree”便可解决…

2024年12月11日Github流行趋势

项目名称&#xff1a;maigret 项目维护者&#xff1a;soxoj, kustermariocoding, dependabot, fen0s, cyb3rk0tik项目介绍&#xff1a;通过用户名从数千个站点收集个人档案信息的工具。项目star数&#xff1a;12,055项目fork数&#xff1a;870 项目名称&#xff1a;uv 项目维护…

智能家居WTR096-16S录放音芯片方案,实现语音播报提示及录音留言功能

前言&#xff1a; 在当今社会的高速运转之下&#xff0c;夜幕低垂之时&#xff0c;许多辛勤工作的父母尚未归家。对于肩负家庭责任的他们而言&#xff0c;确保孩童按时用餐与居家安全成为心头大事。此时&#xff0c;家居留言录音提示功能应运而生&#xff0c;恰似家中的一位无形…

基于IEEE 802.1Qci的时间敏感网络(TSN)主干架构安全分析及异常检测系统设计

中文标题&#xff1a;基于IEEE 802.1Qci的时间敏感网络&#xff08;TSN&#xff09;主干架构安全分析及异常检测系统设计 英文标题&#xff1a;Security Analysis of the TSN Backbone Architecture and Anomaly Detection System Design Based on IEEE 802.1Qci 作者信息&…

【专题】2024年中国新能源汽车用车研究报告汇总PDF洞察(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p38564 本年度&#xff0c;国家及地方政府持续发力&#xff0c;推出诸多政策组合拳&#xff0c;全力推动汽车产业向更高质量转型升级&#xff0c;积极鼓励消费升级&#xff0c;并大力推行以旧换新等惠民生、促发展举措。尤为引人注目…

ESP32-C3 入门笔记07: ESP-NOW动态绑定MAC地址. (ESP-IDF + VSCode)

ESP-NOW 简介 ESP-NOW [gitbuh] ESP-NOW 是一种由乐鑫公司定义的无连接 Wi-Fi 通信协议。在 ESP-NOW 中&#xff0c;应用程序数据被封装在各个供应商的动作帧中&#xff0c;然后在无连接的情况下&#xff0c;从一个 Wi-Fi 设备传输到另一个 Wi-Fi 设备。 CTR 与 CBC-MAC 协…

Elasticsearch02-安装7.x

零、文章目录 Elasticsearch02-安装7.x 1、Windows安装Elasticsearch &#xff08;1&#xff09;JDK安装 Elasticsearch是基于java开发的&#xff0c;所以需要安装JDK。我们安装的Elasticsearch版本是7.15&#xff0c;对应JDK至少1.8版本以上。也可以不安装jdk&#xff0c;…

微信小程序中 crypto-js 加解密全攻略

一、引言 在微信小程序开发中&#xff0c;数据的安全至关重要。加解密技术在保护用户数据和应用程序的安全性方面起着关键作用。小程序在与服务器进行数据交互时&#xff0c;面临着数据泄露、篡改等安全风险。为了确保用户信息的安全&#xff0c;选择合适的加解密算法变得尤为…

fiddler设置抓取https,还抓取不到https如何解决?

一、清楚 C:\Users\Admin\AppData\Roaming\Microsoft\Crypto\RSA 目录下所有文件&#xff08;首次安装fiddler请忽略&#xff09; 二、清除电脑上的根证书&#xff0c;WINR快捷键&#xff0c;输入&#xff1a;certmgr.msc&#xff0c; 然后回车&#xff0c;查找所有fiddler证书…

React 19 除了 RSC 等新功能,还优化了什么?

提示:记录工作中遇到的需求及解决办法 文章目录 前言01. ref 作为 prop02. Context 作为 provider03. refs 的清理函数04. useDeferredValue 的初始值05. 支持文档元数据06. 支持样式表07. 支持异步脚本08. 支持预加载资源09. 支持自定义元素总结前言 React 19 正式发布,新功…