JavaScript API 手册 | Frida JavaScript-api 中文手册
frida_Android 运行frida常用命令 运行server 1 2 3 4 adb push 名 /data/local/tmp adb root adb shell chmod +x /data/local/tmp/fridaad64 adb shell /data/local/tmp/frida-server
spwan启动
1 Frida -U -f 包名 -l hookbool.js
-U 指定USB设备
-f 用app包名spwn方式启动
-P 指定APP的pid,先要自行启动
-pause 暂停
-l 加载hook脚本
一些frida方法的收集
对hook脚本的初步解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 Java .perform (function ( ){ let MainActivity = Java .use ("packname.MainActivity" ); MainActivity ["isEmu" ].implementation = function ( ) { console .log (`MainActivity.isEmu is called` ); let result = this ["isEmu" ](); console .log (`MainActivity.isEmu result=${result} ` ); return 0 ; }; MainActivity ["g4tk4y" ].implementation = function ( ) { console .log (`MainActivity.g4tk4y is called` ); let result = this ["g4tk4y" ](); console .log (`MainActivity.g4tk4y result=${result} ` ); return result; }; Java .choose ("packname.MainActivity" ,{ onMatch : function (x ){ console .log ("ok" + x); let result = x.g4tk4y (); console .log (result); }, onComplete : function ( ) { console .log ("end" ); } }); var MainActivity = Java .use ("packname.MainActivity" ); MainActivity .funname .overload ('int' ).implementation = function (x ) { console .log ("ok" + x); var result = "" ; return result; }; }) function hook ( ) { Java .perform (function ( ){ let MainActivity = Java .use ("packname.MainActivity" ); MainActivity ["g4tk4y" ].implementation = function ( ) { console .log (`MainActivity.g4tk4y is called` ); let result = this ["g4tk4y" ](); console .log (`MainActivity.g4tk4y result=${result} ` ); return result; }; MainActivity .aaa ("x" ); }) }; (function ( ) { function print_arg (addr ) { try { var module = Process .findRangeByAddress (addr); if (module != null ) return "\n" +hexdump (addr) + "\n" ; return ptr (addr) + "\n" ; } catch (e) { return addr + "\n" ; } } function hook_native_addr (funcPtr, paramsNum ) { var module = Process .findModuleByAddress (funcPtr); try { Interceptor .attach (funcPtr, { onEnter : function (args ) { this .logs = "" ; this .params = []; this .logs =this .logs .concat ("So: " + module .name + " Method: Java_ offset: " + ptr (funcPtr).sub (module .base ) + "\n" ); for (let i = 0 ; i < paramsNum; i++) { this .params .push (args[i]); this .logs =this .logs .concat ("this.args" + i + " onEnter: " + print_arg (args[i])); } }, onLeave : function (retval ) { for (let i = 0 ; i < paramsNum; i++) { this .logs =this .logs .concat ("this.args" + i + " onLeave: " + print_arg (this .params [i])); } this .logs =this .logs .concat ("retval onLeave: " + print_arg (retval) + "\n" ); console .log (this .logs ); } }); } catch (e) { console .log (e); } } hook_native_addr (Module .findBaseAddress ("libdebugme.so" ).add (0xff0 ), 0x1 ); })(); .overload () .overload ('int' ) .overload ('java.lang.Exception' ) .overload ('android.content.Context' ) .overload ('java.lang.String' ) .overload ('android.content.Context' , 'java.lang.String' ) .overload ('java.io.BufferedInputStream' , 'java.io.BufferedInputStream' , 'int' ) .overload ('android.content.Context' , 'java.lang.String' , 'java.lang.String' , 'java.lang.String' )
上面的方法都是被动调用,下面一段脚本是主动调用脚本
1 2 3 4 5 6 Java .perform (function ( ){ var main =Java .use ("com.moible.r15.main" ).$new(); var input = "66.666s" ; var result = main.getit (input); console .log (result); })
有时候我们想要把一个调用方法封装在一个函数里面,在之后手动调用,然而在我们后面调用时可能会出现报错 这个时候我们要使用一下代码使handle能够在别的实例中运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 send (Java .available ); function get ( ){ Java .perform (function ( ) { send (Java .androidVersion ); send (Java .isMainThread ()); Java .scheduleOnMainThread (function ( ) { send (Java .isMainThread ()); var main = Java .use ("com.moible.r15.main" ).$new(); var input = "66.666s" ; var result = main.getit (input); console .log (result); }); }); }
frida_win_processes frida可以hookwindows上的的进程,详见功能 |Frida • 世界一流的动态仪表工具包
1 frida -l hook.js -n [name]
这里的name是进程的程序名字。
简单列出frida的一些process API。参考bbs.kanxue.com/article-342.htm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var process = Process .findModuleByAddress (address) var process = Process .findModuleByName () process.base process.name process.size process.path setInterval (f, delay) ptr (addr) hexdump (addr,{offset :0 ,length :64 ,header :true ,absi :true }) this .context
hook脚本来自Windows | Frida • A world-class dynamic instrumentation toolkit 通用脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 var process = Process .findModuleByName ("xx.exe" ); var baseaddr = process.base Interceptor .attach (baseaddr.add (0x0000 ), { onEnter (args ) { console .log ('' ); console .log ('[+] Called SetAesDeCrypt0' + f); console .log ('[+] Input: ' + args[0 ]); aegs[0 ] = 111 ; dumpAddr ('Input' , args[1 ], args[3 ].toInt32 ()); this .outptr = args[2 ]; this .outsize = args[3 ].toInt32 (); var rdx=this .context .rdx ; console .log (hexdump (ptr (rdx),{length : 16 ,ansi :true })); }, onLeave (retval ) { dumpAddr ('Output' , this .outptr , this .outsize ); console .log ('[+] Returned from setAesDecrypt0: ' + retval); } }); function dumpAddr (info, addr, size ) { if (addr.isNull ()) return ; console .log ('Data dump ' + info + ' :' ); const buf = addr.readByteArray (size); console .log (hexdump (buf, { offset : 0 , length : size, header : true , ansi : false })); } function resolveAddress (addr ) { const idaBase = ptr ('0x1FEE0000' ); const offset = ptr (addr).sub (idaBase); const result = baseAddr.add (offset); console .log ('[+] New addr=' + result); return result; }
手动调用函数
1 2 var f = new NativeFunction (ptr (addr), 'void' , ['int' ]); f (1911 );
实用脚本总结 hookjava函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Java .perform (function ( ){ let MainActivity = Java .use ("com.exa.n.MainActivity" ); MainActivity ["func" ].implementation = function (data ) { console .log (`func is called: data=${data} ` ); var result; result = this ["func" ](data); return result; }; }) function printstack ( ) { console .log (Java .use ("android.util.Log" ).getStackTraceString (Java .use ("java.lang.Exception" ).$new()));}
hookso 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var libapp = null ;function onLibappLoaded ( ) { const fn_addr = 0x2FE7F0 ; Interceptor .attach (libapp.add (fn_addr), { onEnter : function ( ) { var rdi = this .context .rdi ; console .log (rdi) console .log (hexdump (ptr (rdi), { length : 100 , ansi : true })) } }); } function tryLoadLibapp ( ) { libapp = Module .findBaseAddress ('libapp.so' ); if (libapp === null ) setTimeout (tryLoadLibapp, 500 ); else onLibappLoaded (); } tryLoadLibapp ();
windows一般dump寄存器的值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var inter=setInterval (function ( ) { var process = Process .findModuleByName ("1.exe" ); var baseaddr = process.base console .log ("base" +baseaddr); clearInterval (inter); console .log (hexdump (baseaddr.add (0x005160 ),{length :255 ,ansi :true })) Interceptor .attach (baseaddr.add (0x001E7F ), { onEnter : function (args ) { var rax=this .context .rax ; console .log ("secret" +rax); console .log (hexdump (ptr (rax),{length : 48 ,ansi :true })); }}), },1 )
修改寄存器的值 (用python处理数据)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function hookinput (data ){ var baseaddr = Module .findBaseAddress ("applib.so" ); Interceptor .attach (baseaddr.add (0x001E7F ), { onEnter : function (args ) { var rdi=this .context .rdi ; console .log ("hook" +rdi); console .log (hexdump (ptr (rax),{length : 48 ,ansi :true })); rdi.writeByteArray (data) console .log ("edit_after" +rdi); console .log (hexdump (ptr (rax),{length : 48 ,ansi :true })); }}) } RTCPeerConnection .export = { hookinput :hookinput }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import timeimport fridaimport sysdef on_message (message, data ): if message['type' ] == 'send' : print ("[*] {0}" .format (message['payload' ])) else : print (message) device8 = frida.get_usb_device() pid = device8.spawn("com.example.aaar" ) device8.resume(pid) time.sleep(1 ) session = device8.attach(pid) with open ("hook.js" ) as f: script = session.create_script(f.read()) script.on('message' , on_message) print ('[*] Hook Start Running' )script.load() ad = "" .join('a' * 42 ) test = f"flag{{{ad} }}" input_arr_byte = bytearray (test.encode()) data = list (map (int ,input_arr_byte)) script.export.inputhook(data)
单js脚本处理,适用于简单数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function stringToAsciiArray (str ) { return Array .from (str).map (char => char.charCodeAt (0 )); } const input = "flag{}" ;const data = stringToAsciiArray (input);function hookinput (data ){ var baseaddr = Module .findBaseAddress ("libflutter.so" ); Interceptor .attach (baseaddr.add (0x001E7F ), { onEnter : function (args ) { var rdi=this .context .rdi ; console .log ("hook" +rdi); console .log (hexdump (ptr (rax),{length : 48 ,ansi :true })); rdi.writeByteArray (data) console .log ("edit_after" +rdi); console .log (hexdump (ptr (rax),{length : 48 ,ansi :true })); }}) } hookinput (data);
文件删除前转移,用于脱壳 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 File .delete .implementation = function ( ) { var filePath = this .getAbsolutePath (); console .log ("文件将在删除之前被保存: " + filePath); var newFile = Java .use ('java.io.File' ); var destPath = "/data/data/com.nobody.zunjia/files/savedDexFile.dex" ; var sourceFile = this ; var inputStream = Java .use ('java.io.FileInputStream' ).$new(sourceFile); var outputStream = Java .use ('java.io.FileOutputStream' ).$new(destPath); var buffer = Java .array ('byte' , [1024 ]); var bytesRead; while ((bytesRead = inputStream.read (buffer)) !== -1 ) { outputStream.write (buffer, 0 , bytesRead); } inputStream.close (); outputStream.close (); console .log ("文件已保存到: " + destPath); return this .delete (); };
hook initarray,替换函数 d3ctf的一段模板,
原理是so的加载流程,liberaryLoad ->liberaryLoad0->dlopen(android_dlopen_ext)->__dl__ZN6soinfo17call_constructorsEv。此时so已经加载,然后会执行init_array段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 function findSo (name ){ var lib=null ; console .log (`finding ${name} ` ); try { lib = Process .findModuleByName (name) if (lib === null ) { setTimeout (findSo,200 ,name); } else { console .log (`found ${name} at ${lib.base} ` ); return lib; } } catch { } } function hookI (name ) { var lib = findSo (name); try { var destFuncAddr = lib.base .add (0x08922A0 ); Interceptor .replace (destFuncAddr, new NativeCallback (function ( ) { console .log (`replace: ${name} func: ${destFuncAddr} ` ); return 0 ; }, 'int' , [])) } catch { } } function hookInitArray (name ) { var linkermodule = Process .getModuleByName ("linker64" ); var call_function_addr = null ; var symbols = linkermodule.enumerateSymbols (); for (var i = 0 ; i < symbols.length ; i++) { var symbol = symbols[i]; if (symbol.name .indexOf ("__dl__ZN6soinfo17call_constructorsEv" ) != -1 ) { call_function_addr = symbol.address ; console .log ("call_function_addr:" + call_function_addr); Interceptor .attach (call_function_addr, { onEnter : function (args ) { console .log ("call_constructors" ); hookI (name); } }) return ; } } } hookInitArray ('libMediaPlayer.so' );
追踪流程定位函数 r3ctf一道题的模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 const FUNC_OFFSET = 0x17C0 ; const pow = [0x89 , 0xc6 , 0xbf , 0x02 , 0x00 , 0x00 , 0x00 , 0xe8 ] var powFlag = 0 ; var shift = []; const delta = [0x48 , 0x89 , 0xc7 , 0xff , 0xd1 ]; var deltaFlag = 0 ; var deltaData = 0 ;const sbox = [0x89 , 0xce , 0x48 , 0x89 , 0xc7 , 0x41 , 0xff , 0xd0 ]; var sboxFlag = 0 ;var sboxData = [];function matchPattern (code,pattern ) { for (let i = 0 ; i < pattern.length ; i++) { if (code[i] !== pattern[i]) return false ; } return true ; } const moduleBase = Process .getModuleByName ("beatme" ).base ;const targetAddr = moduleBase.add (FUNC_OFFSET );console .log ("[*] Waiting to reach function at" , targetAddr);Interceptor .attach (targetAddr, { onEnter (args ) { try { console .log ("[+] Entered target function, starting Stalker at" , targetAddr); console .log (ptr (args[1 ])) Stalker .follow (Process .getCurrentThreadId (), { transform (iterator ) { let instruction; while ((instruction = iterator.next ()) !== null ) { const addr = instruction.address ; const codeBytes = Memory .readByteArray (addr, 8 ); const code = new Uint8Array (codeBytes); if ((powFlag < 4 ) && matchPattern (code,pow)) { console .log ("[*] Match pow found at:" , addr.sub (moduleBase)); console .log (`[${addr} ] ${instruction} ` ); iterator.putCallout (function (context ) { if (powFlag >=4 ) return ; var rax = context.rax ; console .log (`shift: EAX = ${rax.toString(16 )} ` ); shift[powFlag++] = rax; }) } else if (!deltaFlag && matchPattern (code,delta)) { console .log ("[*] Match delta found at:" , addr.sub (moduleBase)); console .log (`[${addr} ] ${instruction} ` ); iterator.putCallout (function (context ) { if (deltaFlag != 0 ) return ; var rsi = context.rsi ; console .log (`delta: RSI = ${rsi.toString(16 )} ` ); deltaData = rsi; deltaFlag++; }) } else if (!sboxFlag && matchPattern (code,sbox)) { console .log ("[*] Match sbox found at:" , addr.sub (moduleBase)); console .log (`[${addr} ] ${instruction} ` ); iterator.putCallout (function (context ) { if (sboxFlag != 0 )return ; var rax = context.rax ; console .log (`sbox: RAX = ${rax.toString(16 )} ` ); for (let i = 0 ; i < 256 ; i++) { const value = Memory .readU32 (rax.add (i * 4 )); sboxData.push (value); sboxFlag++; } }) } iterator.keep (); } } }); } catch (e) { console .log (e); } }, onLeave : function (args ) { console .log (sboxData) console .log (shift);console .log (deltaData); } });
读系统文件 1 2 3 4 function reada ( ){ var f = File .readAllText ("/proc/self/task" ); console .log (f) }
hook so加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function hook ( ) { var addr = null ; try { var module = Process .getModuleByName ("libc.so" ); addr = module .getExportByName ("dlopen" ); } catch (e) { console .log (e); } console .log ("dlopen:" , addr); Interceptor .attach (addr, { onEnter : function (args ) { var loadName = args[0 ].readCString (); console .log ("dlopen: " , loadName); }, onLeave : function (retval ) { console .log ("handle:" , retval); } }); var android_dlopen_ext = module .getExportByName ("android_dlopen_ext" ); console .log ("android_dlopen_ext:" , android_dlopen_ext); Interceptor .attach (android_dlopen_ext, { onEnter : function (args ) { this .call_hook = false ; var so_name = ptr (args[0 ]).readCString (); console .log ("android_dlopen_ext:" , so_name); }, onLeave : function (retval ) { } }); }
调用native函数,获取缓冲区数据 d3ctf一道题的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function getpq ( ){ var libapp = findSo ("libD3piano.so" ).base ; const fn_addr = 0x29AA0 ; console .log ("hook" +libapp); Interceptor .attach (libapp.add (fn_addr), { onEnter : function ( ) { try { var x1 = this .context .x1 ; var x2 = this .context .x2 ; var gmpz_get_str = new NativeFunction (libapp.add (0x5CFD0 ),"pointer" ,["pointer" ,"int" ,"pointer" ]); var mallocAddrp = Memory .alloc (0x1000 ); var mallocAddrq = Memory .alloc (0x1000 ); var p = gmpz_get_str (mallocAddrp, 16 , x1); var q = gmpz_get_str (mallocAddrq, 16 , x2); console .log (mallocAddrp.readCString ()); console .log (mallocAddrq.readCString ()); }catch (e){ console .log (e); } } }); }
虚拟机操作hook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 function toHexUnsigned (val ) { return "0x" + (val >>> 0 ).toString (16 ); } let result = toHexUnsigned (0x38373635 << 3 );console .log ("结果:" , result); function findSo (name ){ var lib=null ; console .log (`finding ${name} ` ); try { lib = Process .findModuleByName (name) if (lib === null ) { setTimeout (findSo,200 ,name); } else { console .log (`found ${name} at ${lib.base} ` ); return lib; } } catch { } } var tmp =0 ;function getpq ( ) { var libapp = findSo ("easyvm.exe" ).base ; const fn_addr = 0x1194B ; console .log ("hook" + libapp); setInterval (() => { Interceptor .attach (libapp.add (fn_addr), { onEnter : function ( ) { try { var p1 = this .context .rax ; var p2 = this .context .rbp ; var p2var = p2.sub (0x74 ).readU32 (); tmp = p1; console .log (`${p1} ^ ${p2var} == ${toHexUnsigned(p1 ^ p2var)} ` ) } catch (e) { console .log (e); } } }); Interceptor .attach (libapp.add (0x1165B ), { onEnter : function ( ) { try { var p1 = this .context .rdx ; var p2 = this .context .rcx ; console .log (`${p1} >> ${p2} == ${toHexUnsigned(p1 >> p2)} ` ) } catch (e) { console .log (e); } } }); Interceptor .attach (libapp.add (0x115B5 ), { onEnter : function ( ) { try { var p1 = this .context .rdx ; var p2 = this .context .rcx ; tmp =p2 console .log (`${p1} << ${p2} == ${toHexUnsigned(p1 << p2)} ` ) } catch (e) { console .log (e); } } }); Interceptor .attach (libapp.add (0x10EBC ), { onEnter : function ( ) { try { var p1 = this .context .rax ; var p2 = this .context .rbp ; var p2var = p2.sub (0x8c ).readU32 (); console .log (`${p1} % ${p2var} == ${toHexUnsigned(p1 % p2var)} ` ) } catch (e) { console .log (e); } } }); Interceptor .attach (libapp.add (0x10EBC ), { onEnter : function ( ) { try { var p1 = this .context .rax ; var p2 = this .context .rbp ; var p2var = p2.sub (0x94 ).readU32 (); console .log (`${p1} // ${p2var} == ${toHexUnsigned(p1 / p2var)} ` ) } catch (e) { console .log (e); } } }); Interceptor .attach (libapp.add (0x10D63 ), { onEnter : function ( ) { try { var p1 = this .context .rax ; var p2 = this .context .rbp ; var p2var = p2.sub (0x9c ).readU32 (); console .log (`${p1} * ${p2var} == ${toHexUnsigned(p1 * p2var)} ` ) } catch (e) { console .log (e); } } }); Interceptor .attach (libapp.add (0x010CC6 ), { onEnter : function ( ) { try { var p1 = this .context .rax ; var p2 = this .context .rbp ; var p2var = p2.sub (0xa4 ).readU32 (); console .log (`${p1} - ${p2var} == ${toHexUnsigned(p1.toUInt32() - p2var.toUInt32())} ` ); } catch (e) { console .log (e); } } }); Interceptor .attach (libapp.add (0x10C2A ), { onEnter : function ( ) { try { var p1 = this .context .rax ; var p2 = this .context .rdx ; console .log (`${p1} + ${p2} == ${toHexUnsigned(p1.toUInt32() + p2.toUInt32())} ` ) } catch (e) { console .log (e); } } }); }, 500 ); } getpq ();
创建函数(直接返回) 1 2 3 4 5 6 7 8 9 function create_fake_pthread_create ( ) { const fake_pthread_create = Memory .alloc (4096 ) Memory .protect (fake_pthread_create, 4096 , "rwx" ) Memory .patchCode (fake_pthread_create, 4096 , code => { const cw = new Arm64Writer (code, { pc : ptr (fake_pthread_create) }) cw.putRet () }) return fake_pthread_create }
创建函数(死锁) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function fakePersistentThread ( ) { const codeAddr = Memory .alloc (0x1000 ); Memory .protect (codeAddr, 0x1000 , 'rwx' ); const cw = new Arm64Writer (codeAddr, { pc : codeAddr }); cw.putBLabel ('loop' ); cw.putNop (); cw.putNop (); cw.putBCondLabel ('al' ,'loop' ); return codeAddr; }