首頁 > 後端開發 > Python教學 > C程式和子進程

C程式和子進程

WBOY
發布: 2024-02-22 13:01:18
轉載
817 人瀏覽過

C程式和子進程

問題內容

我寫了這個簡單的 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,我將透過它以互動模式進行通訊。

這就是為什麼:

  1. 我只能使用內建函式庫
  2. 我必須傳送位元組並且無法附加結尾 \n:我的有效負載將無法對齊或可能導致失敗
  3. 我需要保持 stdin 打開
  4. 我無法更改 c 程式碼


正確答案


更改這些:

  • 在 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中文網其他相關文章!

來源:stackoverflow.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板