当前位置: 首页 > news >正文

辽宁建设工程信息网官方网站建网站需要费用

辽宁建设工程信息网官方网站,建网站需要费用,公司做网站需要服务器吗,酒仙网技术开发与网站建设方面对一手游的自定义 luajit 字节码的研究 前言 最近闲下来之后无聊研究起了一个unity手游 大量使用了 lua #xff08;或者说就是 lua 写的 #xff09; 看到网上已有的一些针对方案 都觉得太不方便 于是深入研究了一下 他自定义的 luajit 情况研究 首先 这是一个 unity的 传…对一手游的自定义 luajit 字节码的研究 前言 最近闲下来之后无聊研究起了一个unity手游 大量使用了 lua 或者说就是 lua 写的 看到网上已有的一些针对方案 都觉得太不方便 于是深入研究了一下 他自定义的 luajit 情况研究 首先 这是一个 unity的 传统手游 这里就跳过较为前期的部分 像是 libtersafe . libbugly . libcri_ware 这些都是老熟人了 都跳过 unity 的 lua 通信方案 对于 unity游戏来说 特别是商业手游 热更新几乎是必须的 由此诞生很多方案 这里简单介绍几个重点 hybridclr c# 原生热更新xlua 代表新兴 lua 框架有详细的文档 tolua 代表老一代 lua 框架 luac 与 luajit 的关系 以下来自 gpt LuaC 和 LuaJIT 都是与 Lua 编程语言相关的工具但它们的用途和性能特点有所不同。 LuaC: LuaC 是 Lua 的标准字节码编译器。它是 Lua 解释器的一部分用于将 Lua 源代码编译成字节码。这个过程通常是为了提高代码的加载速度和执行效率。LuaC 生成的字节码可以在任何 Lua 虚拟机上运行确保了跨平台的兼容性。它是 Lua 发行版的标准组成部分通常用于开发环境或当脚本需要预编译以隐藏源代码时。 LuaJIT: LuaJIT 是一个独立的 Lua 编译器它使用即时编译JIT技术来提高 Lua 代码的执行速度。LuaJIT 能够在运行时将 Lua 字节码转换成机器码这通常可以显著提高性能。LuaJIT 与 Lua 5.1 版本高度兼容但也引入了一些自己的扩展和优化。它特别适合于性能敏感的应用如高性能服务器、游戏、或实时系统。LuaJIT 的性能通常远超标准 Lua 解释器尤其是在复杂或计算密集型任务中。 总的来说LuaC 更多地关注于代码的移植性和标准化而 LuaJIT 更专注于提高执行效率和性能。用户可以根据自己的需要选择使用 LuaC 还是 LuaJIT。 luac 和luajit的字节码一致吗 api呢 LuaC 和 LuaJIT 在字节码和 API 方面存在一些差异 字节码兼容性: LuaC 和 LuaJIT 生成的字节码不完全一致。LuaC 遵循 Lua 官方的标准为特定版本的 Lua如 Lua 5.1, Lua 5.2 等生成相应的字节码。LuaJIT 也能够编译为字节码但它的字节码格式是为 JIT 编译器优化的。 由于 LuaJIT 主要与 Lua 5.1 兼容它的字节码在某种程度上与 Lua 5.1 的字节码相似。然而由于 JIT 优化的原因LuaJIT 生成的字节码可能在某些情况下与标准 Lua 字节码不兼容。 这意味着用 LuaC 编译的字节码可能无法在 LuaJIT 上正确运行反之亦然。 API 兼容性: 在 API 层面LuaJIT 与 Lua 5.1 高度兼容。它支持 Lua 5.1 的全部标准 API并且引入了一些自己的扩展。这些扩展主要是为了提高性能和扩展功能。 如果你的 Lua 代码是针对 Lua 5.1 API 编写的那么它在 LuaJIT 上应该可以无缝运行。但如果你使用了 LuaJIT 的特定扩展那么这部分代码可能无法在标准 Lua 解释器上运行。 综上所述虽然 LuaC 和 LuaJIT 在很多方面是兼容的但在字节码和特定 API 的使用上存在一些差异。如果你的项目需要在不同环境中移植需要特别注意这些差异。对于大多数应用来说这些差异并不会造成显著问题但在高性能或特定的应用场景下选择合适的工具是非常重要的。 准备环境 分析之前 我们现确定一下目标 让使用 luajit 的应用能执行我们提供的 lua 代码 luajit 源码 库源码 xlua , tolua 等等都是开源的 而且区别主要在和 c#对接的部分 对于我们需要研究的部分 差别不大 vs 用于分析 c 源码和 c# 源码 vsc 用于分析编写 lua 和 js / ts python ( 自动化工作流 frida ) node ( 编译 ts ) 010editor 分析二进制 lua bc ida ( 分析修改后的luajit ) 最好吧 unity 也带上 方便需要问题可以用 unity 实际测试一下 手游分析 app 分析 在 app 中 我们可以直接看到 libxlua.so , libil2cpp.so frida / frida-il2cpp 直接用 frida 为了方便使用 frida-il2cpp 我们创建一个 node 项目 添加库 并配置 ts 环境 1 2 3 4 5 types/node types/frida-gum frida-compile frida-java-bridge frida-il2cpp-bridge 添加命令 frida-compile src/index.ts -o dist/_agent.js -c frida -Uf xxx -l dist/_agent.jsfrida js 运行在手机上 运行麻烦 使用 ts 可以避免语法错误 并享受 js 生态 在 index.ts 中开始 hook 我们先使用Il2Cpp.perform((){console.log(OK)}) 确认il2cpp 能够被正常 hook 然后我们就可以使用 il2cpp 获取由元数据的来的c#代码函数签名信息 const destination ${Il2Cpp.applicationDataPath}/${dirName};for (const assembly of Il2Cpp.Domain.assemblies) {const path ${destination}/${assembly.name}.csconst file new File(path, w);for (const klass of assembly.image.classes) {file.write(${klass}\n\n);}file.flush();file.close();}这一步其实和文章主题关系不大 这里的手游 c# 层也没有特别的内容 说明在 c#层没有修改内容 接着我们看向 luaEnv 类 这里就有由 lua 框架映射而来的多数 lua基础 api 在 so 库中也能看到接口 这里我们直接尝试使用 DoString 方法来执行我们提供的 lua 代码 this.AssemblyCSharp Il2Cpp.Domain.assembly(Assembly-CSharp).image; this.AssemblyCSharp.class(LuaEnv).method(DoString).implementation function (bytes: Il2Cpp.ArrayIl2Cpp.Object, name: Il2Cpp.String) { if(name ! undefined name?.contentmain.lua){this.method(DoString).Invoke(Il2Cpp.String.from(print(我的 lua 代码。。。)),Il2Cpp.String.from(test.lua)) } }faq 我怎么知道是 main.lua 你可以先打印他们的名字啊 faq 为什么要在执行 main.lua 之后执行 因为这样才能获取到他代码注册的内容 题外话 记得去把 log hook 了 才能看到输出 frida-il2cpp 提供了 log Il2Cpp.installExceptionListener(all); lua 框架大概率也有logger 可以 hook lua框架的 logger 将输出复一份到 frida上面来 然而 很神奇的事情发生了 程序直接崩溃了 在反复排除了各种东西之后 不得不打开 ida 分析 so 库 好在lua 框架是讲他自己的代码链接到 luajit 上的 也就是说我们可以对照 luajit 的源码 题外话 win 上可以使用 msvc 编译 luajit 参考 luajit 官网教程 记得把 -O2 改成 -Od 开启 debug 模式 直接定位到核心的 lua 代码加载函数 lua_loadx - cpparser 手游的 so 库里面的 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 *(a1[10] 196LL) -1;   v5 (loc_43480)();   if ( !*(a3 136) )   {     if ( v5 )       goto LABEL_4;     return 0LL;   }   if ( v5 )   {     if ( strchr(*(a3 136), b) )     { LABEL_4:       v6 sub_45740(a3);       v7 sub_351F0(a1, v6, a1[9]);       v8 a1[5];       a1[5] v8 1;       *v8 v7 | 0xFFFB800000000000LL;       return 0LL;     }     goto LABEL_8;   }   if ( strchr(*(a3 136), t) )     return 0LL; LABEL_8:   v10 a1[5];   a1[5] v10 1;   *v10 sub_3142C(a1, 2100LL) | 0xFFFD800000000000LL;   v11 sub_3123C(a1, 3LL);   return lua_loadx(v11, v12, v13, v14, v15); } 编译的 so 库里面的 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 *(a1[10] 196LL) -1;   v5 (loc_426F8)();   if ( !*(a3 136) )   {     if ( v5 )       goto LABEL_4;     goto LABEL_6;   }   if ( !v5 )   {     if ( !strchr(*(a3 136), t) )       goto LABEL_9; LABEL_6:     v6 sub_49D8C(a3);     goto LABEL_7;   }   if ( strchr(*(a3 136), b) )   { LABEL_4:     v6 (loc_4A8A8)(a3); LABEL_7:     v7 sub_349F0(a1, v6, a1[9]);     v8 a1[5];     a1[5] v8 1;     *v8 v7 | 0xFFFB800000000000LL;     return 0LL;   } LABEL_9:   v10 a1[5];   a1[5] v10 1;   *v10 sub_30C08(a1, 2100LL) | 0xFFFD800000000000LL;   sub_30A10(a1, 3LL);   v12 v11;   v15 v13;   if ( !feof(*v13) (v14  fread(v15 1, 1uLL, 0x400uLL, *v15), (*v12 v14) ! 0) )     result v15 1;   else     result 0LL;   return result; } 源码 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 // lj_load.c LUA_API int lua_loadx(lua_State *L, lua_Reader reader, void *data,               const char *chunkname, const char *mode) {   LexState ls;   int status;   ls.rfunc reader;   ls.rdata data;   ls.chunkarg chunkname ? chunkname : ?;   ls.mode mode;   lj_buf_init(L, ls.sb);   status lj_vm_cpcall(L, NULL, ls, cpparser);   lj_lex_cleanup(L, ls);   lj_gc_check(L);   return status; } static TValue *cpparser(lua_State *L, lua_CFunction dummy, void *ud) {   LexState *ls (LexState *)ud;   GCproto *pt;   GCfunc *fn;   int bc;   UNUSED(dummy);   cframe_errfunc(L-cframe) -1;  /* Inherit error function. */   bc lj_lex_setup(L, ls);   if (ls-mode !strchr(ls-mode, bc ? b : t)) {     setstrV(L, L-top, lj_err_str(L, LJ_ERR_XMODE));     lj_err_throw(L, LUA_ERRSYNTAX);   }   pt bc ? lj_bcread(ls) : lj_parse(ls);   fn lj_func_newL_empty(L, pt, tabref(L-env));   /* Dont combine above/below into one statement. */   setfuncV(L, L-top, fn);   return NULL; } 题外话 手游的 so 库 开了优化 一些没有注明要内联的函数 也被内联了 看着会有些不一样 不难发现 他直接少了 t 选项 查阅 lua 官网 可知 lua加载代码分为 b (从字节码加载) t (从文本加载 ) 而进一步的分析发现 这个手游直接把 t 模式整个删了没绷住 随后进一步的对比分析 发现不仅仅是加载字节码的模式 而是真个字节码都被加密了 下面的章节会详细介绍 已有的破解分析 在网上搜索时 发现了另一种思路 即通过 lua 暴露的 c api 来控制 lua 1 2 3 4 5 lua_gettop lua_pop lua_pushvalue lua_pcall lua_pushstring 这样也可变相的实现控制逻辑 而且由于 这些暴露的接口对于框架交互来说是必须的 也不用太担心这里会被做手脚 但是这个方案只能进行简单的更改 对于外挂之类的来说可能比较有用 不过这里也提供另类的思路 由于 lua 的特殊性 lua 运行时本身是无状态的 理论上我们可以将 lua_state 直接交给另一个 lua 虚拟机来执行 不过这个方案并不能运用在这里 这里由于是游戏 有大量的网络请求 涉及到协程 lua 会将 协程信息放在 lua 共有的 global 段 中 这样的话 就不是无状态了 另一种思路是 修改一个 lua 虚拟机 将其最终执行的命令记录并转发给我们这里的 lua 虚拟机 得益于 lua 本身的简单 这并非不可能 像 fengari 库 直接在 原始 js 中实现了 lua vm 如果对他进行一下修改后集成在 frida 中 也许可以实现 原始 luajit bytecode 分析 最后 我们还是老老实实的分析他加密后的 bytecode 不过在分析加密的之前 我们得先搞清楚原始的 题外话 大佬提供的 010 的 bt 模板 在我这里似乎有版本兼容问题 没有函数 parentof() 即获取节点的父节点 不过这个可以直接在父节点处 把父节点自己作为参数传给子节点 来绕过这个函数 自定义 bytecode 分析 与 对应实现 为了更好的分析游戏的 lua bytecode 这里我们需要找一个游戏中有的加密过后的文件 同时我们也有源码的 lua 文件加密前的文件 这样的文件我们可以去找框架的 lua 代码 让后使用 frida hook loadbuffer 函数 并判断名称 然后 dump 下来 顺带 我们打开一个 python 并编写 这里我们进行超级多开 ida - 目标游戏的 so 库ida - 自编译的 so 库vs - luajit 源码vsc - python 代码 编写加解密脚本010 - 游戏的lua字节码010 - 原始的 lua字节码 我们可知原始 luajit 字节码 的结构 GlobalHeader 头部 多个 Proto 函数体 header 头部 sizeflagsarg_countframesizeupvalue_countcomplex_constants_countnumeric_constants_countinstructions_count insts 指令 4 个字节一组 详见上一张大佬的文章 constants 常量 upvalue complex CHILD 0 TAB 1 tab 还会进一步细分为键和值 或仅值 lua特色I64 2 U64 3 COMPLEX 4 STR 5 大于 5 的都是字符串 字符串长度为 值-5 numberic 最后以一个 size 为 0 的 proto 结束 而 luajit 解析这是 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 GCproto *lj_bcread(LexState *ls) {   lua_State *L ls-L;   lj_assertLS(ls-c BCDUMP_HEAD1, bad bytecode header);   bcread_savetop(L, ls, L-top);   lj_buf_reset(ls-sb);   /* Check for a valid bytecode dump header. */   if (!bcread_header(ls))     bcread_error(ls, LJ_ERR_BCFMT);   for (;;) {  /* Process all prototypes in the bytecode dump. */     GCproto *pt;     MSize len;     const char *startp;     /* Read length. */     if (ls-p ls-pe ls-p[0] 0) {  /* Shortcut EOF. */       ls-p;       break;     }     bcread_want(ls, 5);     len bcread_uleb128(ls);     if (!len) break;  /* EOF */     bcread_need(ls, len);     startp ls-p;     pt lj_bcread_proto(ls);     if (ls-p ! startp len)       bcread_error(ls, LJ_ERR_BCBAD);     setprotoV(L, L-top, pt);     incr_top(L);   }   if ((ls-pe ! ls-p !ls-endmark) || L-top-1 ! bcread_oldtop(L, ls))     bcread_error(ls, LJ_ERR_BCBAD);   /* Pop off last prototype. */   L-top--;   return protoV(L-top); } 其中 bc 开头的函数都是读取对应部分的函数 重点在于lj_bcread_proto 这个函数 包含了分析 proto 这个重要结构的代码 由于编译器将很多子函数的代码内联了进来 导致这个函数很大 不过不要怕 我们有原程序进行对比 这里就不完整将函数代码贴上来了 第一部分 读取 proto 头部 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ls_p *(ls 32);  *(ls 32) ls_p 1;  ph_b1_framesize *ls_p;  *(ls 32) ls_p 2;  ph_b2 ls_p[1];  *(ls 32) ls_p 3;  flags ph_b2 ^ ph_b1_framesize;  ph_b3 ls_p[2];  *(ls 32) ls_p 4;  v6 (ls 32);  numparams ph_b3 ^ ph_b2 ^ ph_b1_framesize;  ph_b4 ls_p[3];  sizekn bcread_uleb128((ls 32));  sizeuv ph_b4 ^ numparams;  sizekgc bcread_uleb128(v6);  sizebc_1 bcread_uleb128(v6);  sizebc sizebc_1 1; 不难发现 他进行了异或 将函数头的参数互相异或了 并修改了部分参数的位置 这里我们直接让 gpt 给出逆函数 当然让我总结一下我们找到 (fl) 函数反函数的过程 问题描述: 您提供了一个名为 (fl) 的函数它对一个四元素的元组 (t)具体为 (0, 1, 2, 3)进行一系列异或XOR操作生成一个新的四元素元组。您询问如何找到 (fl) 函数的反函数即如何从 (fl) 函数的输出恢复出原始输入。 (fl) 函数的分析: (fl) 函数通过以下方式操作 (o1 t[0])(o2 t[1] \oplus o1)(o3 t[2] \oplus o2)(o4 t[3] \oplus o3) 逆函数的构建: 我们尝试了几种不同的方法来构建这个逆函数关键在于理解 XOR 操作的自反性和如何正确地逆向每一步操作。 最终解决方案: 经过一系列尝试和错误我们找到了正确的逆函数。这个逆函数通过以下方式恢复原始 (t) 值 从 (o4) 开始因为 (o4 t[3] \oplus o3)所以 (t3 o4 \oplus o3)。依此类推我们逆向每一步操作最终恢复出 (t0, t1, t2, t3)。 验证和结果: 逆函数成功地验证了它能够准确地从 (fl) 函数的输出恢复出原始输入 (0, 1, 2, 3)。 1 2 3 4 5 6 7 8 9 10 11 12 13 def fl(t):     o1  t[0]     o2  t[1] ^ o1     o3  t[2] ^ o2     o4  t[3] ^ o3     return o1 , o2 , o3 , o4 def fl_inverse(t):     t3  t[3] ^ t[2]     t2  t[2] ^ t[1]     t1  t[1] ^ t[0]     t0  t[0]      return t0, t1, t2, t3 这样 我们可以先编写proto 的 python 1 2 3 4 5 6 7 8 9 10 11 12 t  (self.framesize[0],self.flags[0],self.argcount[0],self.upvc[0])             a1 , a2 , a3 , a4  fl_inverse(t)             assert tfl((a1 , a2 , a3 , a4))             WriterUtil.write_byte(stream,a1)             WriterUtil.write_byte(stream,a2)             WriterUtil.write_byte(stream,a3)             WriterUtil.write_byte(stream,a4)             WriterUtil.ULeb128Write(stream,self.ncc)             WriterUtil.ULeb128Write(stream,self.ccc)             WriterUtil.ULeb128Write(stream, self.instc) 而对于指令 1 2 3 4 5 6 7 8 9 do     {       v21 *(v18 3);       v22 v20 ^ v21;       v23 *(v18 - 3);       *(v18 - 1) v22;       *(v18 - 3) ~v23;     }     while ( v20 ! sizebc_1 ); 结合 010 我们可以发现 对于指令的 4 个数 op , a1 ,a2, a3 首先 他 opcode 都更改了 但是是一一对应的其次 根据代码 不难 发现 a1 为 ~a10xffa2 不变a3 为 a3^idx0xff 其中 idx 为指令个数 对于指令 好在他虽然打乱顺序了 但是没有完全打乱 他只是按照 lj_bc.h 中指令的大块打乱了 相邻的指令依然是连续的 结合之后获取的更多的 lua 样本和其他模板的解密 指令基本能够恢复 就算不能完全恢复 常用指令也能够恢复 对于达成目标并不影响 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class LuaJitInstruction:     def __init__(self,stream,idx) - None:         self.idx  idx         self.op  ReaderUtil.read_byte(stream)         self.a1  ReaderUtil.read_byte(stream)         self.a2  ReaderUtil.read_byte(stream)         self.a3  ReaderUtil.read_byte(stream)           def dump(self,stream):         if not NEED_OBUF:             WriterUtil.write_byte(stream,self.op)             WriterUtil.write_byte(stream,self.a1)             WriterUtil.write_byte(stream,self.a2)             WriterUtil.write_byte(stream,self.a3)         else:             WriterUtil.write_byte(stream,OP().OPtoOBOP(self.op[0]))             WriterUtil.write_byte(stream,~self.a1[0]0xff)             WriterUtil.write_byte(stream,self.a2)             WriterUtil.write_byte(stream,(self.a3[0]^self.idx)0xff) 接下来对于字符串 我们能在 ida 中看到一串很恐怖的大量代码 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 if ( _loop_next_len - 6 14 )           {             v40 0; LABEL_28:             ls_p_1[v40 / 0x10].n128_u8[0] ~ls_p_1[v40 / 0x10].n128_u8[0] ^ v40;             if ( loop_len v40 1 )             {               ls_p_1[v40 / 0x10].n128_u8[1] ~ls_p_1[v40 / 0x10].n128_u8[1] ^ (v40 1);               if ( loop_len v40 2 )               {                 ls_p_1[v40 / 0x10].n128_u8[2] ~ls_p_1[v40 / 0x10].n128_u8[2] ^ (v40 2);                 if ( v40 3 loop_len )                 {                   ls_p_1[v40 / 0x10].n128_u8[3] ~ls_p_1[v40 / 0x10].n128_u8[3] ^ (v40 3);                   if ( v40 4 loop_len )                   {                     ls_p_1[v40 / 0x10].n128_u8[4] ~ls_p_1[v40 / 0x10].n128_u8[4] ^ (v40 4);                     if ( loop_len v40 5 )                     {                       ls_p_1[v40 / 0x10].n128_u8[5] ~ls_p_1[v40 / 0x10].n128_u8[5] ^ (v40 5);                       if ( loop_len v40 6 )                       {                         ls_p_1[v40 / 0x10].n128_u8[6] ~ls_p_1[v40 / 0x10].n128_u8[6] ^ (v40 6);                         if ( loop_len v40 7 )                         {                           ls_p_1[v40 / 0x10].n128_u8[7] ~ls_p_1[v40 / 0x10].n128_u8[7] ^ (v40 7);                           if ( loop_len v40 8 )                           {                             ls_p_1[v40 / 0x10].n128_u8[8] ~ls_p_1[v40 / 0x10].n128_u8[8] ^ (v40 8);                             if ( loop_len v40 9 )                             {                               ls_p_1[v40 / 0x10].n128_u8[9] ~ls_p_1[v40 / 0x10].n128_u8[9] ^ (v40 9);                               if ( loop_len v40 10 )                               {                                 ls_p_1[v40 / 0x10].n128_u8[10] ~ls_p_1[v40 / 0x10].n128_u8[10] ^ (v40 10);                                 if ( loop_len v40 11 )                                 {                                   ls_p_1[v40 / 0x10].n128_u8[11] ~ls_p_1[v40 / 0x10].n128_u8[11] ^ (v40 11);                                   if ( loop_len v40 12 )                                   {                                     v50 v40 13;                                     ls_p_1[v40 / 0x10].n128_u8[12] ~ls_p_1[v40 / 0x10].n128_u8[12] ^ (v40 12);                                     if ( loop_len v40 13 )                                     {                                       v51 v40 14;                                       ls_p_1-n128_u8[v50] ~ls_p_1-n128_u8[v50] ^ v50;                                       if ( loop_len v51 )                                         ls_p_1-n128_u8[v51] ~ls_p_1-n128_u8[v51] ^ v51;                                     }                                   }                                 }                               }                             }                           }                         }                       }                     }                   }                 }               }             }           }           else           {             v41 ls_p_1;             v42 0;             v43 xmmword_45730;             do             {               v44.n128_u64[0] 0x400000004LL;               v44.n128_u64[1] 0x400000004LL;               v45.n128_u64[0] 0xC0000000CLL;               v45.n128_u64[1] 0xC0000000CLL;               v42;               v46 vaddq_s32(v43, v44);               v44.n128_u64[0] 0x800000008LL;               v44.n128_u64[1] 0x800000008LL;               v47 vaddq_s32(v43, v45);               v48 vmovn_hight_s32(vmovn_s32(v43), v46);               v45.n128_u64[0] 0x1000000010LL;               v45.n128_u64[1] 0x1000000010LL;               v49 vaddq_s32(v43, v44);               v43 vaddq_s32(v43, v45);               *v41 veorq_s8(vmovn_hight_s16(vmovn_s16(v48), vmovn_hight_s32(vmovn_s32(v49), v47)), vmvnq_s8(*v41));               v41;             } 这一大串看着多 其实很简单 大部分内容都是由于编译器为了加快异或而生成的代码 下面xmmword这些其实是 SIMD 指令集 整段代码其实就是 1 2 def stringOBUF(s: bytes) - bytes:     return bytes([( (~e  0xff) ^ i) for i, e in enumerate(s)]) 将字符串按位求反异或 而这个操作的逆函数就是他自身 还有一些其他大大小小的更改 如更换位置 等 这里就不贴上来了 最后 写一个自动编译生成的工作流 结合之前的 ts 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LUAJIT_PATH  os.path.join(LuaJIT, luajit64.exe) LUA_PATH  os.path.join(HookScript, LuaCode, main.lua) LUA_BC_PATH  os.path.join(HookScript, dist, _agent.lua.bc) OUTDATA_PATH  os.path.join(HookScript, src, hooks, luadata.ts) subprocess.run([LUAJIT_PATH, -b,os.path.join(..,LUA_PATH),os.path.join(..,LUA_BC_PATH)], cwdLuaJIT) # f{LUAJIT_PATH} -b {LUA_PATH} {LUA_BC_PATH} with open(LUA_BC_PATH,rb) as f:     data  LuaJitBC(f.read()).dump() luats  [export const luadata [\n] luats.append(,.join([hex(i) for i in data])\n) luats.append(]\n) with open(OUTDATA_PATH,w) as f:     f.writelines(luats) subprocess.run([pnpm,i],cwd./HookScript,shellTrue) subprocess.run([pnpm,run,phone],cwd./HookScript,shellTrue) 在 frida 中 我们直接 hook dll 的对应函数 使用 frida 创建调用 这里核心就 lua_loadbuffer 一个函数 其他都是为了不让我们插入的代码破坏 lua 的原始堆栈引发程序崩溃的保护措施 public LuaLoad(data:Arraynumber,chunkName:string){this.lua_pushtraceback(this.lua_state)const oldTop this.lua_gettop(this.lua_state)const prtbuffer Memory.alloc(data.length1)prtbuffer.writeByteArray(data)const ckn Memory.allocUtf8String(chunkName)console.log(allocing ${data.length} mem)console.log([lua] LuaLoad execing as oldTop:${oldTop} and buffer at ${prtbuffer.toString(16)})if(this.lua_loadbuffer(this.lua_state,prtbuffer,data.length,ckn)0){if(this.lua_pcall(this.lua_state,0,-1,0)0){this.lua_settop(this.lua_state,oldTop-1);console.log([lua] LuaLoad exec finished!)return;}}const errlen new NativePointer(0)const errptr this.lua_tolstring(this.lua_state,-1,errlen)let errmsg:stringtry{if(errlen.isNull()){errmsg errptr.readUtf8String() || }else{errmsg errptr.readUtf8String(errlen.toInt32()) || }console.error(errmsg)console.error([lua] failed to exec)}catch(e){console.error(e)console.log([lua] failed to load errmsg)}this.lua_settop(this.lua_state,oldTop-1);return}尾声 在成功植入 lua 代码之后 参考 unlua 写了反编译 我们就可以直接使用 lua 代码来 hook 并插入内容了 local org_func target_class.target_func target_class.target_func function (self, args)-- doxxx beforeorg_func(self,args)-- doxxx after end文章没有写的很详细 考虑到文章核心是介绍 lua bc 其他部分就都简化了 有什么问题欢迎在文章下提问
http://www.dnsts.com.cn/news/85329.html

相关文章:

  • 东莞品牌做网站如何获取网站根目录
  • 天津企悦在线网站建设兰州网站建设运营方案
  • 外贸定制网站湘潭天元建设集团有限公司
  • 后缀cc的网站做涂鸦的网站
  • 网站到底是域名需要备案还是空间wordpress前台注册插件
  • 网站建设费用组成wordpress打开要10秒
  • 毕业设计选择做网站的意义学生创业做网站制作设计
  • 做一般的公司网站需要多少钱思淘网站建设
  • 做水利网站需要多少钱网站大全全部免费
  • 建设企业官方网站官网全球网
  • 自己做刷东西的网站网络推广器
  • 网站建设与管理的试卷网站的pdf目录怎么做的
  • 国外 上海网站建设软件发布流程
  • 研学网站平台建设方案c 做网站后端
  • 哪里可以做网站平台青岛seo网站关键词优化
  • 网站后台使用什么做的wordpress栏目改瀑布
  • 昆明专门做网站网页美工设计主要从哪些方面设计
  • 常州网站建设公司好么网上电商
  • 个人网站建设规划案例北京信息维护公司
  • 郑州网站建设技术免费seo在线优化
  • 青岛公司网站建设公司天津公司
  • 您与此网站建立的连接不安全东莞餐饮网站建设
  • 银川网站建设网络白天做彩票维护的网站
  • 做网站能设置关键词在百度中搜索到网站建设放什么会计科目
  • 网站 推广做网站必须要dreamever
  • 网站区域名怎么注册吗wordpress需要什么安装环境
  • 网站开发简历网站开发公司销售总监岗位要求
  • 网站底部代码下载企业对网站建设的发展
  • 自己如何做网站源码查一下红之易道学做的什么网站
  • 给人做logo的网站WordPress 斗鱼