英文:
Linux Process GUI freezes when attached
问题
我试图附加(Linux)/ .dll注入(Windows)到给定进程。 代码成功,但图像冻结。 附加的进程本身继续工作(我添加了一个恢复)。
我以前做过这个,但这是我第一次尝试一个具有GUI的进程(游戏)。
在Linux上是否可行,或者我没有正确的方法?
英文:
Im trying to attach(linux)/.dll inject(windows) to a given process. The code succeeds but the image freezes. The attached process itself continues to work (I added a resume).
Ive done this previously but this is the first time I try with a process(game) that has a GUI.
Is it doable in Linux or I dont have the correct approach?
#include <wx/wx.h>
#include <wx/thread.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <string>
#include <dirent.h>
#include <libgen.h>
#include <fcntl.h>
#include <signal.h> // Add this include at the top of your file
// Define new event types
wxDEFINE_EVENT(EVT_ATTACH_SUCCESS, wxThreadEvent);
wxDEFINE_EVENT(EVT_DETACH_SUCCESS, wxThreadEvent);
class WorkerThread : public wxThread {
public:
WorkerThread(wxEvtHandler *parent) : wxThread(wxTHREAD_JOINABLE), m_parent(parent), targetPid(-1) {}
~WorkerThread() override {
if (targetPid != -1) {
if (ptrace(PTRACE_DETACH, targetPid, nullptr, nullptr) == -1) {
wxLogError("Failed to detach from process: %s", strerror(errno));
} else {
wxLogMessage("Successfully detached from process %ld", targetPid);
}
wxQueueEvent(m_parent, new wxThreadEvent(wxEVT_THREAD, EVT_DETACH_SUCCESS));
}
}
wxThread::ExitCode Entry() override {
std::string processName = "dura";
int pipefd[2];
if (pipe(pipefd) == -1) {
wxLogError("pipe failed: %s", strerror(errno));
return (wxThread::ExitCode)0;
}
pid_t childPid = fork();
if (childPid == -1) {
wxLogError("fork failed: %s", strerror(errno));
return (wxThread::ExitCode)0;
}
if (childPid == 0) { // Child process
close(pipefd[0]); // Close read end
dup2(pipefd[1], STDOUT_FILENO); // Redirect stdout to pipe
execlp("pgrep", "pgrep", processName.c_str(), nullptr);
wxLogError("execlp failed: %s", strerror(errno));
exit(EXIT_FAILURE);
} else { // Parent process
close(pipefd[1]); // Close write end
int status;
waitpid(childPid, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { // Process found
char buf[32];
read(pipefd[0], buf, sizeof(buf) - 1);
pid_t targetPid = strtoul(buf, nullptr, 10);
if (ptrace(PTRACE_ATTACH, targetPid, nullptr, nullptr) == -1) {
wxLogError("Failed to attach to process: %s", strerror(errno));
return (wxThread::ExitCode)0;
} else {
this->targetPid = targetPid;
kill(targetPid, SIGCONT); // Add this line to send the SIGCONT signal
wxLogMessage("Successfully attached to process %ld", targetPid);
wxThreadEvent* event = new wxThreadEvent(wxEVT_THREAD, EVT_ATTACH_SUCCESS);
event->SetString("Successfully attached to process " + std::to_string(targetPid));
wxQueueEvent(m_parent, event);
}
// Wait for the process to stop
waitpid(targetPid, nullptr, 0);
while(!TestDestroy()) {
wxThread::This()->Sleep(1000); // 1 second sleep
}
} else if (WIFEXITED(status)) { // Process not found or error executing pgrep
wxLogError("Process %s not found or error executing pgrep.", processName.c_str());
}
}
return (wxThread::ExitCode)0;
}
private:
wxEvtHandler *m_parent;
pid_t targetPid;
};
class MyFrame : public wxFrame {
public:
MyFrame() : wxFrame(NULL, wxID_ANY, "Hello wxWidgets") {
m_panel = new wxPanel(this);
m_attachBtn = new wxButton(m_panel, wxID_ANY, "Attach");
m_detachBtn = new wxButton(m_panel, wxID_ANY, "Detach");
m_textCtrl = new wxTextCtrl(m_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY);
wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(m_attachBtn, 0, wxEXPAND | wxALL, 5);
sizer->Add(m_detachBtn, 0, wxEXPAND | wxALL, 5);
sizer->Add(m_textCtrl, 1, wxEXPAND | wxALL, 5);
m_panel->SetSizer(sizer);
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnAttach, this, m_attachBtn->GetId());
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnDetach, this, m_detachBtn->GetId());
Bind(EVT_ATTACH_SUCCESS, &MyFrame::OnAttachSuccess, this);
Bind(EVT_DETACH_SUCCESS, &MyFrame::OnDetachSuccess, this);
m_workerThread = nullptr;
}
void OnAttach(wxCommandEvent &event) {
m_workerThread = new WorkerThread(this);
if (m_workerThread->Run() != wxTHREAD_NO_ERROR) {
wxLogError("Could not create the worker thread!");
delete m_workerThread;
m_workerThread = nullptr;
}
m_textCtrl->AppendText("Attaching to process...\n");
}
void OnDetach(wxCommandEvent &event) {
if (m_workerThread) {
m_workerThread->Delete(); // Signals the thread to exit its main loop
m_workerThread->Wait(); // Waits for the thread to exit
delete m_workerThread;
m_workerThread = nullptr;
}
}
void OnAttachSuccess(wxThreadEvent &event) {
m_textCtrl->AppendText(event.GetString() + "\n");
}
void OnDetachSuccess(wxThreadEvent &event) {
m_textCtrl->AppendText("Successfully detached from process.\n");
}
private:
wxPanel *m_panel;
wxButton *m_attachBtn;
wxButton *m_detachBtn;
wxTextCtrl *m_textCtrl;
WorkerThread *m_workerThread;
};
class MyApp : public wxApp {
public:
bool OnInit() override {
MyFrame *frame = new MyFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
答案1
得分: 2
你正在尝试通过发送SIGCONT
信号来恢复附加的进程。
ptrace
手册描述了Signal-delivery-stop:
当一个(可能是多线程的)进程接收到除了SIGKILL之外的任何信号时,内核会选择一个处理该信号的任意线程... 如果选择的线程被跟踪,它会进入信号传递停止状态。 在这一点上,信号尚未传递给进程,可以被跟踪器抑制。如果跟踪器不抑制信号,它会在下一个ptrace重启请求中将信号传递给被跟踪者。本手册页中称此信号传递的第二步为信号注入。请注意,如果信号被阻塞,直到信号解除阻塞,信号传递停止才会发生,唯一的例外是SIGSTOP不能被阻塞。
跟踪器将信号传递停止视为waitpid(2)返回WIFSTOPPED(status)为true,并返回由WSTOPSIG(status)返回的信号。如果信号是SIGTRAP,这可能是一种不同类型的ptrace停止;有关详细信息,请参阅下面的“Syscall-stops”和“execve”部分。
所以你所做的只是将信号排队发送给仍然由原始的PTRACE_ATTACH
停止的进程。你可以按照手册中描述的方式,在发送信号后调用waitpid
来确认这一点。
如果你只想让附加的进程继续运行而无需监督,你应该使用PTRACE_CONT
。
英文:
You're trying to resume the attached process by sending SIGCONT
.
The ptrace
manpage describes Signal-delivery-stop:
> When a (possibly multithreaded) process receives any signal except SIGKILL, the kernel selects an arbitrary thread which handles
the signal ... If the selected thread is traced, it enters signal-delivery-stop. At this point, the signal is not yet delivered to the process, and can be
suppressed by the tracer. If the tracer doesn't suppress the signal, it passes the signal to the tracee in the next ptrace restart
request. This second step of signal delivery is called signal injection in this manual page. Note that if the signal is blocked,
signal-delivery-stop doesn't happen until the signal is unblocked, with the usual exception that SIGSTOP can't be blocked.
> Signal-delivery-stop is observed by the tracer as waitpid(2) returning with WIFSTOPPED(status) true, with the signal returned by
WSTOPSIG(status). If the signal is SIGTRAP, this may be a different kind of ptrace-stop; see the "Syscall-stops" and "execve" sections below for details.
So all you've done is queued up a signal to a process which is still stopped by the original PTRACE_ATTACH
. You should be able to confirm this by calling waitpid
after sending the signal, as described in the manpage.
If you just want the attached process to keep running without supervision, you should use PTRACE_CONT
instead.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论