Amateurs CTF 2023 逆向分析题解

WriteUp 9个月前 admin
330 0 0

Amateurs CTF 2023 逆向题解共10题。


volcano


ELF64

Inspired by recent “traumatic” events.

nc amt.rs 31010


在IDA中分析,要求输入3个数b,v,p满足

b== 18476 + 22890k
◆17 <= sum_number_of_1(binary(v)) <= 26
p> 1 &&p% 2 == 1


满足条件后再进行如下检测,其中N= 4919,函数已重命名。

v4 = cnt_digits(v);
if ( v4 == cnt_digits(b)
&& (v5 = sum_digits(v), v5 == sum_digits(b))
&& (v6 = fast_pow(N, v, p), v6 == fast_pow(N, b, p)) )


bv在十进制下位数相同

bv在十进制下各位和相同

◆4919 ^b= 4919 ^v(modp)


p= 4919可满足最后一个条件,按k= 1~100查询b的各位和与位数,构造得
b= 1254536 = 54 * 22890 + 18476,
v= 1048562 = 1111_1111_1111_1111_0010b
就可以过关。


amateursCTF{yep_th0se_l00k_th3_s4me_to_m3!_:clueless:}


headache


ELF64

Ugh… my head hurts…

Flag is amateursCTF{[a-zA-Z0-9_]+}


在IDA中分析,发现这个函数,感觉是用异或做的壳。


__int64 __fastcall sub_401290(_BYTE *s)
{
_DWORD *v2; // rsi

if ( (*s ^ s[25]) != 86 )
return 0LL;
v2 = &loc_4012A4;
do
*v2++ ^= 0xEA228DE6;
while ( *(v2 - 1) != 0xEA228DE6 );
return ((__int64 (*)(void))loc_4012A4)();
}


使用脚本还原后发现还有一层异或套壳,再解密发现还有一层,感到不太对劲。


观察了一下发现套壳的代码全部位于一段地址区间内,头部执行完一些逻辑之后跳转到尾部,尾部对头尾区间内的数据异或解密,再跳转到新解密的代码的头部,如此往复,计算了一下大概有百来层。


头部长这样(rdi是函数输入的第一个参数即我们的输入):

mov r15b, [rdi+19h]
xor r15b, [rdi]
cmp r15b, 56h
jz loc_404374
xor eax, eax
retn
loc_4012A4:
; 后面属于下一个区间


尾部长这样:

loc_404374:
mov eax, 0EA228DE6h
lea rsi, loc_4012A4
loc_404381:
xor [rsi], eax
add rsi, 4
cmp [rsi-4], eax
jnz short loc_404381
call loc_4012A4
retn


首先写IDA脚本还原这段地址区间的代码。


尾部的开头总是4个0x00标记上个区间的结尾,然后跟一个mov eax, __MAGIC

用脚本提取之后需要nop掉加密代码并把call换成jmp,便于我们F5。

这里有一点很坑,在begin和end小于128时,字节码由call变成了call near,短了4个字节。


patch.py


from ida_bytes import get_bytes, patch_bytes

def xor_patch(BEGIN, END, MAGIC):
buf = get_bytes(BEGIN, END - BEGIN)
proc = bytes(buf[i] ^ MAGIC[i % len(MAGIC)] for i in range(len(buf)))
patch_bytes(BEGIN, proc)
print(f"0x{BEGIN:x}: patched {END - BEGIN} bytes")

begin = 0x4012a4
end = 0x404394
while True:
patch_bytes(end - 8, b'xe9') # call to jmp
patch_bytes(end - 3, b'x90') # ret to nop
end -= 36
if end <= begin:
break

magic = get_bytes(end + 5, 4)
xor_patch(begin, end, magic)

patch_bytes(end, b'x90' * 28) # nop pad
begin += 24
if end - begin < 128:
begin -= 4
# 破坏了原函数的最后一个ret,要改回来才行啊@_@


使用F5观察函数,大概有400多行,且所有的要求全为三元组s[x] ^ s[y] = z


int __fastcall check(_BYTE *s)
{
if ( (*s ^ s[25]) != 86 )
return 0;
if ( (s[14] ^ s[45]) != 29 )
return 0;
if ( (s[33] ^ s[34]) != 5 )
return 0;
if ( (s[40] ^ s[52]) != 5 )
return 0;
if ( (s[12] ^ s[56]) != 5 )
return 0;
// 后面省略
}


建立图G


vertex(i): 代表某个s[i]

edge(i, j): 权重为s[i] ^ s[j]


其中vertex[0] = ‘a’已知,只需遍历图就可以得出整个字串。
当然首先要用正则提取一下x,y,z


solve.py


import re

pat1 = re.compile(r'((s[(d+)] ^ s[(d+)]) != (d+))')
pat2 = re.compile(r'(s[(d+)] != s[(d+)])')
flag = bytearray(b'amateurs' + b'x00' * 53)
graph = [{} for i in range(61)]

with open("src.c", "r") as fp:
src = fp.read()

a = pat1.findall(src)
for ai in a:
x, y, w = int(ai[0]), int(ai[1]), int(ai[2])
if y in graph[x]:
if graph[x][y] != w:
print(f"{x} {y} what??")
exit()
else:
continue
graph[x][y] = graph[y][x] = w
print(f"{x} <-{w}-> {y}")

b = pat2.findall(src)
for bi in b:
x, y = int(bi[0]), int(bi[1])
if y in graph[x]:
if graph[x][y] != w:
print(f"{x} {y} what??")
exit()
else:
continue
graph[x][y] = graph[y][x] = 0
print(f"{x} <-{0}-> {y}")

changed = True
while changed:
changed = False
for i in range(61):
if flag[i] != 0:
for j in graph[i].keys():
if flag[j] != 0:
continue
else:
flag[j] = flag[i] ^ graph[i][j]
changed = True

print(flag)


amateursCTF{i_h4v3_a_spli77ing_headache_1_r3qu1re_m04r_sl33p}


rusteze


ELF64

Get rid of all your Rust rust with this brand new Rust-eze™ de-ruster.

Flag is amateursCTF{[a-zA-Z0-9_]+}


Rust编写的程序,先将输入和既定数组异或,再循环左移2位,最后和另一个既定数组比较。


for ( i = 0LL; i < 38; ++i )
{
v15 = e1[i] ^ input_str[i];
v23 = __ROL1__(v15, 2);
v15 = v23;
s[i] = v23;
}


solve.py


e1 = [39, 151, 87, 225, 169, 117, 102, 62, 27, 99, -29, -96, 5, 115, 89, -5, 10, 67, -113, -
32, -70, -64, 84, -103, 6, -65, -97, 47, -60, -86, -90, 116, 30, -35, -105, 34, -19, -59]

e2 = [25, -21, -40, 86, 51, 0, 80, 53, 97, -36, -106, 111, -75, 13, -92, 122, 85, -24, -2,
86, -105, -34, -99, -81, -44, 71, -81, -63, - 62, 106, 90, -84, -79, -94, -118, 89, 82, -30]

def ror2(x):
x = x & 255
y = x & 3
return ((y << 6) + (x >> 2)) & 255

flag = "".join(chr(ror2(y) ^ (x & 255)) for x, y in zip(e1, e2))
print(flag)


amateursCTF{h0pe_y0u_w3r3nt_t00_ru5ty}


rusteze 2


PE64

My boss said Linux binaries wouldn’t reach enough customers so I was forced to make a Windows version.

Flag is amateursCTF{[a-zA-Z0-9_]+}


Rust写的程序,但链接在Windows平台,可读性非常差。然而其逻辑基本与上题相同,按上题相同的解法可得字串’sorry_this_isnt_the_flag_this_time.’。
输入该字串,程序输出’Correct!’,但并没有flag。


观察到加密函数中有语句(byte_7FF6BCD60000为一串乱码):

v14 = e1[idx] ^ s[idx]
if ( idx < 30 )
byte_7FF6BCD60000[idx] ^= v14;


假定输入的是flag的开头’amateursCTF’,将byte_7FF6BCD60000, e1, s异或之后可得’sorry_this’。因此猜测如果输入上面得到的字串,该全局变量处就是flag。


debug得

amateursCTF{d0n3_4nd_deRust3d}


trick question


pyc

Which one do you hate more: decompiling pycs or reading Python bytecode disassembly? Just kidding that’s a trick question.

Run with Python version 3.10.

Flag is amateursCTF{[a-zA-Z0-9_]+}


先用pycdc嗦一下,发现是个套娃:

把一串b64加密的python代码提取出来再exec。

b64decode = lambda x: id.__self__.__dict__['exec'](id.__self__.__dict__['__import__']('base64').b64decode(x))
# 此处省略
b64decode('A'.join(x[::-1]))


这段代码解密如下,发现还是个套娃……

check = lambda:None
code = type(check.__code__)(1, 0, 0, 6, 5, 67, b'|x00dx00dx01x85x02x19x00dx02kx03rx0etx00jx01jx02dx03x19x00Sx00|x00dx04x19x00dx05kx03rx1atx00jx01jx02dx03x19x00Sx00|x00dx01dx04x85x02x19x00}x00tx00jx01jx02dx06x19x00|x00x83x01dx07kx03r0tx00jx01jx02dx03x19x00Sx00gx00}x01tx00jx01jx02dx08x19x00|x00x83x01Dx00]r\x02}x02}x03|x03dtkx02rG|x01xa0x03|x02xa1x01x01x00q:|x01gx00dnxa2x01kx03rTtx00jx01jx02dx03x19x00Sx00|x00xa0x04xa1x00xa0x05dx0bxa1x01}x00|x00dx0cx19x00dx00dx00dx04x85x03x19x00drkx03rltx00jx01jx02dx03x19x00Sx00|x00dx0ex19x00dx0cx19x00|x00dx0ex19x00dx0ex19x00x17x00|x00dx0ex19x00dx0fx19x00x18x00|x00dx0ex19x00dx0ex19x00|x00dx0ex19x00dx0fx19x00x17x00|x00dx0ex19x00dx0cx19x00x18x00|x00dx0ex19x00dx0fx19x00|x00dx0ex19x00dx0cx19x00x17x00|x00dx0ex19x00dx0ex19x00x18x00fx03dx10kx03rxa9tx00jx01jx02dx03x19x00Sx00tx00jx01jx02dx11x19x00dx12x83x01xa0x06|x00dx0fx19x00xa1x01xa0x07xa1x00dx13kx03rxc0tx00jx01jx02dx03x19x00Sx00tx00jx01jx02dx11x19x00dx14x83x01}x04|x04xa0x08|x00dx0fx19x00xa1x01x01x00tx00jx01jx02dx15x19x00|x00dx16x19x00x83x01|x00dx16<x00|x04xa0t|x00dx16x19x00xa1x01x01x00|x00dx16x19x00gx00dx17xa2x01kx03rxf0tx00jx01jx02dx03x19x00Sx00|x00dx18x19x00dx19x17x00dx1akx03rxfetx00jx01jx02dx03x19x00Sx00tx00jx01jx02dx1bx19x00xa0n|x00dx1cx19x00dx0cdx18x85x02x19x00dx1dxa1x02|x04xa0x0bdx0cdx1exa1x02Ax00dx1fkx03x90x01rx1dtx00jx01jx02dx03x19x00Sx00tx00jx01jx02dx1bx19x00xa0n|x00dx1cx19x00dx18d x85x02x19x00dx1dxa1x02|x04xa0x0bdx0cdx1exa1x02Ax00d!kx03x90x01r<tx00jx01jx02dx03x19x00Sx00tx00jx01jx02dx1bx19x00xa0n|x00dx1cx19x00d dx01x85x02x19x00d"x17x00dx1dxa1x02|x04xa0x0bdx0cdx1exa1x02Ax00d#kx03x90x01r]tx00jx01jx02dx03x19x00Sx00dx0c}x05|x00d$x19x00Dx00]x0b}x02|x05d%9x00}x05|x05|x027x00}x05x90x01qctx00jx01jx02d&x19x00|x05x83x01d'kx03x90x01rx80tx00jx01jx02dx03x19x00Sx00tx00jx01jx02d(x19x00Sx00', (None, 12, 'amateursCTF{', 'False', -1, '}', 'len', 42, 'enumerate', '_', (7, 11, 13, 20, 23, 35), b'_', 0, b'sn0h7YP', 1, 2, (160, 68, 34), '__import__', 'hashlib', '4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a', 'random', 'list', 3, (49, 89, 102, 109, 108, 52), 4, b'freebie', b'0ffreebie', 'int', 5, 'little', 4294967295, 4227810561, 8, 825199122, b'x00', 4277086886, 6, 128, 'hex', '0x29ee69af2f3', 'True', 'Did you know? pycdc can decompile marshaled code objects. Just make sure you mention the right version!'), ('id', '__self__', '__dict__', 'append', 'encode', 'split', 'sha256', 'hexdigest', 'seed', 'shuffle', 'from_bytes', 'randint'), ('input', 'underscores', 'i', 'x', 'r', 'c'), '', 'check', 3, b'x10x01x0cx01x0cx01x0cx01x0cx01x14x02x0cx01x04x02x18x01x08x01nx01x02x80x0cx01x0cx01x0ex02x16x01x0cx01nx03x0cx01"x02x0cx01x10x02x0ex01x18x01x0ex01x10x02x0cx01x10x02x0cx012x02x0cx012x02x0cx016x02x0cx01x04x02x0cx01x08x01x0cx01x16x02x0cx01x0cx02', (), ())

check = type(check)(code, {'id': id})
if check(input("Enter the flag: ")):
print("Correct!")
else:
print("Incorrect.")


用dis去读这个代码对象的python字节码,解密要求如下:


输入为’amateurs{FLAG}’,其中FLAG长度为42,由7段组成:0000000_111_2_333333_44_55555555555_666666


◆FLAG[0]是sn0h7YP的倒转 PY7h0ns

◆FLAG[1]是方程组,解得 ar3

◆FLAG[2]的hash值给定,枚举得 4

◆FLAG[3]由b’4’为种子shuffle,观察得 f4m1lY

◆FLAG[4]直接给出为 0f

◆FLAG[5]种子不变,产生随机数异或,解得 N0Nv3nom0us

◆FLAG[6]给定其作为128进制数的值,得 Sn4kes


amateursCTF{PY7h0ns_ar3_4_f4m1lY_0f_N0Nv3nom0us_Sn4kes}

蟒蛇是一种无毒蛇。


Data Structures and Algorithms


ELF64(Coredump)

I was doing some homework for my Data Structures and Algorithms class, but my program unexpectedly crashed when I entered in my flag. Could you help me get it back?

Here’s the coredump and the binary, I’ll even toss in the header file. Can’t give out the source code though, how do I know you won’t cheat off me?


使用IDA分析,建立结构体如下:(data为1字节数据,7字节padding)


00 listnode_t struc
00 data db 8 dup(?)
08 ptr dq ? ; offset
10 listnode_t ends

00 list_t struc
00 len dd ?
04 pad dd ?
08 head dq ? ; offset
10 list_t ends


阅读源程序可知,作者将输入字串提取成链表,但混淆数据时竟将指针异或导致段错误。


将coredump使用gdb调试,发现core格式错误无法调试。


考虑到其一定包含内存的镜像,直接用rehex打开寻找堆的位置。


具体方法是搜索后跟7个0x00字节的’a’字节,即b’x61x00x00x00x00x00x00x00’。

amateursCTF{l1nk3d_bY_4n_xor}


jvm


java

I heard my professor talking about some “Java Virtual Machine” and its weird gimmicks, so I took it upon myself to complete one. It wasn’t even that hard? I don’t know why he was complaining about it so much.

Compiled with openjdk 11.0.19.

Run with java JVM code.jvm.

jd-gui嗦一下,发现是用java搓的虚拟机。


◆位宽: 8

◆寄存器: 4

◆栈: 1024

◆字节码: 1指令 + 0~2数据

◆代码混淆:如果字节码无法识别,就对b,b1,b2解密

  • b = b0 ^ b10 ^ b20

  • b1 = b10

  • b2 = b0


Amateurs CTF 2023 逆向分析题解
Amateurs CTF 2023 逆向分析题解


disas.py


with open("code.jvm", "rb") as fp:
with open("asm.txt", "w") as fq:
program = bytearray(fp.read())
idx = 0
while idx < len(program):
b0 = program[idx]
b1 = program[idx + 1] if idx + 1 < len(program) else 0xff
b2 = program[idx + 2] if idx + 2 < len(program) else 0xff
if b0 == 0 or b0 == 1 or b0 == 2 or b0 == 3:
fq.write(f"{idx} swap r{b0} r{b1}n")
idx += 2
elif b0 == 8:
fq.write(f"{idx} add r{b1}, {b2}n")
idx += 3
elif b0 == 9:
fq.write(f"{idx} add r{b1}, r{b2}n")
idx += 3
elif b0 == 12:
fq.write(f"{idx} sub r{b1}, {b2}n")
idx += 3
elif b0 == 13:
fq.write(f"{idx} sub r{b1}, r{b2}n")
idx += 3
elif b0 == 16:
fq.write(f"{idx} mul r{b1}, {b2}n")
idx += 3
elif b0 == 17:
fq.write(f"{idx} mul r{b1}, r{b2}n")
idx += 3
elif b0 == 20:
fq.write(f"{idx} div r{b1}, {b2}n")
idx += 3
elif b0 == 21:
fq.write(f"{idx} div r{b1}, r{b2}n")
idx += 3
elif b0 == 24:
fq.write(f"{idx} mod r{b1}, {b2}n")
idx += 3
elif b0 == 25:
fq.write(f"{idx} mod r{b1}, r{b2}n")
idx += 3
elif b0 == 28:
fq.write(f"{idx} shl r{b1}, {b2}n")
idx += 3
elif b0 == 29:
fq.write(f"{idx} shl r{b1}, r{b2}n")
idx += 3
elif b0 == 31:
fq.write(f"{idx} read r{b1}n")
idx += 2
elif b0 == 32:
fq.write(f"{idx} rdpushn")
idx += 1
elif b0 == 33:
fq.write(f"{idx} write r{b1}n")
idx += 2
elif b0 == 34:
fq.write(f"{idx} wrpopn")
idx += 1
elif b0 == 41:
fq.write(f"{idx} jmp {b2} if !r{b1}n")
idx += 3
elif b0 == 42:
fq.write(f"{idx} jmp {b2} if r{b1}n")
idx += 3
elif b0 == 43:
fq.write(f"{idx} jmp {b1}n")
idx += 2
elif b0 == 52:
fq.write(f"{idx} push r{b1}n")
idx += 2
elif b0 == 53:
fq.write(f"{idx} pop r{b1}n")
idx += 2
elif b0 == 54:
fq.write(f"{idx} push {b1}n")
idx += 2
elif b0 == 127:
fq.write(f"{idx} quitn")
idx += 1
else:
if idx + 1 < len(program) and idx + 2 < len(program):
program[idx] = b0 ^ b1 ^ b2
program[idx + 1] = b1
program[idx + 2] = b0
else:
print("what??")
exit()


代码逻辑很简单,得

amateursCTF{wh4t_d0_yoU_m34n_j4v4_isnt_A_vm?}

JVM,指用Java做的VM。


flagchecker


scratch3.0

I was making this simple flag checker in Scratch, but my friend logged into my account and messed up all my variable names :(. Can you help me recover my flag please?

You should run on Turbowarp for better performance.


Turbowarp打开发现积木团在一起,变量名也混淆过。


Scratch的项目sb3其实是个zip,由资源文件和积木json文件压缩得到。

首先需要把变量名全部改好看点。


recover.py


import re
pat1 = re.compile(r"(DAT_694206942069420(0*))")

def rename(s):
return 'Name' + str(len(s.groups()[1]))

with open("./project.json", "r") as fp:
buf = re.sub(pat1, rename, fp.read())
with open("./recovered/project.json", "w") as fq:
fq.write(buf)


再上Turbowarp发现还是不太好读,但注意到一个积木中的常数0x9e3779b9,应该是TEA加密。


只要把某块积木拆开就可以下断点,debug了一下发现确实如此。


solve.py


dump = [239, 202, 230, 114, 17, 147, 199, 39, 182, 230, 119, 248, 78, 246, 224, 46, 99, 164, 112, 134, 30, 216, 53, 194, 60, 75, 223,
122, 67, 202, 207, 56, 16, 128, 216, 142, 248, 16, 27, 202, 119, 105, 158, 232, 251, 201, 158, 69, 242, 193, 90, 191, 63, 96, 38, 164]
key = [69420, 1412141, 1936419188, 1953260915]

# print(len(dump))
# 56

def decrypt(v, k):
total, delta = 0xc6ef3720, 0x9e3779b9
v0, v1 = int(v[0].hex(), 16), int(v[1].hex(), 16)
k0, k1, k2, k3 = k[0], k[1], k[2], k[3]
for i in range(32):
v1 = (v1 - (((v0 << 4) + k2) ^ (v0 + total)
^ ((v0 >> 5) + k3))) & 0xffffffff
v0 = (v0 - (((v1 << 4) + k0) ^ (v1 + total)
^ ((v1 >> 5) + k1))) & 0xffffffff
total = (total - delta) & 0xffffffff
return bytes.fromhex(hex(v0)[2:]) + bytes.fromhex(hex(v1)[2:])

flag = b''
for idx in range(0, len(dump), 8):
flag += decrypt([bytes(dump[idx:idx+4]), bytes(dump[idx+4:idx+8])], key)

print(flag)


amateursCTF{screw_scratch_llvm_we_code_by_hand_1a89c87b}

你说的对,但是Scratch是麻省理工学院的“终身幼儿园团队”在2007年发布的一种图形化编程工具。


emoji


emojicode

I apologize in advance.

Flag is amateursCTF{[a-z0-9_]+}

Compiled using the latest version of emojicode

Note that there may be multiple inputs that cause the program to print ✅. The correct input (and the flag) has sha256 hash 53cf379fa8fd802fd2f99b2aa395fe8b19b066ab5e2ff49e44633ce046c346c4.

emojicode,源码就特别抽象,需要对着官网的文档搜索查询。


过关要求:


输入的每个字符按二进制顺序排成新的字符串,
abcd->61626364->1100001_1100010_1100011_1100100
然后将其顺序填入16*16的棋盘中,不够用0补齐。

在每一行和每一列中,统计所有连续的’1’的块的长度记为列表,
1101101110中有3个连续块,长度为[2, 2, 3]

输入flag,使得每一行每一列的连续块长度列表恰为给定值。

爆破感觉很麻烦,因为开头是’amateursCTF’已经给定了,所以打算像扫雷一样玩一下。


最后长这样:


1100001110110111
0000111101001100
1011110101111001
0111001110000111
0101001000110111
1011110111110100
0110100111100111
0111111100010110
0101110110011011
1111011101100111
1110011101111111
0100111011101110
0111100011100100
1100101101111111
0110111010011101
0111000111111101


光是得到比特串还不够,因为flag可能是数字也可能是字母、符号(长度可能是6也可能是7),所以还是需要爆破(enc为得到的比特串)。


solve.py


# 此处省略
keys = '0123456789_abcdefghijklmnopqrstuvwxyz'

def crack(s, i):
if i == len(enc):
s = 'amateursCTF{' + s + '}'
if sha256(bytes(s, 'utf-8')).hexdigest() == sha256hex:
print(s)

if i + 6 <= len(enc):
x6 = chr(int(enc[i : i + 6], 2))
if x6 in keys:
crack(s + x6, i + 6)
if i + 7 <= len(enc):
x7 = chr(int(enc[i : i + 7], 2))
if x7 in keys:
crack(s + x7, i + 7)

crack('', 0)


amateursCTF{7his_belongs_ins1de_mi5c}

misc都没这个抽象……


jsrev


javascript/html

Someone wanted this, so I delivered. Have fun!

jsrev.amt.rs


用three.js写的玩球的游戏,阅读源码发现这样一段:

        

let positions = [];
await fetch('./coords.json')
.then((response) => response.json())
.then((data) => { positions = data; });

for (let i = 0; i < positions.length; i++) {
const [x, y, z] = positions[i];
spheres[i].collider.set(new THREE.Vector3(x, y, z), SPHERE_RADIUS);
}


爬取coords.json发现是三元组组成的列表,每个三元组代表一个初始球的坐标。

又观察到coords.json按y轴大小拍好了序,y轴相同的点排在了一块。

于是怀疑这些点排成了flag,并且从xOz看,y轴的一片就代表了一个字符。


用pyplot绘制散点图,注意xOz要给一点偏移,不然所有的点从上往下看全部团一块了看不清。


solve.py


import matplotlib.pyplot as pyplot
import numpy

coords = []
with open('coords.json', 'r') as fp:
coords = eval(fp.read())

x = numpy.array([(c[2]-(c[1]-34)*10) for c in coords])
y = numpy.array([-c[0] for c in coords])
z = numpy.array([0 for c in coords])

ax = pyplot.subplot(projection='3d')
ax.set_title('jsrev')
ax.scatter(x, y, z, c='b')

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')

pyplot.axis('scaled') # 很重要,比例保持原始,否则pyplot会主动变比例
pyplot.show()

Amateurs CTF 2023 逆向分析题解

amateursCTF{asK_4nD_thr33_5h4ll_r3c31v3}


完结撒花




Amateurs CTF 2023 逆向分析题解


看雪ID:狗敦子

https://bbs.kanxue.com/user-home-962418.htm

*本文为看雪论坛精华文章,由 狗敦子 原创,转载请注明来自看雪社区

Amateurs CTF 2023 逆向分析题解

# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复


Amateurs CTF 2023 逆向分析题解


Amateurs CTF 2023 逆向分析题解

球分享

Amateurs CTF 2023 逆向分析题解

球点赞

Amateurs CTF 2023 逆向分析题解

球在看

原文始发于微信公众号(看雪学苑):Amateurs CTF 2023 逆向分析题解

版权声明:admin 发表于 2023年8月1日 下午6:05。
转载请注明:Amateurs CTF 2023 逆向分析题解 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...