-
Notifications
You must be signed in to change notification settings - Fork 29
/
defs.cpp
359 lines (334 loc) · 10.3 KB
/
defs.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/**
* @file idaplugin/defs.cpp
* @brief Plugin-global definitions and includes.
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/
#include "defs.h"
#include "plugin_config.h"
#include "decompiler.h"
#include "sleighinterface.h"
#if defined(OS_WINDOWS)
#include <windows.h>
#ifndef __X64__
#include <tlhelp32.h>
#endif
#else
#define _pipe(x,y,z) pipe2(x,z)
#define _dup dup
#define _dup2 dup2
#define _close close
#define STDOUT_FILENO 1
#define STDIN_FILENO 0
#define _O_NOINHERIT O_CLOEXEC
#define _O_BINARY 0
#endif
namespace idaplugin {
#if defined(OS_WINDOWS) && !defined(__X64__)
BOOL CALLBACK enumWinCB(HWND hWnd, LPARAM lParam)
{
unsigned long pid = 0;
GetWindowThreadProcessId(hWnd, &pid);
if (lParam != pid || GetWindow(hWnd, GW_OWNER) != (HWND)0 || !IsWindowVisible(hWnd)) {
return TRUE;
}
ShowWindow(hWnd, SW_HIDE);
return FALSE;
}
#endif
void stopDecompilation(RdGlobalInfo* di, bool deregister, bool checkonly, bool closehandles)
{
if (di->decompPid) {
try {
if (deregister) di->idacb->decInt->deregisterProgram();
} catch (DecompError& e) {
WARNING_GUI("Caught deregistration error: " << e.explain);
}
int rc = 0;
if (check_process_exit(di->hDecomp, &rc, 0) != 0) {
if (checkonly) return;
term_process(di->hDecomp);
}
di->decompPid = 0;
}
if (closehandles) {
if (di->rdHandle != -1) {
_close(di->rdHandle); di->rdHandle = -1;
}
if (di->wrHandle != -1) {
_close(di->wrHandle); di->wrHandle = -1;
}
}
}
/**
* Run command using IDA SDK API.
*/
int runCommand(
const std::string& cmd,
const std::string& args,
intptr_t* pid,
void** hdl,
int* readpipe,
int* writepipe,
bool showWarnings)
{
launch_process_params_t procInf;
procInf.path = cmd.c_str();
procInf.args = args.size() == 0 ? NULL : args.c_str();
procInf.flags = LP_HIDE_WINDOW;
#if defined(OS_WINDOWS)
/*SECURITY_ATTRIBUTES saAttr; // Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
WARNING_GUI(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
WARNING_GUI(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
WARNING_GUI(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
WARNING_GUI(TEXT("Stdin SetHandleInformation"));
procInf.in_handle = (ssize_t)g_hChildStd_IN_Rd;
procInf.out_handle = (ssize_t)g_hChildStd_OUT_Wr;
procInf.err_handle = procInf.out_handle;
*writepipe = _open_osfhandle((intptr_t)g_hChildStd_IN_Wr, _O_WRONLY);
*readpipe = _open_osfhandle((intptr_t)g_hChildStd_OUT_Rd, _O_RDONLY);*/
PROCESS_INFORMATION pi{};
procInf.info = π
#endif
//IDA blocks stdout/stdin but on Windows could use _fileno(__acrt_iob_func(1/0)), which returns invalid value -2 as no handle is assigned since no console window present
int rpipefd[2], wpipefd[2];
//qhandle_t rpipefd[2], wpipefd[2]; //problem is the inheritence or close on exec flags are critical for the stdout/stdin child process
if (_pipe(rpipefd, 4096, _O_BINARY | _O_NOINHERIT) < 0) {
//if (qpipe_create(rpipefd) < 0) {
WARNING_GUI("Pipe creation failed");
}
if (_pipe(wpipefd, 4096, _O_BINARY | _O_NOINHERIT) < 0) {
//if (qpipe_create(wpipefd) < 0) {
WARNING_GUI("Pipe creation failed");
}
#if defined(OS_WINDOWS)
int fdStdIn = _dup(wpipefd[0]);// _dup(STDIN_FILENO);
int fdStdOut = _dup(rpipefd[1]); // _dup(STDOUT_FILENO);
#else
int fdStdIn = _dup(STDIN_FILENO);
int fdStdOut = _dup(STDOUT_FILENO);
if (_dup2(wpipefd[0], STDIN_FILENO) != 0) WARNING_GUI("Pipe handle duplication failed");
if (_dup2(rpipefd[1], STDOUT_FILENO) != 0) WARNING_GUI("Pipe handle duplication failed");
#endif
_close(rpipefd[1]);
_close(wpipefd[0]);
*readpipe = rpipefd[0];
*writepipe = wpipefd[1];
#if defined(OS_WINDOWS)
procInf.in_handle = _get_osfhandle(fdStdIn); // (ssize_t)wpipefd[0];
procInf.out_handle = _get_osfhandle(fdStdOut); // (ssize_t)rpipefd[1];
procInf.err_handle = procInf.out_handle;// (ssize_t)rpipefd[1];
#endif
qstring errbuf;
DBG_MSG("Launching: " << cmd << " " << args << "\n");
void* p = launch_process(procInf, &errbuf);
/*#if defined(OS_WINDOWS)
CloseHandle(g_hChildStd_IN_Rd);
//CloseHandle(g_hChildStd_IN_Wr);
//CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
#else*/
#if !defined(OS_WINDOWS)
if (_dup2(fdStdOut, STDOUT_FILENO) != 0) WARNING_GUI("Pipe handle duplicatoin restoration failed");
if (_dup2(fdStdIn, STDIN_FILENO) != 0) WARNING_GUI("Pipe handle duplication restoration failed");;
#endif
_close(fdStdOut);
_close(fdStdIn);
//#endif
if (p == nullptr)
{
if (*readpipe != -1) {
_close(*readpipe); *readpipe = -1;
}
if (*writepipe != -1) {
_close(*writepipe); *writepipe = -1;
}
if (showWarnings)
{
WARNING_GUI("launch_process(" << procInf.path << " "
<< procInf.args << ") failed to launch " << errbuf.c_str()
<< "\n");
}
return 1;
}
*hdl = p;
if (pid)
{
#if defined(OS_WINDOWS)
*pid = pi.dwProcessId;
#ifndef __X64__
//of course there is no nice way to do this, hope the delay allows the process to create its window or it wont hide
int rc = 0, ctr = 0; //up to one second only before bailing out as user could have closed the window or other situation would cause a hang
while (check_process_exit(p, &rc, 0) == 1 && EnumWindows(enumWinCB, (LPARAM)* pid) && ctr != 20) {
qsleep(50); ctr++;
}
/*HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap != INVALID_HANDLE_VALUE) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hProcessSnap, &pe32)) {
do {
if (pe32.th32ParentProcessID == *pid)
EnumWindows(enumWinCB, (LPARAM)pe32.th32ProcessID);
} while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle(hProcessSnap);
}*/
#endif
//*hdl = pi.hProcess;
//CloseHandle(pi.hProcess); //must keep because its a child process and use for check status and but must close process in Windows...
//WARNING_GUI("p: " << std::hex << (__int64)p << " pi.hProcess: " << pi.hProcess << " pi.hThread: " << pi.hThread);
if (pi.hThread != INVALID_HANDLE_VALUE) {
if (!CloseHandle(pi.hThread)) WARNING_GUI("Failed to close thread handle");
}
#else // Linux || macOS
*pid = reinterpret_cast<intptr_t>(p);
#endif
}
int rc = 0;
auto cpe = check_process_exit(p, &rc, 0); //1,-1 for wait infinitely
DBG_MSG("Launched Pid: " << p << " Exit code: " << cpe << " Return code: " << rc << "\n");
if (pid && (cpe != 1 || rc != 0))
{
*pid = 0;
}
if (cpe != 1)
{
if (*readpipe != -1) {
_close(*readpipe); *readpipe = -1;
}
if (*writepipe != -1) {
_close(*writepipe); *writepipe = -1;
}
if (showWarnings)
{
WARNING_GUI("Error in check_process_exit() while executing: "
<< procInf.path << " " << procInf.args << "\n");
}
return 1;
}
if (rc != 0)
{
if (*readpipe != -1) {
_close(*readpipe); *readpipe = -1;
}
if (*writepipe != -1) {
_close(*writepipe); *writepipe = -1;
}
if (showWarnings)
{
WARNING_GUI("launch_process(" << procInf.path << " " << procInf.args
<< ") failed with error code " << rc << "\n");
}
return 1;
}
return 0;
}
RdGlobalInfo::RdGlobalInfo(GhidraDec* pm) :
pluginConfigFile(get_user_idadir())
{
this->pm = pm;
pluginInfo.id = pluginID.data();
pluginInfo.name = pluginName.data();
pluginInfo.producer = pluginProducer.data();
pluginInfo.version = pluginVersion.data();
pluginInfo.url = pluginContact.data();
pluginInfo.freeform = pluginCopyright.data();
navigationActual = navigationList.end();
pluginConfigFile.append(pluginConfigFileName);
}
bool RdGlobalInfo::isAllDecompilation()
{
return !outputFile.empty();
}
bool RdGlobalInfo::isSelectiveDecompilation()
{
return !isAllDecompilation();
}
bool RdGlobalInfo::isDecompilerInSpecifiedPath() const
{
struct stat st;
return stat((decompilerExePath + decompilerExeName).data(), &st) == 0;
}
bool RdGlobalInfo::isDecompilerInSystemPath()
{
char buff[MAXSTR];
if (search_path(buff, sizeof(buff), decompilerExeName.c_str(), false))
{
struct stat st;
if (stat((std::string(buff) + "/" + decompilerExeName).data(), &st) == 0) {
decompilerExePath = std::string(buff) + "/";
return true;
}
}
return false;
}
bool RdGlobalInfo::isUseThreads() const
{
return useThreads;
}
void RdGlobalInfo::setIsUseThreads(bool f)
{
useThreads = f;
}
/**
* @return @c True if canceled, @c false otherwise.
*/
bool RdGlobalInfo::configureDecompilation()
{
#if defined(OS_WINDOWS)
decompilerExePath = ghidraPath + "/Ghidra/Features/Decompiler/os/win64/";
if (!isDecompilerInSpecifiedPath()) decompilerExePath = ghidraPath + "/Ghidra/Features/Decompiler/os/win_x86_64/";
#else
#if defined(OS_LINUX)
decompilerExePath = ghidraPath + "/Ghidra/Features/Decompiler/os/linux64/";
if (!isDecompilerInSpecifiedPath()) decompilerExePath = ghidraPath + "/Ghidra/Features/Decompiler/os/linux_x86_64/";
#else
decompilerExePath = ghidraPath + "/Ghidra/Features/Decompiler/os/osx64/";
if (!isDecompilerInSpecifiedPath()) decompilerExePath = ghidraPath + "/Ghidra/Features/Decompiler/os/mac_x86_64/";
#endif
#endif
if (isDecompilerInSpecifiedPath())
{
INFO_MSG("Found " << decompilerExeName << " at " << decompilerExePath
<< " -> plugin is properly configured.\n");
//decompilationCmd = decompilerExePath;
return false;
}
else if (isDecompilerInSystemPath())
{
INFO_MSG("Found " << decompilerExeName << " at system PATH "
<< decompilerExePath << " -> plugin is properly configured.\n");
//decompilationCmd = decompilerExePath;
return false;
}
else
{
WARNING_GUI("Decompilation is not properly configured.\n"
"The path to " << decompilerExeName << " must be provided in the configuration menu.");
auto canceled = pluginConfigurationMenu(*this);
if (canceled)
{
return canceled;
}
else
{
return configureDecompilation();
}
}
}
} // namespace idaplugin