Я пытаюсь портировать программу с Windows на Linux.
Я столкнулся с проблемой, когда узнал, что нет «настоящего» ReadProcessMemory
аналог на Linux; Я искал альтернативу, и я нашел ptrace
Мощный отладчик процессов.
Я быстро написал два небольших консольных приложения на C ++ для тестирования ptrace
, прежде чем использовать его в программе.
TestApp
Это след; он продолжает печатать два целых числа каждые 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();
}
Тест памяти
Это трассировщик; он запрашивает имя процесса и получает PID с помощью команды pidof -s
, затем 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 = 0x012345; // 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.
Обновить: С помощью Sleeper
в MemoryTest перед циклом «do-while» устраняется проблема получения первого значения адреса памяти, даже если значение секунд, миллисекунд или микросекунд равно 0. Почему?
Davide,
Вы смотрели на файловую систему / proc? Он содержит файлы карты памяти, которые можно использовать для просмотра всего пространства процесса. Вы также можете написать в пространстве, чтобы установить точку останова. Также в / proc есть много другой информации.
Команда PTRACE_CONT может использоваться для продолжения процесса. Как правило, цель будет приостановлена с помощью PTRACE_ATTACH, когда подключится отладчик.
Страница man говорит, что PTRACE_SIEZE не должен приостанавливать процесс. Какой вариант и версию Linux вы используете? PTRACE_SIEZE существует уже довольно давно, поэтому я не уверен, почему у вас там проблемы.
Я отмечаю, что значение addr установлено в 0x12345. Это действительный адрес в целевом пространстве? Или это просто пример? Как интересует адрес стека (&значение) между двумя процессами?
Я не слишком уверен насчет кодов возврата. Обычно 0 означает, что все хорошо, errno может быть просто значением зависания из последней ошибки.
—Matt
После долгих исследований я уверен, что нет никакого способа использовать 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 *) 0x012345; // 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();
}