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);
You're trying to resume the attached process by sending
SIGCONT.The
ptracemanpage describes Signal-delivery-stop: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 callingwaitpidafter sending the signal, as described in the manpage.If you just want the attached process to keep running without supervision, you should use
PTRACE_CONTinstead.