首页
社区
课程
招聘
[原创] DEFCON-2023-Quals Blackbox 好玩的黑盒虚拟机代码执行
发表于: 2024-2-29 20:43 3003

[原创] DEFCON-2023-Quals Blackbox 好玩的黑盒虚拟机代码执行

2024-2-29 20:43
3003

165: Blackbox

题目环境搭建

说明

源码是rust


修复

代码有问题,src\main.rs第377行改为println!("Executed too many instructions");


构建(rust x86_64-unknown-linux-gnu)

题目根目录执行cargo build

结果在target\debug\blackbox


部署

根据题目描述可知这题解题时不提供可执行文件,只有一个远程端口可以与程序交互

使用socat将题目绑定到10001端口

cd /home/yssf/ctf/blackbox/target/debug
socat tcp-listen:10001,fork exec:./blackbox,reuseaddr


初探

猜测开头输入格式

发送ascii格式字符串,"00"等数字,回显Program too large!

p.send('00')
r = p.recv()
print(r)
# b'Program too large!\n'

猜测格式不是ascii


猜输入\x01没有回显

p.sendline(b'\x01')
print(p.recv(timeout=1).decode())
# 无回显


直到`\x01\x00,回显一句"执行停止",后面的A、B、C、D、PC、SP应该是寄存器

p.sendline(b'\x01\x00')
print(p.recv(timeout=1).decode())
'''

Execution halted!
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0
Would you like to start over? (y/n)

'''


开头接收一个2字节长度的bin数据,看样子是程序长度


start over

这个发送y,start over就是重新输入一次

发送n就会结束会话

p.sendline(b'\x01\x00')
print(p.recv(timeout=1).decode())

p.sendline(b'y')
print(p.recv(timeout=1).decode())

p.sendline(b'\x01\x00')
print(p.recv(timeout=1).decode())
'''

Execution halted!
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0
Would you like to start over? (y/n)



Execution halted!
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0
Would you like to start over? (y/n)

'''


猜测开头输入内容及后续

继续测试开头输入的内容

p.sendline(b'\x01\x00') # 无回显
p.sendline(b'\x01\x00\x00') # 回显
p.sendline(b'\x02\x00\x00\x00') # 无回显
p.sendline(b'\x02\x00\x00\x00\x00') # 回显
p.sendline(b'\x03\x00\x00\x00\x00\x00') # 无回显
p.sendline(b'\x03\x00\x00\x00\x00\x00\x00') # 无回显


开头的格式,用c语言结构体可以表示为:

unsigned short len;
unsigned short[len] code;


这是一个虚拟机,接收机器码并执行

def send(sc):
   sc = int.to_bytes(len(sc) // 2, 2, 'little') + sc
   p.send(sc)
   f.write(p.recvuntil(b'Would you like to start over? (y/n)\n').decode())
   p.sendline(b'y')


探索指令1-初探与爆破

按小端序,爆破看看情况


单指令-爆第一字节

爆破脚本,爆第一个字节,0x00??(0x0000 ~ 0x00FF):

for i in range(0x100):
   sc = int.to_bytes(i, 1, 'little') + b'\x00'
   f.write('0x'+sc[::-1].hex().upper())
   send(sc)

爆破结果:

0x0000 ~ 0x0080
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0

0x0081 ~ 0x00FF
A: 1 B: 0 C: 0 D: 0: PC: 1 SP: 0
A: 2 B: 0 C: 0 D: 0: PC: 1 SP: 0
...
A: 127 B: 0 C: 0 D: 0: PC: 1 SP: 0

猜测:

0x0080 ~ 0x00FF是设置A为立即数,或加上一个立即数

汇编A = xA += x,对应机器码0x0080+x


测试两条0x0081,结果A=2,可以得出这个是A+=1而不是A=1`:

sc =  b'\x81\x00' + b'\x81\x00'
# A: 2 B: 0 C: 0 D: 0: PC: 2 SP: 0


同理爆破0x10??(0x0100~0x01FF)等的第一个字节

汇编和机器码对应关系:

x取值范围0~0xff
A += x  =>  0x0080 + x
B += x  =>  0x0180 + x
C += x  =>  0x0280 + x
D += x  =>  0x0380 + x


尝试爆破0x04??的第一个字节,发现似乎是无效指令

0x0400~0x04F
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0


单指令-爆第二字节

爆破第二个字节0x??81(0x0081~0xFF81)

for i in range(0x100):
   try:
       sc = b'\x81' + int.to_bytes(i, 1, 'little')
       f.write('0x'+sc[::-1].hex().upper())
       send(sc)
   except:
       p.close()
       p = remote(ip, port)
       f.write('\nexception\n')

爆破结果,:

0x0081 ~ 0x0381
A/B/C/D += 1

0x0481 ~ 0x0781
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0
似乎无效

0x0881 ~ 0x0981
PC/SP += 1

0x0A81 ~ 0x0F81
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0
似乎无效


0x1081 ~ 0x1381
A: 65535 B: 0 C: 0 D: 0: PC: 1 SP: 0
A: 0 B: 65535 C: 0 D: 0: PC: 1 SP: 0
A: 0 B: 0 C: 65535 D: 0: PC: 1 SP: 0
A: 0 B: 0 C: 0 D: 65535: PC: 1 SP: 0
A/B/C/D -= 1

0x1481 ~ 0x1781
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0
似乎无效

0x1881
没有回显,猜测是PC -= 1

0x1981
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 65535
SP -= 1

0x1A81 ~ 0x4F81
A: 0 B: 0 C: 0 D: 0: PC: 1 SP: 0
似乎无效


.....


整理1

故整理前面的爆破的部分结果得到:

x取值范围0~0xff
A += x  =>  0000 0000 1000 0000 + x
B += x  =>  0000 0001 1000 0000 + x
C += x  =>  0000 0010 1000 0000 + x
D += x  =>  0000 0011 1000 0000 + x
PC += x =>  0000 1000 1000 0000 + x
SP += x =>  0000 1001 1000 0000 + x

A -= x  =>  0001 0000 1000 0000 + x
B -= x  =>  0001 0001 1000 0000 + x
C -= x  =>  0001 0010 1000 0000 + x
D -= x  =>  0001 0011 1000 0000 + x
PC -= x =>  0001 1000 1000 0000 + x
SP -= x =>  0001 1001 1000 0000 + x

猜测指令集架构:

     0000 0000 1 000'0000
bits  ~12 11~8 7 6~0
desc op   ri   t imm

t(ype)=1:
imm: 无符号,0~127
ri:  0, A
    1, B
    2, C
    3, D
    4~7, unknown
    8, PC
    9, SP
    10~15, unknown
op:  0, reg[ri] += imm
    1, reg[ri] -= imm
    2~15, unknown

t(ype)=0: unknown

指令组合器:

def combine(op, ri, ty, imm):
   sc = (imm) & 0x7f
   sc |= (ty << 7) & 1
   sc |= (ri << 8) & 0xf
   sc |= (op << 12) & 0xf
   return int.to_bytes(sc, 2, 'little')


探索指令2-找type=1, 其余op的语义

type=1,爆破op

整理1中,op 2~15的操作未知,猜测是与或非等运算,测试一下

填充一下寄存器,然后爆破op:

for i in range(16):
   try:
       sc = b''
       # 填充ABCD和SP=(88, 115, 53, 46, 77)
       sc += combine(op=0, ri=0, ty=1, imm=0x58)
       sc += combine(op=0, ri=1, ty=1, imm=0x73)
       sc += combine(op=0, ri=2, ty=1, imm=0x35)
       sc += combine(op=0, ri=3, ty=1, imm=0x2e)
       sc += combine(op=0, ri=9, ty=1, imm=0x4d)
       # ri=0(A), ty=1, imm=0x66=102, 爆破op
       sc += combine(op=i, ri=0, ty=1, imm=0x66)
       f.write('op={} '.format(i)+'0x'+sc[::-1].hex().upper())
       send(sc, detail=True)
   except BaseException as e:
       p.close()
       p = remote(ip, port)
       f.write('\nException type={}\n'.format(type(e)))

结果,0~7是加减与或非等一元二元运算(2、3还不是很确定作用),15是syscall(imm),8~14不太确定:

ABCD SP=(88, 115, 53, 46, 77) imm=102
>>> 88+102
190
>>> 88-102+0x10000
65522
>>> 88&102
64
>>> 88|102
126
>>> (~88)+0x10000
65447
>>> 88^102
62

op=0 0x00E609CD03AE02B501F300D8
A: 190 B: 115 C: 53 D: 46: PC: 6 SP: 77
A += imm

op=1 0x10E609CD03AE02B501F300D8
A: 65522 B: 115 C: 53 D: 46: PC: 6 SP: 77
A -= imm

op=2 0x20E609CD03AE02B501F300D8
A: 0 B: 115 C: 53 D: 46: PC: 6 SP: 77
A=0?

op=3 0x30E609CD03AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 77
A没变?

op=4 0x40E609CD03AE02B501F300D8
A: 64 B: 115 C: 53 D: 46: PC: 6 SP: 77
A &= imm

op=5 0x50E609CD03AE02B501F300D8
A: 126 B: 115 C: 53 D: 46: PC: 6 SP: 77
A |= imm

op=6 0x60E609CD03AE02B501F300D8
A: 62 B: 115 C: 53 D: 46: PC: 6 SP: 77
A ^= imm

op=7 0x70E609CD03AE02B501F300D8
A: 65447 B: 115 C: 53 D: 46: PC: 6 SP: 77
A = ~A

op=8 0x80E609CD03AE02B501F300D8
没回显 EOFError

op=9 0x90E609CD03AE02B501F300D8
A: 1 B: 115 C: 53 D: 46: PC: 6 SP: 77
A = 1

op=10 0xA0E609CD03AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 102 SP: 77
PC = 102 (PC = imm)

op=11 0xB0E609CD03AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 109 SP: 78
PC = 109, SP += 1 (PC = 6 + 1 + imm)

op=12 0xC0E609CD03AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 77
不变

op=13 0xD0E609CD03AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 78
SP += 1

op=14 0xE0E609CD03AE02B501F300D8
A: 0 B: 115 C: 53 D: 46: PC: 6 SP: 76
A = 0, SP -= 1 (A = stack[SP--] or A = stack[--SP])

op=15 0xF0E609CD03AE02B501F300D8
Invalid syscall number 88
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 77


将imm设为0x40=64,sp初始设为2,再爆破一次,0~8结果是一样的,主要关注9~15:

op=9 0x90C0098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 2
不变

op=10 0xA0C0098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 64 SP: 2
PC = 64 (PC = imm)

op=11 0xB0C0098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 71 SP: 3
PC = 71, SP += 1 (PC = 6 + 1 + imm)

op=12 0xC0C0098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 2
不变

op=13 0xD0C0098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 3
SP += 1

op=14 0xE0C0098203AE02B501F300D8
A: 0 B: 115 C: 53 D: 46: PC: 6 SP: 1
A = 0, SP -= 1 (A = stack[SP--] or A = stack[--SP])

op=15 0xF0C0098203AE02B501F300D8
Invalid syscall number 88
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 2


op=11有点点像相对位移call;sp+=1,push了一个返回地址?

op=13、14看起来像一对push pop指令

op=15应该是syscall,调用号在A


验证栈指令猜测

op=13, 14看起来像push和pop指令,验证一下

sc = b''
# 填充AB=(88, 115) SP=0x40
sc += combine(op=0, ri=0, ty=1, imm=0x58)
sc += combine(op=0, ri=1, ty=1, imm=0x73)
sc += combine(op=0, ri=9, ty=1, imm=0x40)
# op=13, ri=0(A), ty=1, push A?
sc += combine(op=13, ri=0, ty=1, imm=0)
# op=14, ri=1(B), ty=1, pop B?
sc += combine(op=14, ri=1, ty=1, imm=0)
f.write('0x'+sc[::-1].hex().upper())
send(sc)

结果,14似乎不是pop,B被设置为了0:

0xE180D08009C001F300D8
A: 88 B: 0 C: 0 D: 0: PC: 5 SP: 64


整理2

目前已知的指令集信息:

     0000 0000 1 000'0000
bits  ~12 11~8 7 6~0
desc op   ri   t imm

t(ype)=1:
imm: 无符号,0~127
ri:  0, A
    1, B
    2, C
    3, D
    4~7, unknown
    8, PC
    9, SP
    10~15, unknown
op:  0, reg[ri] += imm
    1, reg[ri] -= imm
    2, reg[ri] = 0?
    3, 不变?
    4, reg[ri] &= imm
    5, reg[ri] |= imm
    6, reg[ri] ^= imm
    7, reg[ri] = ~reg[ri]
    9, 不变?
    10, PC = imm?
    11, PC += 1 + imm, SP += 1?
    12, 不变?
    13, SP += 1?
    14, reg[ri] = 0, sp -= 1?
    15, syscall(callnum=A)

t(ype)=0: unknown


探索指令3-爆破type=0

探索指令1中,对type=0的指令一无所知,现在爆破这种情况


type=0,爆破第二字节

猜测sp可会影响结果,将sp设置一个比较小的值

充一下寄存器,然后爆破第二字节:

for i in range(0x100):
   try:
       sc = b''
       # 填充ABCD和SP=(88, 115, 53, 46, 2)
       sc += combine(op=0, ri=0, ty=1, imm=0x58)
       sc += combine(op=0, ri=1, ty=1, imm=0x73)
       sc += combine(op=0, ri=2, ty=1, imm=0x35)
       sc += combine(op=0, ri=3, ty=1, imm=0x2e)
       sc += combine(op=0, ri=9, ty=1, imm=0x2)
       # ty=1, imm=3, 爆破第二字节
       sc += b'\x03' + int.to_bytes(i, 1, 'little')
       f.write('0x'+sc[::-1].hex().upper())
       send(sc)
   except BaseException as e:
       p.close()
       p = remote(ip, port)
       f.write('\nException type={}\n'.format(type(e)))

结果,有点类似单指令-爆破第二字节的情况,不同是第二操作数原本解释为imm,现在变成寄存器寻址:

ABCD SP=(88, 115, 53, 46, 2) imm=3

0x0003098203AE02B501F300D8
A: 134 B: 115 C: 53 D: 46: PC: 6 SP: 2
88 + 46 = 134 (A += D)

0x0103098203AE02B501F300D8
A: 88 B: 161 C: 53 D: 46: PC: 6 SP: 2
115 + 46 = 134 (B += D)

0x0203098203AE02B501F300D8
A: 88 B: 115 C: 99 D: 46: PC: 6 SP: 2
C += D

0x0303098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 92: PC: 6 SP: 2
D += D

0x0403~0x0703 0x0403098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 2
似乎没有变化

0x0803098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 52 SP: 2
PC += D

0x0903098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 48
SP += D

0x0A03~0x0F03 0x0A03098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 2
似乎没有变化

0x1003098203AE02B501F300D8
A: 42 B: 115 C: 53 D: 46: PC: 6 SP: 2
0x1103098203AE02B501F300D8
A: 88 B: 69 C: 53 D: 46: PC: 6 SP: 2
0x1203098203AE02B501F300D8
A: 88 B: 115 C: 7 D: 46: PC: 6 SP: 2
0x1303098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 0: PC: 6 SP: 2
REG -= D

0x1403~0x1703 0x1403098203AE02B501F300D8
A: 88 B: 115 C: 53 D: 46: PC: 6 SP: 2
似乎没有变化

......


type=0,爆破第一字节imm

第一字节的第7位是type,猜测0~6位是寄存器寻址,验证一下:

for i in range(0x80):
   try:
       sc = b''
       # 填充ABCD和SP=(88, 115, 53, 46, 2)
       sc += combine(op=0, ri=0, ty=1, imm=0x58)
       sc += combine(op=0, ri=1, ty=1, imm=0x73)
       sc += combine(op=0, ri=2, ty=1, imm=0x35)
       sc += combine(op=0, ri=3, ty=1, imm=0x2e)
       sc += combine(op=0, ri=9, ty=1, imm=0x2)
       # op=0 (+=), ri=0 (A), type=0, 爆破第一字节
       sc += int.to_bytes(i, 1, 'little') + b'\x00'
       f.write('imm=0x{:X} '.format(i)+'0x'+sc[::-1].hex().upper())
       send(sc)
   except BaseException as e:
       p.close()
       p = remote(ip, port)
       f.write('\nException type={}\n'.format(type(e)))

结果,0~6位只有低4位是有效的:

imm=0x0 0x0000098203AE02B501F300D8
A: 176 B: 115 C: 53 D: 46: PC: 6 SP: 2
...
imm=0x10 0x0010098203AE02B501F300D8
A: 176 B: 115 C: 53 D: 46: PC: 6 SP: 2
...
imm=0x20 0x0020098203AE02B501F300D8
A: 176 B: 115 C: 53 D: 46: PC: 6 SP: 2
...


整理3

type=0和1的相同点是8~11位作为第一源操作数及目的操作数,使用寄存器寻址;0~6位都是作为第二源操作数

不同点是type=0第二操作数是寄存器寻址,type=1第二操作数直接解释为立即数

目前已知的指令集信息:

     0000 0000 0 000'0000
bits  ~12 11~8 7 6~0
desc op   ri   t imm

t(ype): 0, E = reg[imm&0xf]
       1, E = imm
imm: 无符号,0~127
ri:  0, A
    1, B
    2, C
    3, D
    4~7, unknown
    8, PC
    9, SP
    10~15, unknown
op:  0, reg[ri] += E
    1, reg[ri] -= E
    2, reg[ri] = 0?
    3, 不变?
    4, reg[ri] &= E
    5, reg[ri] |= E
    6, reg[ri] ^= E
    7, reg[ri] = ~reg[ri]
    8, 无回显
    9, 不变?
    10, PC = E
    11, PC = 1 + ri[imm] / PC = PC + 1 + imm
    12, 不变?
    13, SP += 1, push?
    14, reg[ri] = 0, sp -= 1?
    15, syscall(callnum=A)

t(ype)=0: unknown


syscall

找调用号

type=0,爆破第二字节时发现,op=15(syscall)时,ri必须为0,ty好像随意

linux调用号5是open,猜一波,看来和linux调用号不完全一样:

sc = b''
# A+=5 linux-open
sc += combine(op=0, ri=0, ty=1, imm=5)
# syscall
sc += combine(op=15, ri=0, ty=0, imm=0)
f.write('0x'+sc[::-1].hex().upper())
send(sc)

回显内容(更改syscall的imm回显是一样的):

Invalid file descriptor 0

Execution halted!
A: 5 B: 0 C: 0 D: 0: PC: 2 SP: 0
Would you like to start over? (y/n)


开爆调用号:

for callnumber in range(10):
   if callnumber == 3:
       continue
   sc = b''
   # A = callnum
   sc += combine(op=0, ri=0, ty=1, imm=callnumber)
   # syscall
   sc += combine(op=15, ri=0, ty=0, imm=0)
   f.write('cn={} '.format(callnumber)+'0x'+sc[::-1].hex().upper())
   send(sc)

测了几个调用号:

cn=0 0xF0000080
A: 0 B: 0 C: 0 D: 0: PC: 2 SP: 0
cn=1 0xF0000081
A: 1 B: 0 C: 0 D: 0: PC: 2 SP: 0
cn=2 0xF0000082
A: 2 B: 0 C: 0 D: 0: PC: 2 SP: 0

cn=3
没有回显,也不会断开,猜是读控制台

cn=4 0xF0000084
Error: Unable to open file ''

cn=5 0xF0000085
Invalid file descriptor 0

cn=6 0xF0000086
Invalid syscall number 6

cn=7 0xF0000087
Invalid syscall number 7

cn=8 0xF0000088
Invalid syscall number 8

cn=9 0xF0000089
Invalid syscall number 9


结果:

0~2 未知
3 读控制台?
4 open
5 fd相关


syscall_4 open

猜传参方式是op=13 push

sc = b''
# push 'f'
sc += combine(op=0, ri=0, ty=1, imm=ord('f')) # A += 'f'
sc += combine(op=13, ri=0, ty=0, imm=0) # push A
sc += combine(op=1, ri=0, ty=1, imm=ord('f')) # A -= 'f'
# push 'l'
sc += combine(op=0, ri=0, ty=1, imm=ord('l')) # A += 'l'
sc += combine(op=13, ri=0, ty=0, imm=0) # push A
sc += combine(op=1, ri=0, ty=1, imm=ord('l')) # A -= 'l'
# syscall_4
callnumber = 4
sc += combine(op=0, ri=0, ty=1, imm=callnumber)
sc += combine(op=15, ri=0, ty=0, imm=0)
send(sc, detail=True)

回显:

Error: Unable to open file 'fl'

Execution halted!
A: 4 B: 0 C: 65535 D: 0: PC: 8 SP: 2
Would you like to start over? (y/n)


猜测正确,C应该是返回值

push字符串的方式:

def sc_push_str(s):
   sc = b''
   for c in s:
       sc += combine(op=0, ri=0, ty=1, imm=ord(c)) # A += 'f'
       sc += combine(op=13, ri=0, ty=0, imm=0) # push A
       sc += combine(op=1, ri=0, ty=1, imm=ord(c)) # A += 'f'
   return sc


尝试open('flag'),:

sc = b''
sc += sc_push_str('flag')
# syscall_4
callnumber = 4
sc += combine(op=0, ri=0, ty=1, imm=callnumber)
sc += combine(op=15, ri=0, ty=0, imm=0)
# sc += combine(op=15, ri=0, ty=0, imm=0)
send(sc, detail=True)

open一次回显:

Execution halted!
A: 4 B: 0 C: 0 D: 0: PC: 14 SP: 4
Would you like to start over? (y/n)

open两次回显:

Execution halted!
A: 4 B: 0 C: 1 D: 0: PC: 15 SP: 4
Would you like to start over? (y/n)

故C就是返回的fd


意外发现

在测试push参数,然后call open的时候

以为op=2,ty=1的效果是reg[ri] = 0,尝试用来清零寄存器,结果发现syscall时提示调用号不对

一看我push时用的A,然后用op=2, ri=0, ty=1, imm=0尝试清零A,结果这是个读栈指令,A并未被清零


发现ty=1时,op=2/13的功能:

ty=1
op: 2,  reg[ri] = stack[imm], 读栈
   13, stack[SP++] = reg[ri], push到栈


syscall_5 read

测试出来syscall 5的调用约定

传入
A call number
B fd
C ?
D count

返回
B count


可以通过传入一个很大的count(D),然后看返回的count(B)得知文件长度


读出flag文件,这里我本地放的测试文件count是20

count = 20
flag = ''
for i in range(count):
   sc = b''
   sc += sc_open('flag') # ret: B = fd
   sc += combine(op=0, ri=0, ty=1, imm=5) # A = syscall-5-read
   sc += combine(op=0, ri=3, ty=1, imm=40) # D = count
   sc += combine(op=15, ri=0, ty=0, imm=0) # syscall
   sc += combine(op=1, ri=0, ty=1, imm=5) # A = 0
   sc += combine(op=2, ri=0, ty=1, imm=i) # A = stack[i]
   regA = int(send(sc).split('\n')[2].split(' ')[1])
   flag += chr(regA)
print(flag)




[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2024-5-22 22:50 被wx_御史神风编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 3070
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-3-1 09:28
1
游客
登录 | 注册 方可回帖
返回
//