Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSVC support and sample project, frida debugging api updates #6347

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions Ghidra/Debug/Debugger-agent-frida/FridaNotes.txt
Expand Up @@ -14,6 +14,15 @@ Ghidra needs a dynamically-loadable version of libfrida-core.a which you can gen

Libfrida-core.so should then be added to the jna.library.path or put someplace like /usr/lib/x86_64-linux-gnu, where it will get picked up by Native.load().


- For Windows support:
download the latest fridacore dev kit for win x64 for example: frida-core-devkit-16.2.1-windows-x86_64.tar.xz
Extract the files to any folder
Copy the files from Ghidra/Debug/Debugger-agent-frida/src/main/cpp to that folder, overriding any existing.
Open frida_dll-msvc_build.sln and build in release mode for x64. This should give you a frida-core.dll.
Copy that dll to Ghidra/patch/win32-x86-64
Profit

- Frida Functionality
The most interesting bits of Frida are available as "methods" from the Objects Tree. For instance, if you select a function and hit "M", you will get a dialog with available methods. Selecting, for example, "intercept" will bring up a second dialog with the relevant parameters. For many of these, you will want to provide your own Javascript "on" functions, e.g. onEnter for the Interceptor. Stalking is available on Threads and the individual thread entries. Scan, protect, and watch functions are available on Memory. You can also redirect the output to GhidraScript, although this relies on a bit of a hack. If your Javascript "Name" parameter is something like "interpreter", prepend "interpreter<=" to the output from your Javascript, and the results will be passed to both the console and the script.

Expand Down
56 changes: 56 additions & 0 deletions Ghidra/Debug/Debugger-agent-frida/src/main/cpp/dbg.h
@@ -0,0 +1,56 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <time.h>
#include <string.h>
#include <stdarg.h>
#include "stdio.h"
#include <process.h>
#include <share.h>

#define dlog(format, ...) _dbglog(0, __DbgLogNorm, __LINE__,__FILE__,__func__, format, ## __VA_ARGS__)

static FILE* logfile;
typedef enum { __DbgLogNorm, __DbgLogFunc, __DbgLogFatal } __DbgLogType_t;//dbglogfunc means we are logging a call we commented out or norm trace
void _fdbglog_help(FILE* f, const char* prefix_str, const char* format, va_list args) {
fputs(prefix_str, f);
vfprintf(f, format, args);
fputs("\n", f);
fflush(f);

}
void _fdbglog_msg(const char* prefix_str, const char* format, va_list args) {
//char buffer[1024];
//OutputDebugString(prefix_str);
//vsprintf_s(buffer, sizeof(buffer), format, args);
//OutputDebugString(buffer);
//OutputDebugString("\n");
}
void dbgloginit() {
logfile = _fsopen("ghdbg.log", "w", _SH_DENYNO);
}
int _dbglog(int retval, __DbgLogType_t LogType, int lineno, const char* file, const char* func, const char* format, ...) {

va_list args;
va_start(args, format);
time_t rawtime;
struct tm timeinfo;
const char* file_end = strrchr(file, '\\');
if (file_end != NULL)
file = file_end + 1;

time(&rawtime);
localtime_s(&timeinfo, &rawtime);
char prefix_str[150];
sprintf_s(prefix_str, sizeof(prefix_str), "%2d:%02d:%02d %s:%d::%s %s", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, file, lineno, func, LogType == __DbgLogFatal ? "FLOG " : "");

//_fdbglog_help(stderr, prefix_str, format, args);

_fdbglog_help(logfile, prefix_str, format, args);
_fdbglog_msg(prefix_str, format, args);
//DbgPrint();
va_end(args);
if (LogType == __DbgLogFatal)
exit(-99);
return retval;
}

271 changes: 271 additions & 0 deletions Ghidra/Debug/Debugger-agent-frida/src/main/cpp/frida-core-example.c
@@ -0,0 +1,271 @@
/*
* To build, set up your Release configuration like this:
*
* [Runtime Library]
* Multi-threaded (/MT)
*
* Visit https://frida.re to learn more about Frida.
*/

#include "frida-core.h"

#include <stdlib.h>
#include <string.h>

static void on_detached(FridaSession* session, FridaSessionDetachReason reason, FridaCrash* crash, gpointer user_data);
static void on_message(FridaScript* script, const gchar* message, GBytes* data, gpointer user_data);
static void on_signal(int signo);
static gboolean stop(gpointer user_data);

static GMainLoop* loop = NULL;
#include "ghidra_wrapper.c"
void ourtest() {
GH_frida_init();
guint target_pid;
FridaDeviceManager* manager;
GError* error = NULL;
FridaDeviceList* devices;
gint num_devices, i;
FridaDevice* local_device;
FridaSession* session;

loop = g_main_loop_new(NULL, TRUE);
manager = GH_frida_device_manager_new();
devices = GH_frida_device_manager_enumerate_devices_sync(manager, NULL, &error);
local_device = NULL;
num_devices = GH_frida_device_list_size(devices);
for (i = 0; i != num_devices; i++)
{
FridaDevice* device = GH_frida_device_list_get(devices, i);

g_print("[*] Found device: \"%s\"\n", GH_frida_device_get_name(device));

if (frida_device_get_dtype(device) == FRIDA_DEVICE_TYPE_LOCAL)
local_device = g_object_ref(device);

g_object_unref(device);
}
g_assert(local_device != NULL);

GH_frida_unref(devices);
devices = NULL;

session = GH_frida_device_attach_sync(local_device, target_pid, NULL, NULL, &error);
if (error == NULL)
{
FridaScript* script;
FridaScriptOptions* options;

g_signal_connect(session, "detached", G_CALLBACK(on_detached), NULL);
if (frida_session_is_detached(session))
return;

g_print("[*] Attached\n");

options = frida_script_options_new();
GH_frida_script_options_set_name(options, "example");
GH_frida_script_options_set_runtime(options, FRIDA_SCRIPT_RUNTIME_QJS);

//script = GH_frida_session_create_script_sync(session, script_src, options, NULL, &error);
GH_frida_session_enable_debugger_sync(session, 42424, NULL, &error);
script = _int_script;

g_assert(error == NULL);

g_print("[*] Script created\n");

g_assert(error == NULL);

g_clear_object(&options);

g_signal_connect(script, "message", G_CALLBACK(on_message), NULL);

frida_script_load_sync(script, NULL, &error);
g_assert(error == NULL);

g_print("[*] Script loaded\n");


if (g_main_loop_is_running(loop))
g_main_loop_run(loop);
}
}

int
main(int argc,
char* argv[])
{
guint target_pid;
FridaDeviceManager* manager;
GError* error = NULL;
FridaDeviceList* devices;
gint num_devices, i;
FridaDevice* local_device;
FridaSession* session;
ourtest();
return 0;

frida_init();

if (argc != 2 || (target_pid = atoi(argv[1])) == 0)
{
g_printerr("Usage: %s <pid>\n", argv[0]);
return 1;
}

loop = g_main_loop_new(NULL, TRUE);

signal(SIGINT, on_signal);
signal(SIGTERM, on_signal);

manager = frida_device_manager_new();

devices = frida_device_manager_enumerate_devices_sync(manager, NULL, &error);
g_assert(error == NULL);

local_device = NULL;
num_devices = frida_device_list_size(devices);
for (i = 0; i != num_devices; i++)
{
FridaDevice* device = frida_device_list_get(devices, i);

g_print("[*] Found device: \"%s\"\n", frida_device_get_name(device));

if (frida_device_get_dtype(device) == FRIDA_DEVICE_TYPE_LOCAL)
local_device = g_object_ref(device);

g_object_unref(device);
}
g_assert(local_device != NULL);

frida_unref(devices);
devices = NULL;

session = frida_device_attach_sync(local_device, target_pid, NULL, NULL, &error);
if (error == NULL)
{
FridaScript* script;
FridaScriptOptions* options;

g_signal_connect(session, "detached", G_CALLBACK(on_detached), NULL);
if (frida_session_is_detached(session))
goto session_detached_prematurely;

g_print("[*] Attached\n");

options = frida_script_options_new();
frida_script_options_set_name(options, "example");
frida_script_options_set_runtime(options, FRIDA_SCRIPT_RUNTIME_QJS);

script = frida_session_create_script_sync(session,
"Interceptor.attach(Module.getExportByName('kernel32.dll', 'CreateFileW'), {\n"
" onEnter(args) {\n"
" console.log(`[*] CreateFileW(\"${args[0].readUtf16String()}\")`);\n"
" }\n"
"});\n"
"Interceptor.attach(Module.getExportByName('kernel32.dll', 'CloseHandle'), {\n"
" onEnter(args) {\n"
" console.log(`[*] CloseHandle(${args[0]})`);\n"
" }\n"
"});",
options, NULL, &error);
g_assert(error == NULL);

g_clear_object(&options);

g_signal_connect(script, "message", G_CALLBACK(on_message), NULL);

frida_script_load_sync(script, NULL, &error);
g_assert(error == NULL);

g_print("[*] Script loaded\n");

if (g_main_loop_is_running(loop))
g_main_loop_run(loop);

g_print("[*] Stopped\n");

frida_script_unload_sync(script, NULL, NULL);
frida_unref(script);
g_print("[*] Unloaded\n");

frida_session_detach_sync(session, NULL, NULL);
session_detached_prematurely:
frida_unref(session);
g_print("[*] Detached\n");
}
else
{
g_printerr("Failed to attach: %s\n", error->message);
g_error_free(error);
}

frida_unref(local_device);

frida_device_manager_close_sync(manager, NULL, NULL);
frida_unref(manager);
g_print("[*] Closed\n");

g_main_loop_unref(loop);

return 0;
}

static void
on_detached(FridaSession* session,
FridaSessionDetachReason reason,
FridaCrash* crash,
gpointer user_data)
{
gchar* reason_str;

reason_str = g_enum_to_string(FRIDA_TYPE_SESSION_DETACH_REASON, reason);
g_print("on_detached: reason=%s crash=%p\n", reason_str, crash);
g_free(reason_str);

g_idle_add(stop, NULL);
}

static void
on_message(FridaScript* script,
const gchar* message,
GBytes* data,
gpointer user_data)
{
JsonParser* parser;
JsonObject* root;
const gchar* type;

parser = json_parser_new();
json_parser_load_from_data(parser, message, -1, NULL);
root = json_node_get_object(json_parser_get_root(parser));

type = json_object_get_string_member(root, "type");
if (strcmp(type, "log") == 0)
{
const gchar* log_message;

log_message = json_object_get_string_member(root, "payload");
g_print("%s\n", log_message);
}
else
{
g_print("on_message: %s\n", message);
}

g_object_unref(parser);
}

static void
on_signal(int signo)
{
g_idle_add(stop, NULL);
}

static gboolean
stop(gpointer user_data)
{
g_main_loop_quit(loop);

return FALSE;
}