2016-07-30 131 views
2

我試圖將一個程序從Windows移植到Linux。
當我發現Linux上沒有「真正的」ReadProcessMemory副本時遇到問題;我搜索了一個替代方案,並發現ptrace,一個功能強大的流程調試器。
我在C++中快速編寫了兩個小控制檯應用程序,以便在程序中使用它之前測試ptraceLinux:有沒有一種方法可以在不停止/暫停進程的情況下使用ptrace(SIGSTOP)?

TestApp

這是tracee;它每隔50毫秒不斷打印兩個整數,同時每次增加1個值。

#include <QCoreApplication> 
#include <QThread> 
#include <iostream> 

using namespace std; 

class Sleeper : public QThread 
{ 
public: 
    static void usleep(unsigned long usecs){QThread::usleep(usecs);} 
    static void msleep(unsigned long msecs){QThread::msleep(msecs);} 
    static void sleep(unsigned long secs){QThread::sleep(secs);} 
}; 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    int value = 145; 
    int i = 0; 

    do { 
    cout << "i: " << i << " " << "Value: " << value << endl; 
    value++; 
    i++; 
    Sleeper::msleep(50); 
    } while (true); 

    return a.exec(); 
} 

MemoryTest

這是示蹤劑;它會詢問進程名稱並使用命令pidof -s檢索PID,然後ptrace將附加到進程並每隔500毫秒檢索一次內存地址的值10次。

#include <QCoreApplication> 
#include <QThread> 
#include <iostream> 
#include <string> 
#include <sys/ptrace.h> 
#include <errno.h> 

using namespace std; 

class Sleeper : public QThread 
{ 
public: 
    static void usleep(unsigned long usecs){QThread::usleep(usecs);} 
    static void msleep(unsigned long msecs){QThread::msleep(msecs);} 
    static void sleep(unsigned long secs){QThread::sleep(secs);} 
}; 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    char process_name[50]; 
    cout << "Process name: "; 
    cin >> process_name; 

    char command[sizeof(process_name) + sizeof("pidof -s ")]; 
    snprintf(command, sizeof(command), "pidof -s %s", process_name); 

    FILE* shell = popen(command, "r"); 
    char pidI[sizeof(shell)]; 
    fgets(pidI, sizeof(pidI), shell); 
    pclose(shell); 

    pid_t pid = atoi(pidI); 
    cout << "The PID is " << pid << endl; 

    long status = ptrace(PTRACE_ATTACH, pid, NULL, NULL); 
    cout << "Status: " << status << endl; 
    cout << "Error: " << errno << endl; 

    unsigned long addr = 0x; // Example address, not the true one 
    int i = 0; 
    do { 
    status = ptrace(PTRACE_PEEKDATA, pid, addr, NULL); 
    cout << "Status: " << status << endl; 
    cout << "Error: " << errno << endl; 
    i++; 
    Sleeper::msleep(500); 
    } while (i < 10); 

    status = ptrace(PTRACE_DETACH, pid, NULL, NULL); 
    cout << "Status: " << status << endl; 
    cout << "Error: " << errno << endl; 

    return a.exec(); 
} 

一切工作正常,但TestApp暫停(SIGSTOP),直到ptrace從中分離。
此外,當它附加到進程時,狀態爲0,錯誤爲2;它第一次嘗試檢索內存地址值時失敗,狀態-1和錯誤3.是否正常?
有沒有辦法阻止ptrace向進程發送SIGSTOP信號? 我已經嘗試過使用PTRACE_SEIZE代替PTRACE_ATTACH,但它不工作:狀態-1和錯誤3.

更新:前MemoryTest使用Sleeper「辦,而」循環修復了第一個問題內存地址值檢索,即使秒,毫秒或微秒的值爲0.爲什麼?

回答

1

大量的研究,我敢肯定,沒有使用ptrace不停止過程的方法之後。
我發現了一個真實的ReadProcessMemory副本,名爲process_vm_readv,這更簡單。

我發佈了代碼,希望幫助處於我(以前)情況的人。

非常感謝mkrautz他的幫助編碼MemoryTest這個漂亮的功能。

#include <QCoreApplication> 
#include <QThread> 
#include <sys/uio.h> 
#include <stdint.h> 
#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <iostream> 

using namespace std; 

class Sleeper : public QThread 
{ 
public: 
    static void usleep(unsigned long usecs){QThread::usleep(usecs);} 
    static void msleep(unsigned long msecs){QThread::msleep(msecs);} 
    static void sleep(unsigned long secs){QThread::sleep(secs);} 
}; 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    char process_name[50]; 
    cout << "Process name: "; 
    cin >> process_name; 

    char command[sizeof(process_name) + sizeof("pidof -s ")]; 
    snprintf(command, sizeof(command), "pidof -s %s", process_name); 

    FILE* shell = popen(command, "r"); 
    char pidI[sizeof(shell)]; 
    fgets(pidI, sizeof(pidI), shell); 
    pclose(shell); 

    pid_t pid = atoi(pidI); 

    cout << "The PID is " << pid << endl; 

    if (pid == 0) 
     return false; 

    struct iovec in; 
    in.iov_base = (void *) 0x; // Example address, not the true one 
    in.iov_len = 4; 

    uint32_t foo; 

    struct iovec out; 
    out.iov_base = &foo; 
    out.iov_len = sizeof(foo); 

    do { 
     ssize_t nread = process_vm_readv(pid, &out, 1, &in, 1, 0); 
     if (nread == -1) { 
      fprintf(stderr, "error: %s", strerror(errno)); 
     } else if (nread != in.iov_len) { 
      fprintf(stderr, "error: short read of %li bytes", (ssize_t)nread); 
     } 
     cout << foo << endl; 
     Sleeper::msleep(500); 
    } while (true); 

    return a.exec(); 
} 
2

Davide,

你看過/ proc文件系統嗎?它包含可用於查看整個進程空間的內存映射文件。您也可以在空間中寫入以設置斷點。/proc中還有大量其他信息。

PTRACE_CONT命令可用於繼續進程。一般來說,當調試器連接時,目標將被暫停PTRACE_ATTACH。

手冊頁說PTRACE_SIEZE不應該暫停過程。你使用的是什麼風味和版本的Linux? PTRACE_SIEZE已經存在了很長一段時間,所以我不確定你爲什麼在那裏遇到麻煩。

我注意到addr值設置爲0x12345。這是目標空間中的有效地址嗎?還是僅僅是一個例子?在兩個進程之間如何傳送感興趣的堆棧地址(值爲&)?

我不太確定返回碼。一般來說,0表示一切都很好,errno可能只是最後一個錯誤的宿醉值。

--Matt

+0

非常感謝您的回答。我沒有看看/ proc,因爲我想用一個更簡單的方法;如果這是不可能的,我會嘗試使用內存映射文件。我使用Debian 8 x64,這不應該是問題;我的猜測是'PTRACE_SEIZE'需要以與'PTRACE_ATTACH'不同的方式使用,但是我找不到任何特定的文檔。 –

+0

對不起這兩條評論,我按了Enter以爲它打破了界限;我編輯了評論,但5分鐘後無法更新。 'addr'值只是一個例子,正確的工作正常。關於錯誤代碼,它們不應該是個問題,因爲程序工作正常。 –

+0

我現在看到你在代碼中的評論,說這個地址就是一個例子,抱歉應該更好地閱讀它。如果它有用,請'接受'我的回答,因爲我試圖在Stack Overflow上建立我的聲望。 你是正確的,ptrace調用比操作/ proc文件更簡單。然而,/ proc區有很多信息。如果您正在學習有關Linux調試和流程結構的知識,這將是一個富有成效的研究領域。 PTRACE_CONT在程序中工作? –

相關問題