当前位置:网站首页>【frida实战】“一行”代码教你获取WeGame平台中所有的lua脚本

【frida实战】“一行”代码教你获取WeGame平台中所有的lua脚本

2022-07-07 06:58:00 夜猫逐梦

导读

开发环境

版本号描述
操作系统Win11-21H2内部版本号22000.588
PythonPython3.7.1
frida.exe15.0.18

预备知识

luaL_loadbuffer

函数原型(https://www.lua.org/manual/5.1/manual.html#luaL_loadbuffer):

int luaL_loadbuffer (lua_State *L,				// Lua句柄
                     const char *buff,			// lua脚本
                     size_t sz,					// lua脚本字节数
                     const char *name);			// 标记(通过分析可以发现这里是文件名)

将缓冲区加载为 Lua 块。此函数用于加载lua_load指向的缓冲区中的块。 name是块名称,用于调试信息和错误消息。

WeGame中使用的是Lua51.dll,它导出了两个函数luaL_loadbufferluaL_loadbufferx,通过IDA分析可以猜测,luaL_loadbufferx只是比luaL_loadbuffer多了个参数a5,其他参数都是一样的。
在这里插入图片描述

frida拦截器Interceptor

frida的Interceptor模块中,attach函数原型为Interceptor.attach(target, callbacks[, data]),实现了对参数target进行hook的操作,下面是简单的参数分析:

  • target: 这是一个 NativePointer,指定您想要拦截调用的函数的地址。我们可以通过Module.getExportByName()来获取可执行文件的导出函数作为该参数,frida会自动处理。
  • callbacks: 顾名思义,这是hook的回调函数集合,里面包含了hook前和hook后两个回调函数。
    • onEnter(args): 该回调函数的参数args为hook函数的参数数组,每个参数都是一个NativePointer对象,我们可以在这里获取参数信息,或者篡改参数内容。
    • onLeave(retval): 该调用是执行完目标函数后的处理,参数retval是一个NativePointer对象,是目标函数的返回值,我们可以修改该指针内容达到hook的目的。
  • data: 可选参数,暂时忽略掉。

示例:

Interceptor.attach(Module.getExportByName('libc.so', 'read'), {
    
  onEnter(args) {
    
    this.fileDescriptor = args[0].toInt32();

	// this上下文信息:包含寄存器信息context、返回值地址returnAddress、线程ID threadId等
    console.log('Context information:');
    console.log('Context : ' + JSON.stringify(this.context));
    console.log('Return : ' + this.returnAddress);
    console.log('ThreadId : ' + this.threadId);
    console.log('Depth : ' + this.depth);
    console.log('Errornr : ' + this.err);
  },
  onLeave(retval) {
    
    if (retval.toInt32() > 0) {
    
      /* do something with this.fileDescriptor */
    }
  }
});

ps: callbacks是一个对象,frida实现中,会在初始化的时候,给该对象填充上下文信息信息,这也就是为什么示例中的this可以访问context、returnAddress、threadId这些内容。

ps2: 之前一直纠结onEnter中生成的变量,怎么在onLeave中使用,其实直接通过this就可以了this.tempVal=333;。这都是对js的基础理解和应用。

NativeFunction创建目录

frida调用dll函数,通过NativeFunction创建一个函数对象,设置响应的参数和返回值。

本文使用的是WinExec函数调用了cmd命令中的mkdir指令来实现创建目录,具体实现看下面的代码。

ps: 也可以使用c函数中的system,其原型如下:

分析思路

挂起启动WeGame

为了保证hook所有的lua,需要挂起启动WeGame,使用命令frida -f 应用程序全路径即可挂起启动目标进程,命令行示例:

D:\Python\Python371\Scripts\frida.exe -f "G:\Program Files (x86)\WeGame\wegame.exe"

WeGame需要管理员权限启动,否则会报0x000002e4的启动失败错误。
在这里插入图片描述

hook函数luaL_loadbufferx保存所有js

var fnWinExec = new NativeFunction(
	Module.findExportByName('Kernel32.dll', 'WinExec'),
	'int', 
	['pointer', 'int'],
	'stdcall'
);

function ez_fnWinExec(jsStr) {
    
    console.log( 'ez_fnWinExec: ', jsStr );
	
	var cStrPointer = Memory.allocUtf8String(jsStr);
	fnWinExec(cStrPointer, 0);
}

Interceptor.attach(Module.getExportByName('Lua51.dll', 'luaL_loadbufferx'), {
    
  onEnter(args) {
    
  	// 不可以使用数组解构赋值!!!
  	// var [_,a,b]=[0,1,3]
    // var [L, buff, sz, name] = args;
    var buff = args[1];
    var sz = args[2];
    var name = args[3];
    console.log( name.readCString(), sz.toInt32(), buff );

	// 过滤非lua文件
	if (!name.readCString().endsWith('.lua')){
    
		return;
	}
	
	var pth = 'D:\\_TMP\\wegame\\' + name.readCString();
	// 创建文件夹
	var dir_ = pth.substr(0, pth.lastIndexOf('\\')+1);
	ez_fnWinExec('cmd.exe /c mkdir '+ dir_);
    // 保存文件
    var f = new File(pth, 'wb');
    var data = buff.readByteArray(sz.toInt32());
    f.write(data);
    f.close();
  },
  onLeave(retval) {
    
  }
});

一开始想的是不需要调用函数WinExec,优化一下可以写成“一行”代码的,后来发现多级目录,得自己创建目录,又加上了一堆的代码。。。
所以,就不再是“一行”代码实现了。

参考资料

**ps:**文章中内容仅用于技术交流,请勿用于违规违法行为。

原网站

版权声明
本文为[夜猫逐梦]所创,转载请带上原文链接,感谢
https://blog.csdn.net/kinghzking/article/details/125590584