├── HotFix ├── HotFix.lua └── hotfix.js ├── InjectFrida ├── LIEFInjectFrida.py ├── SmaliInjectFrida.py └── tools │ ├── APPkeystore.jks │ ├── aapt.exe │ ├── apksignerNew.jar │ ├── baksmali-2.5.2.jar │ ├── frida-gadget-14.2.18-android-arm.so │ ├── frida-gadget-14.2.18-android-arm64.so │ ├── frida-gadget-14.2.18-android-x86.so │ ├── libfrida-gadget.config.so │ └── smali-2.5.2.jar ├── README.md ├── exportCode.py ├── game ├── Frida-cocos-lua-dump.py ├── Frida-mono-dump.py ├── GGHook.js ├── GenShin-3.2-Dump.js └── yuanshenInject.py └── lua └── GGInjector64.lua /HotFix/HotFix.lua: -------------------------------------------------------------------------------- 1 | 2 | --package.cpath = package.cpath .. ';C:/Users/lenovo/.Rider2018.3/config/plugins/intellij-emmylua/classes/debugger/emmy/windows/x64/?.dll' 3 | function print_func_ref_by_csharp() 4 | local registry = debug.getregistry() 5 | for k, v in pairs(registry) do 6 | if type(k) == 'number' and type(v) == 'function' and registry[v] == k then 7 | local info = debug.getinfo(v) 8 | ReleaseLogger.Log("[oook]", "[HotFix]", "[print_func_ref_by_csharp]",string.format('%s:%d', info.short_src, info.linedefined)) 9 | end 10 | end 11 | end 12 | function hotfix(filename) 13 | --print_func_ref_by_csharp() 14 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]","start hotfix: "..filename) 15 | --local dumpname = string.gsub(filename,"%.","_") 16 | local dumpname = filename 17 | local oldModule 18 | if package.loaded[filename] then 19 | oldModule = package.loaded[filename] 20 | elseif package.loaded[dumpname] then 21 | oldModule = package.loaded[dumpname] 22 | else 23 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]",'this file nevev loaded: '..filename) 24 | return 25 | end 26 | 27 | package.loaded[filename] = nil 28 | package.loaded[dumpname] = nil 29 | 30 | local ok,err = pcall(require, dumpname) 31 | if not ok then 32 | package.loaded[filename] = oldModule 33 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]",'reload lua file failed:'..err) 34 | return 35 | end 36 | 37 | 38 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]",'loaded newMpdule '..dumpname..' ,oldModule: '..filename) 39 | local newModule = package.loaded[dumpname] 40 | 41 | if newModule == nil then 42 | -- try again 43 | require(dumpname) 44 | newModule = package.loaded[dumpname] 45 | end 46 | 47 | 48 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]",'oldModule: '.. tostring(oldModule)..' ,newModule: '..tostring(newModule)) 49 | 50 | -- 51 | -- 52 | -- if newModule == nil then 53 | -- package.loaded[filename] = oldModule 54 | -- ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]",'replaced faild !! ') 55 | -- return 56 | -- end 57 | --local updated_tables = {} 58 | -- ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]","updated_tables:"..type(oldModule).." new:"..type(newModule)) 59 | -- --update_table(newModule, oldModule,updated_tables) 60 | -- ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]","7") 61 | -- 62 | -- package.loaded[filename] = newModule 63 | 64 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]",'replaced succeed') 65 | 66 | end 67 | 68 | function ResetENV(object, name) 69 | local visited = {} 70 | local function f(object, name) 71 | if not object or visited[object] then return end 72 | visited[object] = true 73 | if type(object) == "function" then 74 | xpcall(function () setfenv(object, _G) end, CS.UnityEngine.Debug.LogError) 75 | elseif type(object) == "table" then 76 | for k, v in pairs(object) do 77 | f(k, tostring(k).."__key" ) 78 | f(v, tostring(k)) 79 | end 80 | end 81 | end 82 | f(object, name) 83 | end 84 | 85 | function update_func(new_func, old_func) 86 | assert("function" == type(new_func)) 87 | assert("function" == type(old_func)) 88 | 89 | -- Get upvalues of old function. 90 | local old_upvalue_map = {} 91 | local OldExistName = {} 92 | for i = 1, math.huge do 93 | local name, value = debug.getupvalue(old_func, i) 94 | if not name then break end 95 | old_upvalue_map[name] = value 96 | OldExistName[name] = true 97 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]","OldExistName "..name.." i :"..i..'-->'..tostring(value)) 98 | end 99 | 100 | -- Update new upvalues with old. 101 | for i = 1, math.huge do 102 | local name, value = debug.getupvalue(new_func, i) 103 | if not name then break end 104 | --CS.UnityEngine.Debug.LogError('set up value: name:'..name..' typeof '.. type(value)) 105 | if OldExistName[name] then 106 | local old_value = old_upvalue_map[name] 107 | if type(old_value) == "function" then 108 | --update_func(value,old_value) 109 | debug.setupvalue(new_func, i, old_value) 110 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]",name.." is function") 111 | else 112 | if old_value ~= value then 113 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]","set "..name.."") 114 | debug.setupvalue(new_func, i, old_value) 115 | end 116 | end 117 | 118 | else 119 | 120 | -- 对新添加的upvalue设置正确的环境表 121 | ResetENV(value,name) 122 | end 123 | end 124 | end 125 | 126 | function update_table(new_table, old_table, updated_tables) 127 | assert("table" == type(new_table)) 128 | assert("table" == type(old_table)) 129 | 130 | -- Compare 2 tables, and update old table. 131 | for key, value in pairs(new_table) do 132 | --CS.UnityEngine.Debug.LogError("update_table "..key..'-->'..tostring(value)) 133 | local old_value = old_table[key] 134 | local type_value = type(value) 135 | if type_value == "function" then 136 | update_func(value, old_value) 137 | old_table[key] = value 138 | 139 | elseif type_value == "table" then 140 | if ( updated_tables[value] == nil ) then 141 | updated_tables[value] = true 142 | update_table(value, old_value,updated_tables) 143 | end 144 | end 145 | end 146 | 147 | ReleaseLogger.Log("[oook]", "[HotFix]", "[hotfix]","---- Update metatable") 148 | ---- Update metatable. 149 | local old_meta = debug.getmetatable(old_table) 150 | local new_meta = debug.getmetatable(new_table) 151 | if type(old_meta) == "table" and type(new_meta) == "table" then 152 | update_table(new_meta, old_meta,updated_tables) 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /HotFix/hotfix.js: -------------------------------------------------------------------------------- 1 | var lua_State = null 2 | const LUA_PATH = "/data/data/packagename/luadump" 3 | const APP_DIR = "/data/data/packagename" 4 | var luaL_loadfilex,luaL_loadstring,lua_pcall,lua_tolstring,luaL_loadbufferx,LuaEnv 5 | 6 | 7 | 8 | // 作者说只支持art 9 | // Java.performNow(function (){ 10 | // var FileObserver = Java.use("android.os.FileObserver"); 11 | // var LuaFileObserver = Java.registerClass({ 12 | // name:'com.hotfix.LuaFileObserver', 13 | // superClass:FileObserver, 14 | // implements: [FileObserver], 15 | // methods:{ 16 | // $init:[{ 17 | // returnType: 'void', 18 | // arguments:['java.lang.String'], 19 | // implementation:function (p){ 20 | // this.$super.$init(p) 21 | // } 22 | // }, { 23 | // returnType: 'void', 24 | // arguments:[''], 25 | // implementation:function (){ 26 | // this.$super.$init() 27 | // } 28 | // }], 29 | // $new:{ 30 | // returnType: 'void', 31 | // arguments:['java.lang.String'], 32 | // implementation:function (p){ 33 | // this.$super.$new(p) 34 | // } 35 | // }, 36 | // // onEvent:{ 37 | // // returnType: 'void', 38 | // // arguments:['int','java.lang.String'], 39 | // // implementation:function(event,path){ 40 | // // console.log("event :"+event) 41 | // // console.log("path :"+path) 42 | // // } 43 | // // }, 44 | // stopWatching:{ 45 | // returnType: 'void', 46 | // arguments:[''], 47 | // implementation:function (){ 48 | // this.$super.stopWatching() 49 | // } 50 | // }, 51 | // startWatching:{ 52 | // returnType: 'void', 53 | // arguments:[''], 54 | // implementation:function (){ 55 | // this.$super.stopWatching() 56 | // } 57 | // }, 58 | // finalize:{ 59 | // returnType: 'void', 60 | // arguments:[''], 61 | // implementation:function (){ 62 | // this.$super.stopWatching() 63 | // } 64 | // }, 65 | // onEvent:function(event,path){ 66 | // console.log("event :"+event) 67 | // console.log("path :"+path) 68 | // } 69 | // 70 | // } 71 | // }); 72 | // var FileWatcher = LuaFileObserver.$new(LUA_PATH) 73 | // FileObserver.onEvent.implementation = function (event,path){ 74 | // console.log("event :"+event) 75 | // console.log("path :"+path) 76 | // } 77 | // FileWatcher.startWatching() 78 | // }) 79 | var gettid = new NativeFunction(Module.findExportByName(null,"gettid"),'int',[]) 80 | 81 | var status = 0 82 | 83 | function LuaFileWatcher(){ 84 | 85 | var pthread_mutex_init = new NativeFunction(Module.findExportByName(null,"pthread_mutex_init"),'int',['pointer','pointer']) 86 | var pthread_mutex_lock = new NativeFunction(Module.findExportByName(null,"pthread_mutex_lock"),'int',['pointer']) 87 | var pthread_mutex_unlock = new NativeFunction(Module.findExportByName(null,"pthread_mutex_unlock"),'int',['pointer']) 88 | 89 | var inotify_init = new NativeFunction(Module.findExportByName(null,"inotify_init"),'int',[]) 90 | var inotify_add_watch = new NativeFunction(Module.findExportByName(null,"inotify_add_watch"),'int',['int','pointer','int']) 91 | const read = new NativeFunction(Module.findExportByName(null,"read"),'int',['int','pointer','int']); 92 | var fd = inotify_init() 93 | var wd = inotify_add_watch(fd,Memory.allocUtf8String(LUA_PATH),256) //ALL_EVENTS = 4095,OPEN=32 94 | console.log("fd "+fd+",wd "+wd) 95 | const inotify_event_len = 0x10 96 | var data = Memory.alloc(inotify_event_len*10); 97 | while (1){ 98 | let readlen = read(fd,data,inotify_event_len*10-1) 99 | if( readlen<0){ 100 | console.log('[+] Unable to read [!] '); 101 | continue 102 | } 103 | console.log(readlen,data) 104 | 105 | // struct inotify_event { 106 | // __s32 wd; 107 | // __u32 ; 108 | // __u32 cookie; 109 | // __u32 len; 110 | // char name[0]; 111 | // }; 112 | for (let i = 0; i < (readlen/0x10) ; i++) { 113 | let readData = data.add(i*0x10) 114 | let envent = [] 115 | envent.wd = readData.readS32(); 116 | envent.mask = readData.add(4).readU32(); 117 | envent.cookie = readData.add(8).readU32(); 118 | envent.len = readData.add(12).readU32(); 119 | envent.name = readData.add(16).readCString(); 120 | console.log('open file : ',envent.name,envent.mask) 121 | if(envent.mask!=256) 122 | continue; 123 | var mutex = Memory.alloc(Process.pointerSize) 124 | pthread_mutex_init(mutex,new NativePointer(0)) 125 | console.log("run thread pid "+Process.id +" run "+gettid()) 126 | pthread_mutex_lock(mutex) 127 | 128 | try{ 129 | status = 1 130 | console.log('----------------------') 131 | let luaname = envent.name.replaceAll("_",".") 132 | console.log("luaname"+luaname) 133 | var scr ='if string.find(package.path,"/data/data/package_name/luadump/") == nil then\n' + 134 | ' package.path = package.path .. ";/data/data/package_name/luadump/?"\n' + 135 | 'end\n'+ 136 | 'require(\"HotFixOOOK\")\n'+ 137 | 'hotfix(\"'+luaname+'\")' 138 | var luaL_loadstring_ret = luaL_loadstring(lua_State,Memory.allocUtf8String(scr)) 139 | console.log("luaL_loadstring_ret : "+luaL_loadstring_ret) 140 | send("load lua init ret "+ lua_pcall(lua_State,0,0,0) + " str:"+lua_tolstring(lua_State, -1).readCString()) 141 | 142 | }catch (e) { 143 | send("err:"+e.toString()) 144 | }finally { 145 | pthread_mutex_unlock(mutex) 146 | status = 0 147 | } 148 | 149 | } 150 | 151 | } 152 | 153 | } 154 | 155 | var pthread_create = new NativeFunction(Module.findExportByName(null,"pthread_create"),'int',['pointer','pointer','pointer','pointer']) 156 | var pthread_join = new NativeFunction(Module.findExportByName(null,"pthread_join"),'int',['pointer','pointer']) 157 | var LuaFileWatcherNative = new NativeCallback(LuaFileWatcher,'void',['void']) 158 | 159 | 160 | // 启动新线程对目标目录进行文件监控。 161 | var pthread_t = Memory.alloc(16).writeLong(0) 162 | pthread_create(pthread_t,new NativePointer(0),LuaFileWatcherNative,new NativePointer(0)) 163 | console.log("run pthread_create pid "+Process.id +" run "+gettid()) 164 | 165 | 166 | var libil2cpp = null; 167 | while(libil2cpp == null){ 168 | libil2cpp = Process.findModuleByName("libil2cpp.so"); 169 | } 170 | send(libil2cpp); 171 | 172 | var module = null; 173 | while(module == null){ 174 | module = Process.findModuleByName("libxlua.so"); 175 | } 176 | send(module); 177 | 178 | Interceptor.attach(Module.findExportByName("libxlua.so","luaL_loadbufferx"),{ 179 | onEnter:function(args){ 180 | const name = Memory.readCString(args[3]); 181 | console.log("luaL_loadbufferx name :",name) 182 | } 183 | }); 184 | 185 | 186 | 187 | Interceptor.attach(Module.findExportByName("libxlua.so","luaL_openlibs"),{ 188 | onEnter:function(args){ 189 | send("lua_State:"+args[0]) 190 | lua_State = ptr(args[0]) 191 | luaL_loadfilex = new NativeFunction(Module.findExportByName("libxlua.so","luaL_loadfilex"),'int',['pointer','pointer']) 192 | luaL_loadstring = new NativeFunction(Module.findExportByName("libxlua.so","luaL_loadstring"),'int',['pointer','pointer']) 193 | lua_pcall = new NativeFunction(Module.findExportByName("libxlua.so","lua_pcall"),'int',['pointer','int','int','int']) 194 | lua_tolstring = new NativeFunction(Module.findExportByName("libxlua.so","lua_tolstring"),'pointer',['pointer','int']) 195 | 196 | luaL_loadbufferx = new NativeFunction(Module.findExportByName("libxlua.so","luaL_loadbufferx"),'int',['pointer','pointer','int','pointer','pointer']) 197 | 198 | },onLeave:function (ret) { 199 | } 200 | }); 201 | 202 | 203 | -------------------------------------------------------------------------------- /InjectFrida/LIEFInjectFrida.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import shutil 4 | import sys 5 | import zipfile 6 | import lief 7 | import sys 8 | 9 | """ 10 | 使用方法: 11 | python3 需要注入的apk 输出路径(注意结尾不要添加/) 注入so的名字(最好是第一个加载的) 12 | -apksign(可选项,写了就一键签名) -persistence(反正只多一个config文件,最好加上) 13 | """ 14 | def getpwd(): 15 | pwd = sys.path[0] 16 | if os.path.isfile(pwd): 17 | pwd = os.path.dirname(pwd) 18 | return pwd 19 | 20 | 21 | def getpwd(): 22 | pwd = sys.path[0] 23 | if os.path.isfile(pwd): 24 | pwd = os.path.dirname(pwd) 25 | return pwd 26 | 27 | class LIEFInject: 28 | def __init__(self,args): 29 | has_lib = False 30 | with zipfile.ZipFile(args.input, 'r') as apk_file: 31 | for item in apk_file.infolist(): 32 | if item.filename.endswith(".so"): 33 | has_lib = True 34 | break 35 | if not has_lib: 36 | print('apk can\'t find so') 37 | exit(1) 38 | self.apkpath = args.input 39 | self.outdir = args.output 40 | self.soname = args.soname 41 | self.deletelist = [] 42 | self.toolPath = getpwd() + r"\tools" 43 | 44 | 45 | def injectso(self): 46 | injectsolist = [] 47 | with zipfile.ZipFile(self.apkpath,'r')as apk_file: 48 | for item in apk_file.infolist(): 49 | if item.filename.find(self.soname) != -1: 50 | apk_file.extract(item.filename) 51 | self.deletelist.append(item.filename) 52 | injectsolist.append(item.filename) 53 | #x86有一点问题,且不支持x86_64 54 | for soname in injectsolist: 55 | # if soname.find("x86_64") != -1: 56 | if soname.find("x86") != -1: 57 | continue 58 | so = lief.parse(os.getcwd()+"\\"+soname) 59 | so.add_library("libfrida-gadget.so") 60 | so.write(soname+"gadget.so") 61 | 62 | 63 | def modifyapk(self): 64 | (path, filename) = os.path.split(self.apkpath) 65 | (file, ext) = os.path.splitext(filename) 66 | outapk = os.path.join(self.outdir,file+"_frida.apk") 67 | with zipfile.ZipFile(self.apkpath, 'r')as orig_file: 68 | with zipfile.ZipFile(outapk, 'w')as out_file: 69 | for item in orig_file.infolist(): 70 | if item.filename.find(self.soname) != -1 and os.path.exists(os.getcwd()+"\\"+item.filename+"gadget.so"): 71 | out_file.write(os.getcwd()+"\\"+item.filename+"gadget.so",arcname=item.filename) 72 | if item.filename.find("lib/armeabi-v7a") != -1: 73 | out_file.write(os.path.join(self.toolPath,"frida-gadget-14.2.18-android-arm.so"), 74 | arcname="lib/armeabi-v7a/libfrida-gadget.so") 75 | print("add lib/armeabi-v7a/libfrida-gadget.so") 76 | if item.filename.find("lib/arm64-v8a") != -1: 77 | out_file.write(os.path.join(self.toolPath, "frida-gadget-14.2.18-android-arm64.so"), 78 | arcname="lib/arm64-v8a/libfrida-gadget.so") 79 | print("add lib/arm64-v8a/libfrida-gadget.so") 80 | if item.filename.find("lib/x86/") != -1: 81 | out_file.write(os.path.join(self.toolPath, "frida-gadget-14.2.18-android-x86.so"), 82 | arcname="lib/x86/libfrida-gadget.so") 83 | print("add lib/x86/libfrida-gadget.so") 84 | continue 85 | if item.filename.find("META-INF") == -1: 86 | out_file.writestr(item, orig_file.read(item.filename)) 87 | 88 | shutil.rmtree("lib") 89 | return outapk 90 | 91 | def addHook(self,apk_path): 92 | with zipfile.ZipFile(apk_path, 'a')as apk_file: 93 | for item in apk_file.infolist(): 94 | if item.filename == "lib/armeabi-v7a/libfrida-gadget.so": 95 | apk_file.write(os.path.join(self.toolPath, "libfrida-gadget.config.so"), 96 | arcname="lib/armeabi-v7a/libfrida-gadget.config.so") 97 | print("add lib/armeabi-v7a/libfrida-gadget.config.so") 98 | if item.filename == "lib/arm64-v8a/libfrida-gadget.so": 99 | apk_file.write(os.path.join(self.toolPath, "libfrida-gadget.config.so"), 100 | arcname="lib/arm64-v8a/libfrida-gadget.config.so") 101 | print("add lib/arm64-v8a/libfrida-gadget.config.so") 102 | if item.filename == "lib/x86/libfrida-gadget.so": 103 | apk_file.write(os.path.join(self.toolPath, "libfrida-gadget.config.so"), 104 | arcname="lib/x86/libfrida-gadget.config.so") 105 | print("add lib/x86/libfrida-gadget.config.so") 106 | continue 107 | 108 | 109 | 110 | def signApk(self,apk_path): 111 | keystore = os.path.join(self.toolPath,'APPkeystore.jks') 112 | alias = 'key0' 113 | pswd = 'qwer1234' 114 | aliaspswd = 'qwer1234' 115 | 116 | apkname = os.path.splitext(os.path.split(apk_path)[1])[0] 117 | outfile = os.path.join(os.path.split(apk_path)[0], apkname + "_Signed.apk") 118 | 119 | cmd = 'java -jar %s\\apksignerNew.jar sign --ks %s --ks-key-alias %s --ks-pass pass:%s --key-pass pass:%s --out %s %s'% \ 120 | (self.toolPath,keystore, alias,pswd,aliaspswd,outfile,apk_path) 121 | # print(cmd) 122 | os.system(cmd) 123 | 124 | if __name__ == "__main__": 125 | parser = argparse.ArgumentParser() 126 | parser.add_argument('input', help="apk path") 127 | parser.add_argument('output', help="Folder to store output files") 128 | parser.add_argument('soname', help="the so name of apk first load ") 129 | parser.add_argument('-apksign', help="Sign apk", action='store_true') 130 | parser.add_argument('-persistence', help="HOOK Persistence ", action='store_true') 131 | 132 | args = parser.parse_args() 133 | liefs = LIEFInject(args) 134 | liefs.injectso() 135 | out = liefs.modifyapk() 136 | if args.persistence: 137 | liefs.addHook(out) 138 | if args.apksign: 139 | liefs.signApk(out) 140 | 141 | print(u"sucess, new apk :"+out) 142 | 143 | -------------------------------------------------------------------------------- /InjectFrida/SmaliInjectFrida.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import re 4 | import shutil 5 | import subprocess 6 | import zipfile 7 | 8 | 9 | class SmaliInject: 10 | def __init__(self,args): 11 | has_lib = False 12 | with zipfile.ZipFile(args.input, 'r') as apk_file: 13 | for item in apk_file.infolist(): 14 | if item.filename.endswith(".so"): 15 | has_lib = True 16 | break 17 | if has_lib: 18 | print('apk find so , Use LIEF inject is better') 19 | proceed = input("Keep running ? [Y/N]") 20 | if proceed == 'Y' or proceed == 'y': 21 | print("----- Smali inject -----") 22 | else: 23 | exit(0) 24 | self.apkpath = args.input 25 | self.outdir = args.output 26 | self.deletelist = [] 27 | self.toolPath = os.getcwd() + r"\tools" 28 | self.decompileDir = os.getcwd() + r"\decompile" 29 | self.dexPath = os.getcwd() + r"\dex" 30 | self.dexList = [] 31 | apkfile = zipfile.ZipFile(self.apkpath, 'r') 32 | for file_name in apkfile.namelist(): 33 | if file_name.endswith(".dex") and file_name.startswith("classes"): 34 | if not os.path.exists(self.dexPath): 35 | os.mkdir(self.dexPath) 36 | apkfile.extract(file_name, self.dexPath) 37 | self.dexList.append(os.path.join(self.dexPath, file_name)) 38 | 39 | def injectso(self): 40 | target_activity = self.get_launchable_activity_aapt() 41 | print(target_activity) 42 | for dex in self.dexList: 43 | print(dex) 44 | if self.dexDecompile(dex): 45 | smali_path = os.path.join(self.decompileDir,target_activity.replace('.','\\'))+".smali" 46 | print(smali_path) 47 | with open(smali_path, 'r') as fp: 48 | lines = fp.readlines() 49 | has_clinit = False 50 | start = 0 51 | for i in range(len(lines)): 52 | if lines[i].find(".source") != -1: 53 | start = i 54 | if lines[i].find(".method static constructor ()V") != -1: 55 | if lines[i + 3].find(".line") != -1: 56 | code_line = lines[i + 3][-3:] 57 | lines.insert(i + 3, "%s%s\r" % (lines[i + 3][0:-3], str(int(code_line) - 2))) 58 | print("%s%s" % (lines[i + 3][0:-3], str(int(code_line) - 2))) 59 | lines.insert(i + 4, "const-string v0, \"frida-gadget\"\r") 60 | lines.insert(i + 5, 61 | "invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\r") 62 | has_clinit = True 63 | break 64 | if not has_clinit: 65 | lines.insert(start + 1, ".method static constructor ()V\r") 66 | lines.insert(start + 2, ".registers 1\r") 67 | lines.insert(start + 3, ".line 10\r") 68 | lines.insert(start + 4, "const-string v0, \"frida-gadget\"\r") 69 | lines.insert(start + 5, 70 | "invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\r") 71 | lines.insert(start + 6, "return-void\r") 72 | lines.insert(start + 7, ".end method\r") 73 | 74 | with open(smali_path, "w") as fp: 75 | fp.writelines(lines) 76 | self.dexCompile(dex) 77 | 78 | def modifyapk(self): 79 | (path, filename) = os.path.split(self.apkpath) 80 | (file, ext) = os.path.splitext(filename) 81 | outapk = os.path.join(self.outdir, file + "_frida.apk") 82 | with zipfile.ZipFile(self.apkpath, 'r')as orig_file: 83 | with zipfile.ZipFile(outapk, 'w')as out_file: 84 | for item in orig_file.infolist(): 85 | if item.filename.startswith("classes") and item.filename.endswith(".dex"): 86 | continue 87 | if item.filename.find("META-INF") == -1 : 88 | out_file.writestr(item, orig_file.read(item.filename)) 89 | for dex in self.dexList: 90 | out_file.write(dex,os.path.split(dex)[1]) 91 | out_file.write(os.path.join(self.toolPath, "frida-gadget-14.2.18-android-arm.so"), 92 | arcname="lib/armeabi-v7a/libfrida-gadget.so") 93 | print("add lib/armeabi-v7a/libfrida-gadget.so") 94 | out_file.write(os.path.join(self.toolPath, "frida-gadget-14.2.18-android-arm64.so"), 95 | arcname="lib/arm64-v8a/libfrida-gadget.so") 96 | print("add lib/arm64-v8a/libfrida-gadget.so") 97 | out_file.write(os.path.join(self.toolPath, "frida-gadget-14.2.18-android-x86.so"), 98 | arcname="lib/x86/libfrida-gadget.so") 99 | print("add lib/x86/libfrida-gadget.so") 100 | shutil.rmtree("dex") 101 | shutil.rmtree("decompile") 102 | return outapk 103 | 104 | def addHook(self,apk_path): 105 | with zipfile.ZipFile(apk_path, 'a')as apk_file: 106 | for item in apk_file.infolist(): 107 | if item.filename == "lib/armeabi-v7a/libfrida-gadget.so": 108 | apk_file.write(os.path.join(self.toolPath, "libfrida-gadget.config.so"), 109 | arcname="lib/armeabi-v7a/libfrida-gadget.config.so") 110 | print("add lib/armeabi-v7a/libfrida-gadget.config.so") 111 | if item.filename == "lib/arm64-v8a/libfrida-gadget.so": 112 | apk_file.write(os.path.join(self.toolPath, "libfrida-gadget.config.so"), 113 | arcname="lib/arm64-v8a/libfrida-gadget.config.so") 114 | print("add lib/arm64-v8a/libfrida-gadget.config.so") 115 | if item.filename == "lib/x86/libfrida-gadget.so": 116 | apk_file.write(os.path.join(self.toolPath, "libfrida-gadget.config.so"), 117 | arcname="lib/x86/libfrida-gadget.config.so") 118 | print("add lib/x86/libfrida-gadget.config.so") 119 | continue 120 | 121 | def signApk(self,apk_path): 122 | keystore = os.path.join(self.toolPath,'APPkeystore.jks') 123 | alias = 'key0' 124 | pswd = 'qwer1234' 125 | aliaspswd = 'qwer1234' 126 | 127 | apkname = os.path.splitext(os.path.split(apk_path)[1])[0] 128 | outfile = os.path.join(os.path.split(apk_path)[0], apkname + "_Signed.apk") 129 | 130 | cmd = 'java -jar %s\\apksignerNew.jar sign --ks %s --ks-key-alias %s --ks-pass pass:%s --key-pass pass:%s --out %s %s'% \ 131 | (self.toolPath,keystore, alias,pswd,aliaspswd,outfile,apk_path) 132 | os.system(cmd) 133 | 134 | def get_launchable_activity_aapt(self): 135 | 136 | aapt_path = os.path.join(self.toolPath, 'aapt.exe') 137 | cmd = '%s dump badging "%s" ' % (aapt_path, self.apkpath) 138 | p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 139 | out,err = p.communicate() 140 | cmd_output = out.decode('utf-8').split('\r') 141 | for line in cmd_output: 142 | pattern = re.compile("launchable-activity: name='(\S+)'") 143 | match = pattern.search(line) 144 | if match: 145 | # print match.group()[27:-1] 146 | return match.group()[27:-1] 147 | def dexCompile(self,dexPath): 148 | baksmaliJarPath = os.path.join(self.toolPath, "smali-2.5.2.jar") 149 | command = 'java -jar \"%s\" assemble -o \"%s\" \"%s\"' % (baksmaliJarPath, dexPath, self.decompileDir) 150 | os.system(command) 151 | if not os.path.exists(dexPath): 152 | print(u"反编译失败") 153 | def dexDecompile(self,dexPath): 154 | if os.path.exists(self.decompileDir): 155 | shutil.rmtree(self.decompileDir) 156 | baksmaliJarPath = os.path.join(self.toolPath, "baksmali-2.5.2.jar") 157 | if not os.path.exists(dexPath): 158 | print(u"[dexDecompile] 文件%s不存在", dexPath) 159 | return False 160 | if not os.path.exists(baksmaliJarPath): 161 | print(u"[dexDecompile] 文件%s不存在", baksmaliJarPath) 162 | return False 163 | command = 'java -jar \"%s\" disassemble -o \"%s\" \"%s\"' % (baksmaliJarPath, self.decompileDir, dexPath) 164 | os.system(command) 165 | if not os.path.exists(self.decompileDir): 166 | print(u"[dexDecompile] 路径%s不存在", self.decompileDir) 167 | return False 168 | return True 169 | 170 | 171 | if __name__ == "__main__": 172 | 173 | parser = argparse.ArgumentParser() 174 | parser.add_argument('input', help="apk path") 175 | parser.add_argument('output', help="Folder to store output files") 176 | parser.add_argument('-apksign', help="Sign apk", action='store_true') 177 | parser.add_argument('-persistence', help="HOOK Persistence ", action='store_true') 178 | 179 | args = parser.parse_args() 180 | tool = SmaliInject(args) 181 | tool.injectso() 182 | out = tool.modifyapk() 183 | if args.persistence: 184 | tool.addHook(out) 185 | if args.apksign: 186 | tool.signApk(out) 187 | 188 | 189 | print(u"sucess, new apk :"+out) -------------------------------------------------------------------------------- /InjectFrida/tools/APPkeystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nszdhd1/UtilScript/546d37c8d3fa7dbe42bf347fc125b7d303f22c71/InjectFrida/tools/APPkeystore.jks -------------------------------------------------------------------------------- /InjectFrida/tools/aapt.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nszdhd1/UtilScript/546d37c8d3fa7dbe42bf347fc125b7d303f22c71/InjectFrida/tools/aapt.exe -------------------------------------------------------------------------------- /InjectFrida/tools/apksignerNew.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nszdhd1/UtilScript/546d37c8d3fa7dbe42bf347fc125b7d303f22c71/InjectFrida/tools/apksignerNew.jar -------------------------------------------------------------------------------- /InjectFrida/tools/baksmali-2.5.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nszdhd1/UtilScript/546d37c8d3fa7dbe42bf347fc125b7d303f22c71/InjectFrida/tools/baksmali-2.5.2.jar -------------------------------------------------------------------------------- /InjectFrida/tools/frida-gadget-14.2.18-android-arm.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nszdhd1/UtilScript/546d37c8d3fa7dbe42bf347fc125b7d303f22c71/InjectFrida/tools/frida-gadget-14.2.18-android-arm.so -------------------------------------------------------------------------------- /InjectFrida/tools/frida-gadget-14.2.18-android-arm64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nszdhd1/UtilScript/546d37c8d3fa7dbe42bf347fc125b7d303f22c71/InjectFrida/tools/frida-gadget-14.2.18-android-arm64.so -------------------------------------------------------------------------------- /InjectFrida/tools/frida-gadget-14.2.18-android-x86.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nszdhd1/UtilScript/546d37c8d3fa7dbe42bf347fc125b7d303f22c71/InjectFrida/tools/frida-gadget-14.2.18-android-x86.so -------------------------------------------------------------------------------- /InjectFrida/tools/libfrida-gadget.config.so: -------------------------------------------------------------------------------- 1 | { 2 | "interaction": { 3 | "type": "script", 4 | "path": "/data/local/tmp/frida_script.js", 5 | "on_change":"reload" 6 | } 7 | } -------------------------------------------------------------------------------- /InjectFrida/tools/smali-2.5.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nszdhd1/UtilScript/546d37c8d3fa7dbe42bf347fc125b7d303f22c71/InjectFrida/tools/smali-2.5.2.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UtilScript 2 | 3 | frida 赛高~ o( ̄▽ ̄)d 4 | 5 | ## game 6 | 7 | 一些脚本: 8 | 9 | Frida-cocos-lua-dump.py dump cocos游戏的lua代码 10 | 11 | Frida-mono-dump.py dump unity mono 的dll 12 | 13 | yuansheninject.py 是喵喵子的 [使用frida获取unity il2cpp符号信息](https://nszdhd1.github.io/2020/12/04/%E4%BD%BF%E7%94%A8frida%E8%8E%B7%E5%8F%96il2cpp%E7%AC%A6%E5%8F%B7%E4%BF%A1%E6%81%AF/#more) 里的代码 14 | 15 | exportCode.py 是ida 导出伪代码到 cpp文件里 16 | 17 | GGHook.js 用户获取GG脚本运行时的相关信息 [GameGuardian的Lua脚本分析](https://nszdhd1.github.io/2022/09/08/GameGuardian%E7%9A%84Lua%E8%84%9A%E6%9C%AC%E6%B7%B7%E6%B7%86%E5%88%86%E6%9E%90/#more) 18 | 19 | GenShin-3.2-Dump.js 原神3.2版本符号信息dump [IL2CPP runtime dump](https://bbs.pediy.com/thread-275146.htm) 20 | 21 | ## InjectFrida 22 | 23 | frida注入apk的两种实现方式: 24 | 25 | [非root环境下frida持久化的两种方式及脚本](https://bbs.pediy.com/thread-268175.htm) 26 | 27 | 使用方法: 28 | python3 script.py 需要注入的apk 输出路径(注意结尾不要添加/) 注入so的名字(最好是第一个加载的) apksign(可选项,写了就一键签名) -persistence(反正只多一个config文件,最好加上) 29 | 30 | ## lua 31 | 32 | GGInjector64.lua 使用 gameguardian lua脚本 实现64位elf文件解析 -------------------------------------------------------------------------------- /exportCode.py: -------------------------------------------------------------------------------- 1 | import idaapi 2 | import idautils 3 | 4 | def decompile(func): 5 | try: 6 | func_str = idaapi.decompile(func) 7 | except: 8 | return " decompile faild \n" 9 | return str(func_str) 10 | 11 | def main(): 12 | if not idaapi.init_hexrays_plugin(): 13 | return False 14 | output = GetInputFile().split('.')[0]+'.cpp' 15 | f = open(output,'a') 16 | for segea in Segments(): 17 | for funcea in idautils.Functions(segea, SegEnd(segea)): 18 | code = decompile(funcea) 19 | f.write(code) 20 | f.close() 21 | 22 | if main(): 23 | idaapi.term_hexrays_plugin() 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /game/Frida-cocos-lua-dump.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | import sys 4 | import frida 5 | 6 | """ 7 | 该脚本用于dump cocos 使用的 lua 脚本 8 | dump 文件保存在 /sdcard/fridadump文件夹中, 9 | 如果 fridadump 文件夹中存在lua文件,就会加载fridadump中的lua文件 10 | 游戏安全测试好帮手,(#^.^#) 11 | """ 12 | 13 | device = frida.get_usb_device() 14 | pid = device.spawn(["com.nayijian.guanwang"]) 15 | session = device.attach(pid) 16 | device.resume(pid) 17 | 18 | scr = """ 19 | var APP_NAME = "com.nayijian.guanwang" 20 | 21 | var module = null; 22 | while(module == null){ 23 | module = Process.findModuleByName("libil2cpp.so"); 24 | } 25 | send(module); 26 | 27 | Interceptor.attach(Module.findExportByName(null,"luaL_loadbufferx"),{ 28 | onEnter:function(args){ 29 | var name = Memory.readCString(args[3]); 30 | send(name.length); 31 | if(name.length < 50){ 32 | send(name); 33 | 34 | this.Path = name.substring(0,name.lastIndexOf("/")); 35 | this.file = name.substring(name.lastIndexOf("/")+1,name.length); 36 | if(access("/sdcard/fridadump/lua/"+this.Path) == -1){ //文件夹不存在 37 | folder_mkdirs(this.Path); 38 | } 39 | if(access("/sdcard/fridadump/lua/"+name) == 0 ){//文件存在 40 | var data = read_lua("/sdcard/fridadump/lua/"+name); 41 | send(data); 42 | args[1] = data.data; 43 | args[2] = new NativePointer(ptr(data.size)); 44 | send("do load file :" + name); 45 | 46 | }else{ 47 | Dump("/sdcard/fridadump/lua/"+name,args[1],args[2].toInt32()); 48 | } 49 | 50 | } 51 | 52 | } 53 | }); 54 | 55 | function Dump(filePath,data,datalen){ 56 | send("dump : "+ filePath); 57 | var dumpfile = new File(filePath,"wb"); 58 | dumpfile.write(data.readByteArray(datalen)); 59 | dumpfile.close(); 60 | } 61 | 62 | function access(filePath){ 63 | var ptr_access = Module.findExportByName("libc.so","access"); 64 | var func_access = new NativeFunction(ptr_access,'int',['pointer','int']); 65 | var ptr_filepath = Memory.allocUtf8String(filePath); 66 | var ret = func_access(ptr_filepath,0); 67 | return ret; 68 | } 69 | 70 | function mkdir(Path){ 71 | var ptr_mkdir = Module.findExportByName("libc.so","mkdir"); 72 | var func_mkdir = new NativeFunction(ptr_mkdir,'int',['pointer','int']); 73 | var ptr_filepath = Memory.allocUtf8String(Path); 74 | var ret = func_mkdir(ptr_filepath,777); 75 | return ret; 76 | } 77 | 78 | function folder_mkdirs(p){ 79 | var p_list = p.split("/"); 80 | var pp = "/sdcard/fridadump/lua"; 81 | for(var i = 0;i< p_list.length ;i++){ 82 | pp = pp + "/" + p_list[i]; 83 | if(access(pp) != 0){ 84 | var x = mkdir(pp) 85 | send("mkdir :"+pp+"ret :" +x); 86 | } 87 | } 88 | 89 | } 90 | // frida file 对象没有read 91 | function read_lua(filePath){ 92 | var ptr_open = Module.findExportByName("libc.so","open"); 93 | const open = new NativeFunction(ptr_open,'int',['pointer','int']); 94 | 95 | var ptr_read = Module.findExportByName("libc.so","read"); 96 | const read = new NativeFunction(ptr_read,'int',['int','pointer','int']); 97 | 98 | var ptr_close = Module.findExportByName("libc.so","close"); 99 | const close = new NativeFunction(ptr_close,'int',['int']); 100 | 101 | var fd = open(Memory.allocUtf8String(filePath),0); 102 | var size = get_file_size(fd); 103 | if(size >0){ 104 | var data = Memory.alloc(size + 5); 105 | if( read(fd,data,size) <0){ 106 | console.log('[+] Unable to read DLL [!]'); 107 | close(fd); 108 | return 0; 109 | } 110 | close(fd); 111 | return {data:data,size:size}; 112 | } 113 | 114 | } 115 | 116 | function get_file_size(fd){ 117 | var statBuff = Memory.alloc(500); 118 | var fstatSymbol = Module.findExportByName('libc.so', 'fstat'); 119 | var fstat = new NativeFunction(fstatSymbol, 'int', ['int', 'pointer']); 120 | if(fd > 0) { 121 | var ret = fstat(fd, statBuff); 122 | if(ret < 0) { console.log('[+] fstat --> failed [!]'); 123 | } 124 | } 125 | var size = Memory.readS32(statBuff.add(0x30)); 126 | if(size > 0) { 127 | return size; 128 | } else { 129 | return 0; 130 | } 131 | } 132 | 133 | """ 134 | 135 | def on_message(message, data): 136 | if message['type'] == 'send': 137 | print("[*] {0}".format(message['payload'])) 138 | else: 139 | print(message) 140 | 141 | 142 | script = session.create_script(scr) 143 | script.on("message", on_message) 144 | script.load() 145 | sys.stdin.read() 146 | 147 | 148 | -------------------------------------------------------------------------------- /game/Frida-mono-dump.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import frida 4 | 5 | 6 | """ 7 | 该脚本用于dump unity mono 的dll, 8 | dll 未保护 使用 mono_image_open_from_data_with_name, 9 | dll 被保护(加密,主要是TX),使用 do_mono_image_load,这是一个非导出函数,在libmono.so搜索字符串 “data-%p”即可。 10 | 具体原因及讲解见大佬18年帖子:https://bbs.pediy.com/thread-247487.htm 11 | 12 | ps. 13 | 需要 手动创建 两个文件夹 14 | 1. /sdcard/fridadump 2. /data/data/app_anme/frida 15 | 16 | """ 17 | 18 | APP_NAME = "com.tencent.pocket" 19 | 20 | def pull_dll(): 21 | inpath = pulldir = "/data/data/" + APP_NAME + "/frida" 22 | cmd_cp = "adb shell su -c 'cp -r " + inpath + " /sdcard/fridadump ' " 23 | print(cmd_cp) 24 | cmd_pull = "adb pull /sdcard/fridadump" 25 | os.system(cmd_cp) 26 | os.system(cmd_pull) 27 | 28 | 29 | def push_dll(): 30 | path = "/data/data/" + APP_NAME 31 | cmd_push = "adb push fridadump /sdcard" 32 | cmd_cp = " adb shell su -c 'cp -r /sdcard/fridadump/frida " + path +" '" 33 | print(cmd_cp) 34 | os.system(cmd_push) 35 | os.system(cmd_cp) 36 | 37 | 38 | 39 | 40 | device = frida.get_usb_device() 41 | pid = device.spawn(["com.DefaultCompany.unity2020"]) 42 | session = device.attach(pid) 43 | device.resume(pid) 44 | 45 | scr = """ 46 | var DUMP_FILE_PATH = "/data/data/com.tencent.pocket/frida/"; 47 | var APP_NAME = "com.tencent.pocket" 48 | function DumpDll(filePath,data,datalen){ 49 | 50 | send("dump dll : "+ filePath); 51 | var dumpfile = new File(filePath,"wb"); 52 | dumpfile.write(data.readByteArray(datalen)); 53 | dumpfile.close(); 54 | 55 | } 56 | 57 | 58 | Interceptor.attach(Module.findExportByName(null , "dlopen"), { 59 | onEnter: function(args) { 60 | var soName = args[0].readCString(); 61 | if(soName.indexOf(APP_NAME) != -1 && soName.indexOf("libmono.so") != -1){ 62 | send("dlopen load :"+soName); 63 | this.hook = true; 64 | } 65 | }, 66 | onLeave:function(retval){ 67 | if(this.hook == true){ 68 | do_image_hook(); 69 | } 70 | } 71 | }); 72 | 73 | 74 | function do_image_hook(){ 75 | 76 | var module = Process.getModuleByName("libmono.so"); 77 | send(module.base); 78 | Interceptor.attach(ptr(module.base).add(0x194878),{ //do_mono_image_load() 79 | onEnter:function(args){ 80 | var images = args[0]; 81 | var name = images.add(20).readPointer().readCString(); 82 | var data = images.add(8).readPointer(); 83 | var length = images.add(12).readPointer() 84 | 85 | send(name); 86 | send(length); 87 | var s = name.split("/"); 88 | var filePath = DUMP_FILE_PATH + s[s.length -1]; 89 | DumpDll(filePath,data,length.toInt32()); 90 | 91 | } 92 | 93 | }); 94 | 95 | Interceptor.attach(Module.findExportByName("libmono.so","mono_image_open_from_data_with_name"),{ 96 | onEnter:function(args){ 97 | 98 | var s = args[5].readCString().split("/"); 99 | var filePath = DUMP_FILE_PATH + s[s.length -1]; 100 | 101 | 102 | // DumpDll(filePath,args[0],args[1].toInt32()); 103 | 104 | 105 | 106 | } 107 | }); 108 | 109 | } 110 | 111 | function Check_dump_file(filePath){ 112 | 113 | var ptr_access = Module.findExportByName("libc.so","access"); 114 | var func_access = new NativeFunction(ptr_access,'int',['pointer','int']); 115 | var ptr_filepath = Memory.allocUtf8String(filePath); 116 | var ret = func_access(ptr_filepath,0); 117 | return ret; 118 | } 119 | 120 | function get_file_size(fd){ 121 | var statBuff = Memory.alloc(500); 122 | var fstatSymbol = Module.findExportByName('libc.so', 'fstat'); 123 | var fstat = new NativeFunction(fstatSymbol, 'int', ['int', 'pointer']); 124 | if(fd > 0) { 125 | var ret = fstat(fd, statBuff); 126 | if(ret < 0) { console.log('[+] fstat --> failed [!]'); 127 | } 128 | } 129 | var size = Memory.readS32(statBuff.add(0x30)); 130 | if(size > 0) { 131 | return size; 132 | } else { 133 | return 0; 134 | } 135 | } 136 | 137 | // frida file 对象没有read 138 | function do_load_dll(filePath){ 139 | var ptr_open = Module.findExportByName("libc.so","open"); 140 | const open = new NativeFunction(ptr_open,'int',['pointer','int']); 141 | 142 | var ptr_read = Module.findExportByName("libc.so","read"); 143 | const read = new NativeFunction(ptr_read,'int',['int','pointer','int']); 144 | 145 | var ptr_close = Module.findExportByName("libc.so","close"); 146 | const close = new NativeFunction(ptr_close,'int',['int']); 147 | 148 | var fd = open(Memory.allocUtf8String(filePath),0); 149 | var size = get_file_size(fd); 150 | if(size >0){ 151 | var data = Memory.alloc(size + 5); 152 | if( read(fd,data,size) <0){ 153 | console.log('[+] Unable to read DLL [!]'); 154 | close(fd); 155 | return 0; 156 | } 157 | close(fd); 158 | return data; 159 | } 160 | 161 | } 162 | 163 | """ 164 | 165 | 166 | 167 | def on_message(message, data): 168 | if message['type'] == 'send': 169 | print("[*] {0}".format(message['payload'])) 170 | else: 171 | print(message) 172 | 173 | 174 | script = session.create_script(scr) 175 | script.on("message", on_message) 176 | script.load() 177 | sys.stdin.read() 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /game/GGHook.js: -------------------------------------------------------------------------------- 1 | Java.performNow(function () { 2 | Java.enumerateLoadedClasses({ 3 | onMatch: function (name, handle) { 4 | if (name.startsWith("android.ext.Script$")) { 5 | if (name == "android.ext.Script$isVisible" || name == "android.ext.Script$ApiFunction" 6 | || name == "android.ext.Script$BusyApiFunction" || name == "android.ext.Script$DebugFunctio" 7 | || name.endsWith("$clearResults")) 8 | return; 9 | var klass = Java.use(name); 10 | console.log(JSON.stringify(klass)); 11 | if ("android.ext.Script$ApiFunction" == klass.$super.$className || "android.ext.Script$BusyApiFunction" == klass.$super.$className) { 12 | for (var _i = 0, _a = klass.$ownMembers; _i < _a.length; _i++) { 13 | var m = _a[_i]; 14 | if (m == "a" && typeof klass[m] == "function") { 15 | try { 16 | Java.use(name).a.overload().implementation = function () { 17 | console.log(this.a()); 18 | return this.a(); 19 | }; 20 | } 21 | catch (e) { 22 | console.log(e); 23 | console.log(name); 24 | } 25 | } 26 | if (m == "d" && typeof klass[m] == "function") { 27 | try { 28 | Java.use(name).d.implementation = function (a) { 29 | if (name.endsWith("searchNumber")) { 30 | console.log("a1 string :", a.r(1)); 31 | } 32 | console.log(name, ":", a); 33 | return this.d(a); 34 | }; 35 | } 36 | catch (e) { 37 | console.log(e); 38 | console.log(name); 39 | } 40 | } 41 | if (m == "b" && typeof klass[m] == "function") { 42 | try { 43 | Java.use(name).b.implementation = function (a) { 44 | console.log(name, ":", a); 45 | return this.b(a); 46 | }; 47 | } 48 | catch (e) { 49 | console.log(e); 50 | console.log(name); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | }, 57 | onComplete: function () { 58 | } 59 | }); 60 | }); 61 | console.log("end"); -------------------------------------------------------------------------------- /game/GenShin-3.2-Dump.js: -------------------------------------------------------------------------------- 1 | let so = Process.findModuleByName("libil2cpp.so") 2 | let il2cpp_method_get_name = new NativeFunction(so.base.add(0x4260354),'pointer',['pointer']) 3 | let il2cpp_method_get_param = new NativeFunction(so.base.add(0x425FEC4),'pointer',['pointer','int']) 4 | let il2cpp_method_get_return_type = new NativeFunction(so.base.add(0x42306EC),'pointer',['pointer']) 5 | 6 | let il2cpp_class_from_type = new NativeFunction(so.base.add(0x4251ED8),'pointer',['pointer']) 7 | let il2cpp_class_get_name = new NativeFunction(so.base.add(0x4252E48),'pointer',['pointer']) 8 | 9 | 10 | Interceptor.attach(so.base.add(0x4252BE4),{ 11 | onEnter:function (args) { 12 | // console.log("---------il2cpp_class_get_methods--------") 13 | this.class = args[0] 14 | 15 | }, 16 | onLeave:function (ret) { 17 | try{ 18 | let classname = this.class.add(40).readPointer().readCString() 19 | let namespace = this.class.add(120).readPointer().readCString() 20 | let name_ptr = il2cpp_method_get_name(ret) 21 | let ret_type = il2cpp_method_get_return_type(ret) 22 | let ret_type_class = il2cpp_class_from_type(ret_type) 23 | let ret_class_name = il2cpp_class_get_name(ret_type_class) 24 | 25 | //InvokerMethod ret.add(16) methodPointer ret 26 | let parameters_count = ret.add(50).readU8() 27 | let pstr = "(" 28 | for(let idx = 0;idx "+ptr(ret_class_name).readCString()+" "+namespace+"."+classname+"."+ptr(name_ptr).readCString()+pstr) 36 | 37 | }catch (e) { 38 | console.log(e.toString()) 39 | } 40 | 41 | } 42 | }) -------------------------------------------------------------------------------- /game/yuanshenInject.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import frida 4 | 5 | device = frida.get_usb_device() 6 | pid = device.spawn(["com.miHoYo.Yuanshen"]) 7 | session = device.attach(pid) 8 | device.resume(pid) 9 | 10 | scr = """ 11 | var APP_NAME = "com.miHoYo.Yuanshen" 12 | 13 | var module = null; 14 | while(module == null){ 15 | module = Process.findModuleByName("libil2cpp.so"); 16 | } 17 | send(module); 18 | 19 | var unity = Process.findModuleByName("libunity.so"); 20 | send(unity); 21 | 22 | var x = Interceptor.attach(ptr(unity.base).add(0xb2aecc),{ 23 | onEnter:function(args){ 24 | send(args[1]); 25 | },onLeave:function(ret){ 26 | send(ret.sub(1).readUtf8String()); 27 | 28 | } 29 | }); 30 | 31 | Interceptor.attach(Module.findExportByName("libil2cpp.so","il2cpp_class_from_name"),{ 32 | onEnter:function(args){ 33 | //send(args[1].readUtf8String() + " ----- : "+args[2].readUtf8String()); 34 | 35 | },onLeave:function(ret){ 36 | 37 | } 38 | }); 39 | 40 | 41 | // hook SetupMethodsLocked 42 | var p_size = 8; 43 | Interceptor.attach(ptr(module.base).add(0x72F09EC).add(0x204),{ 44 | onEnter:function(args){ 45 | var newMethod = this.context.x20 46 | var pointer = newMethod.readPointer(); 47 | var name = newMethod.add(p_size * 2).readPointer().readCString(); 48 | var klass = newMethod.add(p_size * 3).readPointer(); 49 | var klass_name = klass.add(p_size * 2).readPointer().readCString(); 50 | var klass_paze = klass.add(p_size * 3).readPointer().readCString(); 51 | send(klass_paze+"."+klass_name+":"+name+" -> "+pointer.sub(module.base)); 52 | },onLeave:function(ret){ 53 | 54 | } 55 | }); 56 | 57 | 58 | 59 | """ 60 | 61 | 62 | 63 | 64 | def on_message(message, data): 65 | if message['type'] == 'send': 66 | print("[*] {0}".format(message['payload'])) 67 | else: 68 | print(message) 69 | 70 | 71 | script = session.create_script(scr) 72 | script.on("message", on_message) 73 | script.load() 74 | sys.stdin.read() 75 | 76 | 77 | -------------------------------------------------------------------------------- /lua/GGInjector64.lua: -------------------------------------------------------------------------------- 1 | --- 2 | --- Created by yangtong02. 3 | --- DateTime: 2023/2/9 13:40 4 | --- GG lua 解析64位elf文件 5 | --- 6 | --- 7 | 8 | 9 | sf = string.format 10 | function alert(caption, text) 11 | assert(caption ~= nil, "\n\n>> [alert]: error, caption was nil. <<\n\n") 12 | if text == nil then 13 | text = caption 14 | caption = "[Info]: Notice" 15 | end 16 | gg.alert(caption .. "\n\t- " .. text) 17 | end 18 | function rwmem(address, SizeOrBuffer) 19 | assert(type(address) ~= "string", "\n\n>> [rwmem]: error, address is string. Please check caller. <<\n\n") 20 | assert(address ~= nil, "\n\n>> [rwmem]: error, provided address is nil. <<\n\n") 21 | _rw = {} 22 | if type(SizeOrBuffer) == "number" then 23 | _ = "" 24 | for _ = 1, SizeOrBuffer do _rw[_] = {address = (address - 1) + _, flags = gg.TYPE_BYTE} end 25 | for v, __ in ipairs(gg.getValues(_rw)) do _ = _ .. string.format("%02X", __.value & 0xFF) end 26 | return _ 27 | end 28 | Byte = {} SizeOrBuffer:gsub("..", function(x) 29 | Byte[#Byte + 1] = x _rw[#Byte] = {address = (address - 1) + #Byte, flags = gg.TYPE_BYTE, value = x .. "h"} 30 | end) 31 | gg.setValues(_rw) 32 | end 33 | function rdstr(address, strsz) 34 | assert(address ~= nil, "\n\n>> [rdstr]: error, provided address is nil. <<\n\n") 35 | if strsz == nil or type(strsz) ~= "number" then strsz = 128 end 36 | local str = "" 37 | for _ in rwmem(address, strsz):gmatch("..") do 38 | if _ == "00" then break end 39 | str = str .. string.char(tonumber(_, 16)) 40 | end 41 | return str 42 | end 43 | 44 | --------------------------- 45 | 46 | function getLibraryBase(lib) 47 | print("search ".. lib) 48 | --print(gg.getRangesList(lib)) 49 | for _, __ in pairs(gg.getRangesList(lib)) do 50 | print(sf("start 0x%x ~ 0x%x",__["start"], __["end"])) 51 | --if __["state"] == "Xa" or __["state"] == "Xs" then 52 | -- --print(sf("start 0x%x ~ 0x%x",__["start"], __["end"])) 53 | return __["start"], __["end"] end 54 | --end 55 | return nil 56 | end 57 | function getLibInformation(LibName) 58 | local LibBase = getLibraryBase(LibName) 59 | if LibBase == nil then 60 | print("can't find "..LibName) 61 | end 62 | if LibBase ~= nil then 63 | _ = gg.getValues({ 64 | {address = LibBase, flags = gg.TYPE_DWORD }, -- Magic 65 | -- EI_PAD skipped -- 66 | {address = LibBase + 0x12, flags = gg.TYPE_WORD }, -- Machine 67 | {address = LibBase + 0x20, flags = gg.TYPE_DWORD }, -- Program Header Table (PH) Offset 68 | {address = LibBase + 0x30, flags = gg.TYPE_DWORD }, -- Flags 69 | {address = LibBase + 0x36, flags = gg.TYPE_WORD }, -- Program Header Table (PH) Size Entry 70 | {address = LibBase + 0x38, flags = gg.TYPE_WORD }, -- Number Of Entries In Program Header Table (PH) 71 | }) 72 | local Elf = { -- Elf Information Table Structure-- 73 | Magic = _[1].value, 74 | Machine = _[2].value, 75 | PHOffset = _[3].value, 76 | Flags = _[4].value, 77 | PHSize = _[5].value, 78 | PHNum = _[6].value, 79 | pHdr = {}, 80 | Dyn = {}, 81 | Sym = {}, 82 | vAddress = LibBase 83 | } 84 | print(sf("ELF info Magic %s ,PHOffset 0x%x PHNum 0x%x",Elf.Magic,Elf.PHOffset,Elf.PHSize)) 85 | for _ = 1, Elf.PHNum do -- Parsing Program Header 86 | local _pHdr = LibBase + Elf.PHOffset + (_ * Elf.PHSize) 87 | local pHdr = gg.getValues({ 88 | { address = _pHdr, flags = gg.TYPE_DWORD }, -- p_type 89 | { address = _pHdr + 8, flags = gg.TYPE_DWORD }, -- p_offset 90 | { address = _pHdr + 0x10, flags = gg.TYPE_DWORD }, -- p_vaddr 91 | { address = _pHdr + 0x18, flags = gg.TYPE_DWORD }, -- p_paddr 92 | { address = _pHdr + 0x20, flags = gg.TYPE_DWORD }, -- p_filesz 93 | { address = _pHdr + 0x28, flags = gg.TYPE_DWORD }, -- p_memsz 94 | { address = _pHdr + 4, flags = gg.TYPE_DWORD }, -- p_flags 95 | --{ address = _pHdr + 0x30, flags = gg.TYPE_DWORD }, -- p_align 96 | }) 97 | Elf.pHdr[_] = { -- All data in Program Header now in Elf.pHdr[Elf.PHNum] 98 | p_type = pHdr[1].value, 99 | p_offset = pHdr[2].value, 100 | p_vaddr = pHdr[3].value, 101 | p_paddr = pHdr[4].value, 102 | p_filesz = pHdr[5].value, 103 | p_memsz = pHdr[6].value, 104 | p_flags = pHdr[7].value, 105 | --p_align = pHdr[8].value 106 | } 107 | print(sf("pHdr type %d offset 0x%x p_vaddr 0x%x ",pHdr[1].value,pHdr[2].value,pHdr[3].value)) 108 | end 109 | for _ = 1, Elf.PHNum do -- Parsing Dynamic Segment 110 | if Elf.pHdr[_].p_type == 2 then -- PT_DYNAMIC 111 | local DynCount = 0 112 | while true do 113 | local _Dyn = gg.getValues({ 114 | { address = LibBase + Elf.pHdr[_].p_vaddr + (DynCount * 8), flags = gg.TYPE_DWORD }, -- d_tag 115 | { address = LibBase + Elf.pHdr[_].p_vaddr + 8 + (DynCount * 8), flags = gg.TYPE_DWORD } -- d_ptr / d_val 116 | }) 117 | if _Dyn[1].value == 0 and _Dyn[2].value == 0 then break end -- End of dynamic segment 118 | DynCount = DynCount + 1 -- Keep growing ! 119 | Elf.Dyn[DynCount] = { -- All data in Dynamic Segment now in Elf.Dyn[Section] 120 | d_tag = _Dyn[1].value, 121 | d_val = _Dyn[2].value, 122 | d_ptr = _Dyn[2].value 123 | } 124 | end 125 | end 126 | end 127 | return Elf 128 | end 129 | return nil 130 | end 131 | 132 | function getSymbolAddress(ElfData, symName) 133 | assert(ElfData ~= nil, "\n\n>> [getSymbolAddress]: error, provided ElfData is nil. <<\n\n") 134 | for _ = 1, #ElfData.Dyn do 135 | if tonumber(ElfData.Dyn[_].d_tag) == 4 then nChain = gg.getValues({{address = (ElfData.Dyn[_].d_ptr + 4) + ElfData.vAddress, flags = gg.TYPE_DWORD}})[1].value end 136 | if tonumber(ElfData.Dyn[_].d_tag) == 5 then strtab = ElfData.Dyn[_].d_ptr + ElfData.vAddress end 137 | if tonumber(ElfData.Dyn[_].d_tag) == 6 then symtab = ElfData.Dyn[_].d_ptr + ElfData.vAddress end 138 | end 139 | if nChain ~= nil then 140 | for _ = 1, nChain do 141 | local sym = symtab + (_ * 0x18) 142 | __ = gg.getValues({ 143 | { address = sym, flags = gg.TYPE_DWORD }, -- st_name 144 | { address = sym + 0x4, flags = gg.TYPE_DWORD }, -- st_value 145 | }) 146 | print(rdstr(strtab + __[1].value) .. ", offset:"..sf("0x%x",__[1].value)) 147 | if rdstr(strtab + __[1].value) == symName then 148 | return ElfData.vAddress + __[2].value 149 | end 150 | end 151 | else 152 | print("nChain is nil") 153 | end 154 | 155 | return nil 156 | end 157 | 158 | function getLib(libName) 159 | gg.toast("Searching for '"..libName.."', This may take a while. Please wait...") 160 | local m_lib = getLibInformation(libName) 161 | if m_lib ~= nil then 162 | print(sf("[getLib]: %s Architecture: 0x%08X", libName, m_lib.Machine)) 163 | if m_lib.Machine == 0xb7 then -- Arm 64-bits 164 | return m_lib 165 | end 166 | alert("[Error]: Unsupported Device !", "Currently, only Arm 64-bits device are supported.") 167 | os.exit() 168 | return nil 169 | end 170 | 171 | alert("[Error]: Missing dependencies !", "One of required shared library '" .. libName .. "', has left us in the dark.") 172 | os.exit() 173 | return nil 174 | end 175 | function getSymbol(ElfData, Symname) 176 | gg.toast("Searching for Symbol '"..Symname.."', This may take a while. Please wait...") 177 | local s_address = getSymbolAddress(ElfData, Symname) 178 | if s_address ~= nil then 179 | return s_address 180 | end 181 | 182 | alert("[Error]: Missing dependencies !", "One of required symbol '" .. Symname .. "', has been reported missing.") 183 | os.exit() 184 | return nil 185 | end 186 | 187 | 188 | 189 | 190 | local m_libdl = getLib("libil2cpp.so") 191 | local s_dlopen = getSymbol(m_libdl, "il2cpp_assembly_get_image") 192 | print("find dlopen "..s_dlopen) --------------------------------------------------------------------------------