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