CModule是frida提供的一种在javascript环境中直接运行C代码的机制

什么时候使用CModule?

  • 需要更高的执行效率

  • 处理复杂的数据结构和内存操作

  • 需要访问Frida内部API进行高级操作

  • 与Stalker结合使用进行指令级追踪

javaScript调用CModule函数

var cm = new CModule(`
    void hello(void) {
        frida_log("Hello World from CModule");
    }
`);

const hello = new NativeFunction(cm.hello, 'void', []);
hello();
var cm = new CModule(`
    int add(int a, int b) {
        return a + b;
    }
`);

// 这里就和frida调用navite函数是一样的,我们通过CModule构造了一个native函数然后用frida去调用它
const add = new NativeFunction(cm.add, 'int', ['int', 'int']);
const result = add(1, 2);
console.log(result);

CModule调用javaScript函数

var cm = new CModule(`
    extern void onMessage(const char * message);
    void hello(void) {
        onMessage("Hello World from CModule");
    }
`, {
    onMessage: new NativeCallback(function (messagePtr) {
        const message = messagePtr.readUtf8String();
        console.log('[*] Message: ', message);
    }, 'void', ['pointer'])
});

const hello = new NativeFunction(cm.hello, 'void', []);
hello();

extern表明这个函数或者变量是在外部声明的

NativeCallback创建一个c调用js的函数,NativeFunction是创建一个js调用c的函数

CModule与javaScript的数据交换

// 申请4字节的内存
var shared_memory = Memory.alloc(4);
// 往内存中写入值
shared_memory.writeS32(123);

var cm = new CModule(`
    #include <stdio.h>  // 定义printf
    extern volatile int shared_from_js;
    
    void hello(void) {
        printf("Value from JS: %d\\n", shared_from_js);

        // 修改shared_memory的实际值
        shared_from_js = 456;
    }
`, {
    "shared_from_js": shared_memory
});

const hello = new NativeFunction(cm.hello, 'void', []);
hello();
console.log('New Value: ' + shared_memory.readS32());  // 输出 456

CModule Hook示例(拦截open函数并修改文件路径)

// 创建一个 CModule 对象
var cm = new CModule(`
#include <gum/guminterceptor.h>
#include <string.h>
typedef struct _IcState IcState;
struct _IcState { // 类似 js hook native 时的 this 指针,用于传递上下文,可以自己添加想传递的数据
  const char *path;
};


static void frida_log (const char * format, ...);
extern void onMessage(const gchar * message); // 从 CModule 中调用 frida js 的例子

// CModule加载的时候执行一次
void init (void)
{
  frida_log("init()");
}

// CModule销毁的时候执行一次
void finalize (void)
{
  frida_log("finalize()");
}

void
onEnter (GumInvocationContext * ic) // for libc open
{
  const char *path;
  IcState *state;
  gpointer arg0;
  const char *redirect_path = NULL;

  state = GUM_IC_GET_INVOCATION_DATA (ic, IcState); // 获取上下文
  path = gum_invocation_context_get_nth_argument (ic, 0); // 获取第 0 个参数

  state->path = path;

  // 判断两个字符串是否相等如果想等则返回0
  if (strcmp(path, "/proc/self/status") == 0) {
    redirect_path = "/data/local/tmp/status.txt";
    frida_log("Redirecting: %s -> %s", path, redirect_path);
    state->path = redirect_path;
  }
  
    // 如果设置了重定向路径,则替换参数
    if (redirect_path != NULL) {
        // 替换第 1 个参数
        gum_invocation_context_replace_nth_argument(ic, 0, (gpointer)redirect_path);
    }

  frida_log("on_enter() open path=%s", path);
}

void
onLeave (GumInvocationContext * ic) // for libc open
{
  gpointer retval;
  IcState *state;

  retval = gum_invocation_context_get_return_value (ic); // 读取返回值
  // gum_invocation_context_replace_return_value: 替换返回值

  state = GUM_IC_GET_INVOCATION_DATA (ic, IcState); // 获取上下文
  const char *path = state->path;

  frida_log("on_leave() retval=%p open path=%s", retval, path);
}

static void
frida_log (const gchar * format,
           ...)
{
    gchar * message;
    va_list args;

    va_start (args, format);
    message = g_strdup_vprintf (format, args);
    va_end (args);

    onMessage (message); // 调用 frida 的 onMessage 函数

    g_free (message);
}
`, {
    onMessage: new NativeCallback(function(messagePtr) { 
        // 提供一个 NativeCallback 函数,用于调用 frida 的 onMessage 函数
        const message = messagePtr.readUtf8String();
        console.log('[*] message from cmodule: ' + message);
    }, 'void', ['pointer'])
});

console.log(JSON.stringify(cm));

var openImpl = Module.findExportByName(null, 'open');

console.log('[*] open: ' + openImpl);
Interceptor.attach(openImpl, cm);

CModule Hook示例(Hook open函数并修改返回值)

var cm = new CModule(`
#include <gum/guminterceptor.h>
#include <string.h>
#include <stddef.h>

typedef struct _IcState IcState;
struct _IcState {
    const char *path;
    int should_block;  // 记录文件是否需要阻止
};

static void frida_log(const char *format, ...);
extern void onMessage(const gchar *message);

void init(void) {
    frida_log("CModule initialized");
}

void finalize(void) {
    frida_log("CModule finalized");
}

void onEnter(GumInvocationContext *ic) {
    IcState *state = GUM_IC_GET_INVOCATION_DATA(ic, IcState);
    
    // 初始化状态
    state->should_block = 0;
    
    // 获取参数
    const char *path = gum_invocation_context_get_nth_argument(ic, 0);
    state->path = path;
    
    // 检查是否需要阻止
    if (path != NULL && strcmp(path, "/data/user/0/cn.binary.frida/files/test_hack.txt") == 0) {
        state->should_block = 1;
        frida_log("Will block: %s", path);
    }
    
     frida_log("on_enter() open path=%s", path);
}

void onLeave(GumInvocationContext *ic) {
    IcState *state = GUM_IC_GET_INVOCATION_DATA(ic, IcState);
    gpointer retval = gum_invocation_context_get_return_value(ic);
    
    if (state->should_block) {
        // gum_invocation_context_replace_return_value修改返回值
        // 这里将返回值改成-1文件将会打开失败
        gum_invocation_context_replace_return_value(ic, (gpointer)(gssize)-1);
        frida_log("BLOCKED: %s (returned -1)", state->path);
    } else {
        frida_log("on_enter() open path=%s retval=%p", state->path, retval);
    }
}

static void frida_log(const gchar *format, ...) {
    gchar *message;
    va_list args;
    
    va_start(args, format);
    message = g_strdup_vprintf(format, args);
    va_end(args);
    
    onMessage(message);
    g_free(message);
}
`, {
    onMessage: new NativeCallback(function(messagePtr) {
        console.log('[CModule]', messagePtr.readUtf8String());
    }, 'void', ['pointer'])
});

console.log(JSON.stringify(cm));

var openImpl = Module.findExportByName(null, 'open');

console.log('[*] open: ' + openImpl);
Interceptor.attach(openImpl, cm);