我寫了這個簡單的 c 程式來解釋具有相同特徵的更困難的問題。
#include <stdio.h> int main(int argc, char *argv[]) { int n; while (1){ scanf("%d", &n); printf("%d\n", n); } return 0; }
它按預期工作。
我還編寫了一個子進程腳本來與該程式互動:
from subprocess import popen, pipe, stdout process = popen("./a.out", stdin=pipe, stdout=pipe, stderr=stdout) # sending a byte process.stdin.write(b'3') process.stdin.flush() # reading the echo of the number print(process.stdout.readline()) process.stdin.close()
問題是,如果我執行 python 腳本,執行會在 readline()
上凍結。事實上,如果我中斷腳本,我會得到:
/tmp » python script.py ^ctraceback (most recent call last): file "/tmp/script.py", line 10, in <module> print(process.stdout.readline()) ^^^^^^^^^^^^^^^^^^^^^^^^^ keyboardinterrupt
如果我更改我的 python 腳本:
from subprocess import popen, pipe, stdout process = popen("./a.out", stdin=pipe, stdout=pipe, stderr=stdout) with process.stdin as pipe: pipe.write(b"3") pipe.flush() # reading the echo of the number print(process.stdout.readline()) # sending another num: pipe.write(b"4") pipe.flush() process.stdin.close()
我得到了這個輸出:
» python script.py b'3\n' Traceback (most recent call last): File "/tmp/script.py", line 13, in <module> pipe.write(b"4") ValueError: write to closed file
這意味著第一個輸入已正確發送,並且讀取已完成。
我真的找不到解釋這種行為的東西;有人可以幫助我理解嗎? 提前致謝
[編輯]:由於有很多要點需要澄清,所以我添加了此編輯。我正在使用 rop
技術進行緩衝區溢出漏洞利用的培訓,並且我正在編寫 python 腳本來實現這一目標。為了利用這個漏洞,由於aslr,我需要發現libc
位址並使程式重新啟動而不終止。由於腳本將在目標機器上執行,我不知道哪些庫可用,那麼我將使用 subprocess,因為它是內建於 python 中的。不詳細說明,攻擊在第一個scanf
上發送一系列位元組,目的是洩漏libc
基底位址並重新啟動程式;然後發送第二個有效負載以獲得一個shell,我將透過它以互動模式進行通訊。
這就是為什麼:
\n
:我的有效負載將無法對齊或可能導致失敗stdin
打開更改這些:
在 c 程式讀取的數字之間發送分隔符號。 scanf(3) 接受任何非數字位元組作為分隔符號。為了最簡單的緩衝,請從 python 發送換行符(例如 .write(b'42\n')
)。如果沒有分隔符,scanf(3) 將無限期地等待更多數字。
每次寫入(c 和 python 中)後,刷新輸出。
這對我有用:
#include <stdio.h> int main(int argc, char *argv[]) { int n; while (1){ scanf("%d", &n); printf("%d\n", n); fflush(stdout); /* i've added this line only. */ } return 0; }
import subprocess p = subprocess.popen( ('./a.out',), stdin=subprocess.pipe, stdout=subprocess.pipe) try: print('a'); p.stdin.write(b'42 '); p.stdin.flush() print('b'); print(repr(p.stdout.readline())); print('c'); p.stdin.write(b'43\n'); p.stdin.flush() print('d'); print(repr(p.stdout.readline())); finally: print('e'); print(p.kill())
原始 c 程式在終端機視窗中交互運行時能夠正常工作的原因是,在 c 中,當將換行符 (\n
) 寫入終端時,輸出會自動刷新。因此 printf("%d\n", n);
最後會隱含執行 fflush(stdout);
。
當使用 subprocess
從 python 運行時,原始 c 程式無法工作的原因是它將輸出寫入管道(而不是終端),並且沒有自動刷新到管道。發生的情況是,python 程式正在等待字節,而c 程式不會將這些位元組寫入管道,但它正在等待更多位元組(在下一個scanf
中),因此兩個程式都在無限期地等待對方。 (但是,在輸出幾 kib(通常為 8192 位元組)後,將會出現部分自動刷新。但是單個十進制數太短,無法觸發該操作。)
如果無法更改 c 程序,那麼您應該使用終端設備而不是管道來在 c 和 python 程式之間進行通訊。 pty
python 模組可以建立終端設備,這對我來說適用於你的原始 c 程式:
import os, pty, subprocess master_fd, slave_fd = pty.openpty() p = subprocess.popen( ('./a.out',), stdin=slave_fd, stdout=slave_fd, preexec_fn=lambda: os.close(master_fd)) try: os.close(slave_fd) master = os.fdopen(master_fd, 'rb+', buffering=0) print('a'); master.write(b'42\n'); master.flush() print('b'); print(repr(master.readline())); print('c'); master.write(b'43\n'); master.flush() print('d'); print(repr(master.readline())); finally: print('e'); print(p.kill())
如果你不想從python發送換行符,這裡有一個沒有換行符的解決方案,它對我有用:
import os, pty, subprocess, termios master_fd, slave_fd = pty.openpty() ts = termios.tcgetattr(master_fd) ts[3] &= ~(termios.ICANON | termios.ECHO) termios.tcsetattr(master_fd, termios.TCSANOW, ts) p = subprocess.Popen( ('./a.out',), stdin=slave_fd, stdout=slave_fd, preexec_fn=lambda: os.close(master_fd)) try: os.close(slave_fd) master = os.fdopen(master_fd, 'rb+', buffering=0) print('A'); master.write(b'42 '); master.flush() print('B'); print(repr(master.readline())); print('C'); master.write(b'43\t'); master.flush() print('D'); print(repr(master.readline())); finally: print('E'); print(p.kill())
以上是C程式和子進程的詳細內容。更多資訊請關注PHP中文網其他相關文章!