Python爬蟲之app逆向 frida javascript api手冊


 

frida

什么是frida不用多說了,前面我有篇文章   對app的反爬測試之apk逆向分析-frida繞過ssl pinning檢測     已經介紹過了,   不過里面用到的js腳本,貌似挺屌的是吧,那么做開發的肯定知道,想一直用同一套js腳本hook各種app肯定是不行的,那么我們做開發的,肯定想自己寫hook腳本,然后讀了別人的腳本,發現看不太懂啊,很多方法啥意思都不知道,其實,那些hookj腳本是javascript語法,有點不同的是,里面調用了很多frida 的api,所以就不太容易看懂腳本啥意思,那么我們要想自己寫腳本,肯定要學會那些api的使用了

 

 

javascript api

 

以下內容轉自看雪大佬  安全鳥一起飛  的frida手冊指南 

 

原文地址:

Frida官方手冊 - JavaScript API(篇一)

Frida官方手冊 - JavaScript API(篇二)

Frida官方手冊 - JavaScript API(篇三)

 

篇一

 

JavaScript API

目錄

  1. Global
  2. console
  3. rpc
  4. Frida
  5. Process
  6. Module
  7. ModuleMap
  8. Memory
  9. MemoryAccessMonitor
  10. Thread
  11. Int64
  12. UInt64
  13. NativePointer
  14. NativeFunction
  15. NativeCallback
  16. SystemFunction
  17. Socket
  18. SocketListener
  19. SocketConnection
  20. IOStream
  21. InputStream
  22. OutputStream
  23. UnixInputStream
  24. UnixOutputStream
  25. Win32InputStream
  26. Win32OutputStream
  27. File
  28. SqliteDatabase
  29. SqliteStatement
  30. Interceptor
  31. Stalker
  32. ApiResolver
  33. DebugSymbol
  34. Instruction
  35. ObjC
  36. Java
  37. WeakRef
  38. X86Writer
  39. X86Relocator
  40. X86_enum_types
  41. ArmWriter
  42. ArmRelocation
  43. ThumbWriter
  44. ThumbRelocator
  45. ARM_enum_types
  46. Arm64Writer
  47. Arm64Relocator
  48. AArch64_enum_types
  49. MipsWriter
  50. MipsRelocator
  51. Mips_enum_types

Global

  • hexdump(target[, options]): 把一個 ArrayBuffer 或者 NativePointer 的target變量,附加一些 options 屬性,按照指定格式進行輸出,比如:
  • int64(v): new Int64(v) 的縮寫格式
  • uint64(v): new UInt64(v) 的縮寫格式
  • ptr(s): new NativePointer(s) 的縮寫格式
  • NULL ptr(“0”) 的縮寫格式
  • recv([type, ]callback): 注冊一個回調,當下次有消息到來的時候會收到回調消息,可選參數 type 相當於一個過濾器,表示只接收這種類型的消息。需要注意的一點是, 這個消息回調是一次性的, 收到一個消息之后,如果需要繼續接收消息,那就需要重新調用一個 recv
  • send(message[, data]): 從目標進程中往主控端發 message(必須是可以序列換成Json的),如果你還有二進制數據需要附帶發送(比如使用 Memory.readByteArray 拷貝的內存數據),就把這個附加數據填入 data 參數,但是有個要求,就是 data 參數必須是一個 ArrayBuffer 或者是一個整形數組(數值是 0-255)
  • setTimeout(fn, delay): 在延遲 delay 毫秒之后,調用 fn,這個調用會返回一個ID,這個ID可以傳遞給 clearTimeout 用來進行調用取消。
  • clearTimeout(id): 取消通過 setTimeout 發起的延遲調用
  • setInterval(fn, delay): 每隔 delay 毫秒調用一次 fn,返回一個ID,這個ID可以傳給 clearInterval 進行調用取消。
  • clearInterval(id): 取消通過 setInterval 發起的調用

console

  • console.log(line), console.warn(line), console.error(line): 向標准輸入輸出界面寫入 line 字符串。 比如:使用 Frida-Python 的時候就輸出到 stdout 或者 stderr,使用 frida-qml 的時候則輸出到 qDebug,如果輸出的是一個ArrayBuffer,會以默認參數自動調用 hexdump 進行格式化輸出。

rpc

  • rpc.exports: 可以在你的程序中導出一些 RPC-Style API函數,Key指定導出的名稱,Value指定導出的函數,函數可以直接返回一個值,也可以是異步方式以 Promise 的方式返回,舉個例子:
  • 對於Python主控端可以使用下面這樣的腳本使用導出的函數:
  • 在上面這個例子里面,我們使用 script.on(‘message’, on_message) 來監控任何來自目標進程的消息,消息監控可以來自 script 和 session 兩個方面,比如,如果你想要監控目標進程的退出,可以使用下面這個語句 session.on(‘detached’, my_function)

Frida

  • Frida.version: 包含當前Frida的版本信息

Process

  • Process.arch: CPU架構信息,取值范圍:ia32、x64、arm、arm64
  • Process.platform: 平台信息,取值范圍:windows、darwin、linux、qnx
  • Process.pageSize: 虛擬內存頁面大小,主要用來輔助增加腳本可移植性
  • Process.pointerSize: 指針占用的內存大小,主要用來輔助增加腳本可移植性
  • Process.codeSigningPolicy: 取值范圍是 optional 或者 required,后者表示Frida會盡力避免修改內存中的代碼,並且不會執行未簽名的代碼。默認值是 optional,除非是在 Gadget 模式下通過配置文件來使用 required,通過這個屬性可以確定 Interceptor API 是否有限制,確定代碼修改或者執行未簽名代碼是否安全。(譯者注:這個目前沒有實驗清楚,可以參考原文)
  • Process.isDebuggerAttached(): 確定當前是否有調試器附加
  • Process.getCurrentThreadId(): 獲取當前線程ID
  • Process.enumerateThreads(callbacks): 枚舉所有線程,每次枚舉到一個線程就執行回調類callbacks:
    Process.enumerateThreadSync(): enumerateThreads()的同步版本,返回線程對象數組
    • onMatch: function(thread): 當枚舉到一個線程的時候,就調用這個函數,其中thread參數包含 :
      1. id,線程ID
      2. state,線程狀態,取之范圍是 running, stopped, waiting, uninterruptible, halted
      3. context, 包含 pc, sp,分別代表 EIP/RIP/PC 和 ESP/RSP/SP,分別對應於 ia32/x64/arm平台,其他的寄存器也都有,比如 eax, rax, r0, x0 等。
      4. 函數可以直接返回 stop 來停止枚舉。
    • onComplete: function(): 當所有的線程枚舉都完成的時候調用。
  • Process.findModuleByAddress(address), Process.getModuleByAddress(address), Process.findModuleByName(name), Process.getModuleByName(name): 根據地址或者名稱來查找模塊,如果找不到這樣的模塊,find開頭的函數返回 null,get開頭的函數會拋出異常。
  • Process.enumerateModules(callbacks): 枚舉已經加載的模塊,枚舉到模塊之后調用回調對象:
    Process.enumerateModulesSync(): enumerateModules() 函數的同步版本,返回模塊對象數組
    • onMatch: function(module): 枚舉到一個模塊的時候調用,module對象包含如下字段:
      1. name, 模塊名
      2. base, 基地址
      3. size,模塊大小
      4. path,模塊路徑
      5. 函數可以返回 stop 來停止枚舉 。
    • onComplete: function(): 當所有的模塊枚舉完成的時候調用。
  • Process.findRangeByAddress(address), Process.getRangeByAddress(address): 返回一個內存塊對象, 如果在這個address找不到內存塊對象,那么 findRangeByAddress() 返回 null 而 getRangeByAddress 則拋出異常。
  • Process.numerateRanges(protection | specifier, callbacks): 枚舉指定 protection 類型的內存塊,以指定形式的字符串給出:rwx,而 rw- 表示最少是可讀可寫,也可以用分類符,里面包含 protection 這個Key,取值就是前面提到的rwx,還有一個 coalesce 這個Key,表示是否要把位置相鄰並且屬性相同的內存塊合並給出結果,枚舉過程中回調 callbacks 對象:
    Process.enumerateRangesSync(protection | specifier): enumerateRanges()函數的同步版本,返回內存塊數組
    • onMatch: function(range): 每次枚舉到一個內存塊都回調回來,其中Range對象包含如下屬性:
      1. base:基地址
      2. size:內存塊大小
      3. protection:保護屬性
      4. file:(如果有的話)內存映射文件:
        4.1 path,文件路徑
        4.2 offset,文件內偏移
      5. 如果要停止枚舉過程,直接返回 stop 即可
    • onComplete: function(): 所有內存塊枚舉完成之后會回調
  • Process.enumerateMallocRanges(callbacks): 用於枚舉在系統堆上申請的內存塊
  • Process.enumerateMallocRangesSync(protection): Process.enumerateMallocRanges() 的同步版本
  • Process.setExceptionHandler(callback): 在進程內安裝一個異常處理函數(Native Exception),回調函數會在目標進程本身的異常處理函數之前調用 ,回調函數只有一個參數 details,包含以下幾個屬性:
    • type,取值為下列之一:
      1. abort
      2. access-violation
      3. guard-page
      4. illegal-instruction
      5. stack-overflow
      6. arithmetic
      7. breakpoint
      8. single-step
      9. system
    • address,異常發生的地址,NativePointer
    • memory,如果這個對象不為空,則會包含下面這些屬性:
      1. operation: 引發一場的操作類型,取值范圍是 read, write 或者 execute
      2. address: 操作發生異常的地址,NativePointer
    • context,包含 pc 和 sp 的NativePointer,分別代表指令指針和堆棧指針
    • nativeContext,基於操作系統定義的異常上下文信息的NativePointer,在 context 里面的信息不夠用的時候,可以考慮用這個指針,但是一般不建議使用(譯者注:估計是考慮到可移植性或者穩定性)
    • 捕獲到異常之后,怎么使用就看你自己了,比如可以把異常信息寫到日志里面,然后發送個信息給主控端,然后同步等待主控端的響應之后處理,或者直接修改異常信息里面包含的寄存器的值,嘗試恢復掉異常,繼續執行。如果你處理了異常信息,那么這個異常回調里面你要返回 true,Frida會把異常交給進程異常處理函數處理,如果到最后都沒人去處理這個異常,就直接結束目標進程。

Module

  • Module.emuerateImports(name, callbacks): 枚舉模塊 name 的導入表,枚舉到一個導入項的時候回調callbacks, callbacks包含下面2個回調:
    Module.eumerateImportsSync(name): enumerateImports()的同步版本
    • onMatch: function(imp): 枚舉到一個導入項到時候會被調用,imp包含如下的字段:
      1. type,導入項的類型, 取值范圍是 function或者variable
      2. name,導入項的名稱
      3. module,模塊名稱
      4. address,導入項的絕對地址
      5. 以上所有的屬性字段,只有 name 字段是一定會有,剩余的其他字段不能保證都有,底層會盡量保證每個字段都能給出數據,但是不能保證一定能拿到數據,onMatch函數可以返回字符串 stop 表示要停止枚舉。
    • onComplete: function(): 當所有的導入表項都枚舉完成的時候會回調
  • Module.emuerateExports(name, callbacks): 枚舉指定模塊 name 的導出表項,結果用 callbacks 進行回調:
    Module.enumerateExportsSync(): Module.enumerateExports()的同步版本
    • onMatch: function(exp): 其中 exp 代表枚舉到的一個導出項,包含如下幾個字段:
      1. type,導出項類型,取值范圍是 function或者variable
      2. name,導出項名稱
      3. address,導出項的絕對地址,NativePointer
      4. 函數返回 stop 的時候表示停止枚舉過程
    • onComplete: function(): 枚舉完成回調
  • Module.enumerateSymbols(name, callbacks): 枚舉指定模塊中包含的符號,枚舉結果通過回調進行通知:
    • onMatch: function(sym): 其中 sym 包含下面幾個字段:
      • isGlobal,布爾值,表示符號是否全局可見
      • type,符號的類型,取值是下面其中一種:
        1. unknown
        2. undefined
        3. absolute
        4. section
        5. prebound-undefined
        6. indirect
      • section,如果這個字段不為空的話,那這個字段包含下面幾個屬性:
        1. id,小節序號,段名,節名
        2. protection,保護屬性類型, rwx這樣的屬性
      • name,符號名稱
      • address,符號的絕對地址,NativePointer
      • 這個函數返回 stop 的時候,表示要結束枚舉過程
    • Module.enumerateSymbolsSync(name): Module.enumerateSymbols() 的同步版本
  • Module.enumerateRanges(name, protection, callbacks): 功能基本等同於 Process.enumerateRanges(),只不過多了一個模塊名限定了枚舉的范圍
  • Module.enumerateRangesSync(name, protection): Module.enumerateRanges() 的同步版本
  • Module.findBaseAddress(name): 獲取指定模塊的基地址
  • Module.findExportByName(module | null, exp): 返回模塊module 內的導出項的絕對地址,如果模塊名不確定,第一個參數傳入 null,這種情況下會增大查找開銷,盡量不要使用。

ModuleMap

  • new ModuleMap([filter]): 可以理解為內存模塊快照,主要目的是可以作為一個模塊速查表,比如你可以用這個快照來快速定位一個具體的地址是屬於哪個模塊。創建ModuleMap的時候,就是對目標進程當前加載的模塊的信息作一個快照,后續想要更新這個快照信息的時候,可以使用 update 進行更新。 這個 filter 參數是可選的,主要是用來過濾你關心的模塊,可以用來縮小快照的范圍(注意:filter是過濾函數,不是字符串參數),為了讓模塊進入這個快照里,過濾函數的返回值要設置為true,反之設為false,如果后續內存中的模塊加載信息更新了, 還會繼續調用這個filter函數。
  • has(address): 檢查 address 這個地址是不是包含在ModuleMap里面,返回bool值
  • find(address), get(address): 返回 address 地址所指向的模塊對象詳細信息,如果不存在 find 返回null,get 直接會拋出異常,具體的返回的對象的詳細信息,可以參考 Process.enumerateModules()
  • findName(address), getName(address), findPath(address), getPath(address): 功能跟 find(), get() 類似,但是只返回 name 或者 path 字段,可以省點開銷
  • update(): 更新ModuleMap信息,如果有模塊加載或者卸載,最好調用一次,免得使用舊數據。

Memory

  • Memory.scan(address, size, pattern, callbacks): 在 address 開始的地址,size 大小的內存范圍內以 pattern 這個模式進行匹配查找,查找到一個內存塊就回調callbacks,各個參數詳細如下:
    Memory.scanSync(address, size, pattern): 內存掃描 scan() 的同步版本
    • pattern 比如使用13 37 ?? ff來匹配0x13開頭,然后跟着0x37,然后是任意字節內容,接着是0xff這樣的內存塊
    • callbacks 是掃描函數回調對象:
      1. onMatch: function(address, size): 掃描到一個內存塊,起始地址是address,大小size的內存塊,返回 stop 表示停止掃描
      2. onError: function(reason): 掃描內存的時候出現內存訪問異常的時候回調
      3. onComplete: function(): 內存掃描完畢的時候調用
  • Memory.alloc(size): 在目標進程中的堆上申請size大小的內存,並且會按照Process.pageSize對齊,返回一個NativePointer,並且申請的內存如果在JavaScript里面沒有對這個內存的使用的時候會自動釋放的。也就是說,如果你不想要這個內存被釋放,你需要自己保存一份對這個內存塊的引用。
  • Memory.copy(dust, src, n): 就像是memcpy
  • Memory.dup(address, size): 等價於 Memory.alloc()和Memory.copy()的組合。
  • Memory.protect(address, size, protection): 更新address開始,size大小的內存塊的保護屬性,protection 的取值參考 Process.enumerateRanges(),比如:Memory.protect(ptr(“0x123”, 4096, ‘rw-‘));
  • Memory.patchCode(address, size, apply): apply是一個回調函數,這個函數是用來在 address 開始的地址和 size 大小的地方開始Patch的時候調用,回調參數是一個NativePointer的可寫指針,需要在apply回調函數里面要完成patch代碼的寫入,注意,這個可寫的指針地址不一定和上面的address是同一個地址,因為在有的系統上是不允許直接寫入代碼段的,需要先寫入到一個臨時的地方,然后在影射到響應代碼段上,(比如 iOS上, 會引發進程丟失 CS_VALID 狀態),比如:
  • 下面是接着是一些數據類型讀寫:
    1. Memory.readPointer(address)
    2. Memory.writePointer(address, ptr)
    3. Memory.readS8, Memory.readU8

MemoryAccessMonitor

  • MemoryAccessMonitor.enable(ranges, callbacks): 監控一個或多個內存塊的訪問,在觸發到內存訪問的時候發出通知。ranges 要么是一個單獨的內存塊,要么是一個內存塊數組,每個內存塊包含如下屬性:

    • base: 觸發內存訪問的NativePointer地址
    • size: 被觸發訪問的內存塊的大小
    • callbacks: 回調對象結構:
    • onAccess: function(details): 發生訪問的時候同步調用這個函數,details對象包含如下屬性:
      1. operation: 觸發內存訪問的操作類型,取值范圍是 read, write 或者 execute
      2. from: 觸發內存訪問的指令地址,NativePointer
      3. address: 被訪問的內存地址
      4. rangeIndex: 被訪問的內存塊的索引,就是調用MemoryAccessMonitor.enable()的時候指定的內存塊序號
      5. pageIndex: 在被監控內存塊范圍內的頁面序號
      6. pagesCompleted: 到目前為止已經發生過內存訪問的頁面的個數(已經發生過內存訪問的頁面將不再進行監控)
      7. pagesTotal: 初始指定的需要監控的內存頁面總數
  • MemoryAccessMonitor.disable(): 停止監控頁面訪問操作

Thread

    • Thread.backtrace([context, backtracer]): 抓取當前線程的調用堆棧,並以 NativePointer 指針數組的形式返回。
      Thread.sleep(delay): 線程暫停 delay 秒執行
      1. 如果你是在 Interceptor.onEnter或者Interceptor.onLeave 中調用這個函數的話,那就必須要把 this.context 作為參數傳入,這樣就能拿到更佳精准的堆棧調用信息,如果省略這個參數不傳,那就意味着從當前堆棧的位置開始抓取,這樣的抓取效果可能不會很好,因為有不少V8引擎的棧幀的干擾。
      2. 第二個可選參數 backtracer,表示使用哪種類型的堆棧抓取算法,目前的取值范圍是 Backtracer.FUZZY 和 Backtracer.ACCURATE,目前后者是默認模式。精確抓取模式下,如果如果程序是調試器友好(比如是標准編譯器編譯的結果,沒有什么反調試技巧)或者有符號表的支持,抓取效果是最好的,而模糊抓取模式下,抓取器會在堆棧上嘗試抓取,並且會猜測里面包含的返回地址,也就是說中間可能包含一些錯誤的信息,但是這種模式基本能在任何二進制程序里面工作:

 

篇二

JavaScript API

Int64

  • new Int64(v): 以v為參數,創建一個Int64對象,v可以是一個數值,也可以是一個字符串形式的數值表示,也可以使用 Int64(v) 這種簡單的方式。
  • add(rhs), sub(rhs), and(rhs), or(rhs), xor(rhs): Int64相關的加減乘除。
  • shr(n), shl(n): Int64相關的左移、右移操作
  • compare(rhs): Int64的比較操作,有點類似 String.localCompare()
  • toNumber(): 把Int64轉換成一個實數
  • toString([radix = 10]): 按照一定的數值進制把Int64轉成字符串,默認是十進制

UInt64

  • 可以直接參考Int64

NativePointer

  • 可以直接參考Int64

NativeFunction

  • new NativeFunction(address, returnType, argTypes[, abi]): 在address(使用NativePointer的格式)地址上創建一個NativeFunction對象來進行函數調用,returnType 指定函數返回類型,argTypes 指定函數的參數類型,如果不是系統默認類型,也可以選擇性的指定 abi 參數,對於可變類型的函數,在固定參數之后使用 “…” 來表示。
  • 類和結構體

    • 在函數調用的過程中,類和結構體是按值傳遞的,傳遞的方式是使用一個數組來分別指定類和結構體的各個字段,理論上為了和需要的數組對應起來,這個數組是可以支持無限嵌套的,結構體和類構造完成之后,使用NativePointer的形式返回的,因此也可以傳遞給Interceptor.attach() 調用。
    • 需要注意的點是, 傳遞的數組一定要和需要的參數結構體嚴格吻合,比如一個函數的參數是一個3個整形的結構體,那參數傳遞的時候一定要是 [‘int’, ‘int’, ‘int’],對於一個擁有虛函數的類來說,調用的時候,第一個參數一定是虛表指針。
  • Supported Types

    • void
    • pointer
    • int
    • uint
    • long
    • ulong
    • char
    • uchar
    • float
    • double
    • int8
    • uint8
    • int16
    • uint16
    • int32
    • uint32
    • int64
    • uint64
  • Supported ABIs

    • default
    • Windows 32-bit:
      • sysv
      • stdcall
      • thiscall
      • fastcall
      • mscdecl
    • Windows 64-bit:
      • win64
    • UNIX x86:
      • sysv
      • unix64
    • UNIX ARM:
      • sysv
      • vfp

NativeCallback

  • new NativeCallback(func, returnType, argTypes[, abi]): 使用JavaScript函數 func 來創建一個Native函數,其中returnType和argTypes分別指定函數的返回類型和參數類型數組。如果不想使用系統默認的 abi 類型,則可以指定 abi 這個參數。關於argTypes和abi類型,可以查看NativeFunction來了解詳細信息,這個對象的返回類型也是NativePointer類型,因此可以作為 Interceptor.replace 的參數使用。

SystemFunction

  • new SystemFunction(address, returnType, argTypes[, abi]): 功能基本和NativeFunction一致,但是使用這個對象可以獲取到調用線程的last error狀態,返回值是對平台相關的數值的一層封裝,為value對象,比如是對這兩個值的封裝, errno(UNIX) 或者 lastError(Windows)。

Socket

SocketListener

SocketConnection

IOStream

InputStream

OutputStream

UnixInputStream

UnixOutputStream

Win32InputStream

Win32OutputStream

File

SqliteDatabase

SqliteStatement

Interceptor

  • Interceptor.attach(target, callbacks): 在target指定的位置進行函數調用攔截,target是一個NativePointer參數,用來指定你想要攔截的函數的地址,有一點需要注意,在32位ARM機型上,ARM函數地址末位一定是0(2字節對齊),Thumb函數地址末位一定1(單字節對齊),如果使用的函數地址是用Frida API獲取的話, 那么API內部會自動處理這個細節(比如:Module.findExportByName())。其中callbacks參數是一個對象,大致結構如下:
    事實上Frida可以在代碼的任意位置進行攔截,但是這樣一來 callbacks 回調的時候,因為回調位置有可能不在函數的開頭,這樣onEnter這樣的回調參數Frida只能盡量的保證(比如攔截的位置前面的代碼沒有修改過傳入的參數),不能像在函數頭那樣可以確保正確。
    • onEnter: function(args): 被攔截函數調用之前回調,其中原始函數的參數使用args數組(NativePointer對象數組)來表示,可以在這里修改函數的調用參數。
    • onLeave: function(retval): 被攔截函數調用之后回調,其中retval表示原始函數的返回值,retval是從NativePointer繼承來的,是對原始返回值的一個封裝,你可以使用retval.replace(1337)調用來修改返回值的內容。需要注意的一點是,retval對象只在 onLeave函數作用域范圍內有效,因此如果你要保存這個對象以備后續使用的話,一定要使用深拷貝來保存對象,比如:ptr(retval.toString())。
  • 攔截器的attach調用返回一個監聽對象,后續取消攔截的時候,可以作為 Interceptor.detach() 的參數使用。
  • 還有一個比較方便的地方,那就是在回調函數里面,包含了一個隱藏的 this 的線程tls對象,方便在回調函數中存儲變量,比如可以在 onEnter 中保存值,然后在 onLeave 中使用,看一個例子:
  • 另外,this 對象還包含了一些額外的比較有用的屬性:
    Interceptor.detachAll(): 取消之前所有的攔截調用
    • returnAddress: 返回NativePointer類型的 address 對象
    • context: 包含 pc,sp,以及相關寄存器比如 eax, ebx等,可以在回調函數中直接修改
    • errno: (UNIX)當前線程的錯誤值
    • lastError: (Windows) 當前線程的錯誤值
    • threadId: 操作系統線程Id
    • depth: 函數調用層次深度
    • 看個例子:
  • Interceptor.replace(target, replacement): 函數實現代碼替換,這種情況主要是你想要完全替換掉一個原有函數的實現的時候來使用,注意replacement參數使用JavaScript形式的一個NativeCallback來實現,后續如果想要取消這個替換效果,可以使用 Interceptor.revert調用來實現,如果你還想在你自己的替換函數里面繼續調用原始的函數,可以使用以 target 為參數的NativeFunction對象來調用,來看一個例子:
  • Interceptor.revert(target): 還原函數的原始實現邏輯,即取消前面的 Interceptor.replace調用
  • Interceptor.flush(): 確保之前的內存修改操作都執行完畢,並切已經在內存中發生作用,只要少數幾種情況需要這個調用,比如你剛執行了 attach() 或者 replace() 調用,然后接着想要使用NativeFunction對象對函數進行調用,這種情況就需要調用flush。正常情況下,緩存的調用操作會在當前線程即將離開JavaScript運行時環境或者調用send()的時候自動進行flush操作,也包括那些底層會調用 send() 操作的函數,比如 RPC 函數,或者任何的 console API

Stalker

  • Stalker.follow([threadId, options]): 開始監視線程ID為 threadId(如果是本線程,可以省略)的線程事件,舉個例子:
  • Stalker.unfollow([threadId]): 停止監控線程事件,如果是當前線程,則可以省略 threadId 參數
  • Stalker.parse(events[, options]): 按照指定格式介些 GumEvent二進制數據塊,按照options的要求格式化輸出,舉個例子:
  • Stalker.garbageCollect(): 在調用Stalker.unfollow()之后,在一個合適的時候,釋放對應的內存,可以避免多線程競態條件下的內存釋放問題。
  • Stalker.addCallProbe(address, callback): 當address地址處的函數被調用的時候,調用callback對象(對象類型和Interceptor.attach.onEnter一致),返回一個Id,可以給后面的Stalker.removeCallProbe使用
  • Stalker.removeCallProbe(): 移除前面的 addCallProbe 調用效果。
  • Stalker.trustThreshold: 指定一個整型x,表示可以確保一段代碼在執行x次之后,代碼才可以認為是可靠的穩定的, -1表示不信任,0表示持續信任,N表示執行N次之后才是可靠的,穩定的,默認值是1。
  • Stalker.queueCapacity: 指定事件隊列的長度,默認長度是16384
  • Stalker.queueDrainInterval: 事件隊列查詢派發時間間隔,默認是250ms,也就是說1秒鍾事件隊列會輪詢4次

ApiResolver

  • new ApiResolver(type): 創建指定類型type的API查找器,可以根據函數名稱快速定位到函數地址,根據當前進程環境不同,可用的ApiResolver類型也不同,到目前為止,可用的類型有:
    • Module: 枚舉當前進程中已經加載的動態鏈接庫的導入導出函數名稱。
    • objc: 定位已經加載進來的Object-C類方法,在macOS和iOS進程中可用,可以使用 Objc.available 來進行運行時判斷,或者在 try-catch 塊中使用 new ApiResolver(‘objc’) 來嘗試創建。
    • 解析器在創建的時候,會加載最小的數據,后續使用懶加載的方式來持續加載剩余的數據,因此最好是一次相關的批量調用使用同一個resolver對象,然后下次的相關操作,重新創建一個resolver對象,避免使用上個resolver的老數據。
  • enumerateMatches(query, callbacks): 執行函數查找過程,按照參數query來查找,查找結果調用callbacks來回調通知:
    enumerateMatchesSync(query): enumerateMatches()的同步版本,直接返回所有結果的數組形式
    • onMatch: function(match): 每次枚舉到一個函數,調用一次,回調參數match包含name和address兩個屬性。
    • onComplete: function(): 整個枚舉過程完成之后調用。
    • 舉個例子:

DebugSymbol

  • DebugSymbol.fromAddress(address), DebugSymbol.fromName(name): 在指定地址或者指定名稱查找符號信息,返回的符號信息對象包含下面的屬性:
    DebugSymbol.getFunctionByName(name), DebugSymbol.findFunctionsNamed(name), DebugSymbol.findFunctionsMatching(glob): 這三個函數,都是根據符號信息來查找函數,結果返回 NativePointer 對象。
    • address: 當前符號的地址,NativePointer
    • name: 當前符號的名稱,字符串形式
    • moduleName: 符號所在的模塊名稱
    • fileName: 符號所在的文件名
    • lineNumber: 符號所在的文件內的行號
    • 為了方便使用,也可以在這個對象上直接使用 toString() ,輸出信息的時候比較有用,比如和 Thread.backtrace 配合使用,舉個例子來看:

Instruction

  • Instruction.parse(target): 在 target 指定的地址處解析指令,其中target是一個NativePointer。注意,在32位ARM上,ARM函數地址需要是2字節對齊的,Thumb函數地址是1字節對齊的,如果你是使用Frida本身的函數來獲取的target地址,Frida會自動處理掉這個細節,parse函數返回的對象包含如下屬性:
    • address: 當前指令的EIP,NativePointer類型
    • next: 下條指令的地址,可以繼續使用parse函數
    • size: 當前指令大小
    • mnemonic: 指令助記符
    • opStr: 字符串格式顯示操作數
    • operands: 操作數數組,每個操作數對象包含type和value兩個屬性,根據平台不同,有可能還包含一些額外屬性
    • regsRead: 這條指令顯式進行讀取的寄存器數組
    • regsWritten: 這條指令顯式的寫入的寄存器數組
    • groups: 該條指令所屬的指令分組
    • toString(): 把指令格式化成一條人比較容易讀懂的字符串形式
    • 關於operands和groups的細節,請參考CapStone文檔

ObjC

Java

    • Java.available: 布爾型取值,表示當前進程中是否存在完整可用的Java虛擬機環境,Dalvik或者Art,建議在使用Java方法之前,使用這個變量來確保環境正常。
    • Java.enumerateLoadedClasses(callbacks): 枚舉當前進程中已經加載的類,每次枚舉到加載的類回調callbacks:
      Java.enumerateLoadedClassesSync(): 同步枚舉所有已經加載的類
      • onMatch: function(className): 枚舉到一個類,以類名稱進行回調,這個類名稱后續可以作為 Java.use() 的參數來獲取該類的一個引用對象。
      • onComplete: function(): 所有的類枚舉完畢之后調用
    • Java.use(fn): 把當前線程附加到Java VM環境中去,並且執行Java函數fn(如果已經在Java函數的回調中,則不需要再附加到VM),舉個例子:
    • Java.use(className): 對指定的類名動態的獲取這個類的JavaScript引用,后續可以使用$new()來調用類的構造函數進行類對象的創建,后續可以主動調用 $dispose() 來調用類的析構函數來進行對象清理(或者等待Java的垃圾回收,再或者是JavaScript腳本卸載的時候),靜態和非靜態成員函數在JavaScript腳本里面也都是可見的, 你可以替換Java類中的方法,甚至可以在里面拋出異常,比如:
    • Java.scheduleOnMainThread(fn): 在虛擬機主線程上執行函數fn
    • Java.choose(className, callbacks): 在Java的內存堆上掃描指定類名稱的Java對象,每次掃描到一個對象,則回調callbacks:
      Java.cast(handle, klass): 使用對象句柄handle按照klass(Java.use方法返回)的類型創建一個對象的JavaScript引用,這個對象引用包含一個class屬性來獲取當前對象的類,也包含一個$className屬性來獲取類名稱字符串,比如:
      • onMatch: function(instance): 每次掃描到一個實例對象,調用一次,函數返回stop結束掃描的過程
      • onComplete: function(): 當所有的對象都掃描完畢之后進行回調

 

篇三

JavaScript API

WeakRef

  • WeakRef.bind(value, fn): 監控value對象,當被監控的對象即將被垃圾回收或者腳本即將被卸載的時候,調用回調函數fn,bind返回一個唯一ID,后續可以使用這個ID進行 WeakRef.unbind()調用來取消前面的監控。這個API還是很有用處的,比如你想要在JavaScript的某個對象銷毀的時候跟着銷毀一些本地資源,這種情況下,這個機制就比較有用了。
  • WeakRef.unbind(id): 停止上述的對象監控,並且會立即調用一次f n

x86Writer

  • new X86Writer(codeAddress[, {pc: ptr(‘0x1234’)}]): 創建一個x86機器碼生成器,並且在codeAddress指向的內存進行寫入,codeAddress是NativePointer類型,第二個參數是可選參數,用來指定程序的初始EIP。在iOS系統上,使用Memory.patchCode()的時候,指定初始EIP是必須的,因為內存寫入是先寫入到一個臨時的位置,然后再映射到指定位置的內存
  • reset(codeAddress[, { pc: ptr(‘0x1234’) }]): 取消codeAddress位置的上次的代碼寫入
  • dispose(): 立即進行X86相關的內存修改清理
  • flush(): 代碼中標簽引用的解析,操作緩存立即應用到內存中去。在實際的應用中,當生成一段代碼片段的時候,就應該調用一次這個函數。多個相關聯的函數片段在一起使用的時候,也應該調用一次,尤其是要在一起協同運行的幾個函數片段。
  • base: 輸出結果的第一個字節碼的內存位置,NativePointer類型
  • code: 輸出結果的下一個字節碼的內存位置,NativePointer類型
  • pc: 輸出結果的指令指針的內存位置,NativePointer類型
  • offset: 當前的偏移(JavaScript數值)
  • putLabel(id): 在當前位置插入一個標簽,標簽用字符串id表示
  • putCallAddressWithArguments(fund, args): 准備好一個調用C函數的上下文環境,其中args表示被調用函數的參數數組(JavaScript數組),數組里面可以是字符串形式指定的寄存器,可以是一個數值,也可以是一個指向立即數的NativePointer
  • putCallAddressWithAlignedArguments(func, args): 跟上面一個函數差不多,但是參數數組是16字節對齊的
  • putCallRegWithArguments(reg, args): 准備好一個調用C函數的上下文環境,其中args表示被調用函數的參數數組(JavaScript數組),數組里面可以是字符串形式指定的寄存器,可以是一個數值,也可以是一個指向立即數的NativePointer
  • putCallRegWithAlignedArguments(reg, args): 參數數組16字節對齊
  • putCallRegOffsetPtrWithArguments(reg, offset, args): 准備好一個調用C函數的上下文環境,其中args表示被調用函數的參數數組(JavaScript數組),數組里面可以是字符串形式指定的寄存器,可以是一個數值,也可以是一個指向立即數的NativePointer
  • putCallAddress(address): 寫入一個Call指令
  • putCallReg(reg): 寫入一個Call指令
  • putCallRegOffsetPtr(reg, offset): 寫入一個Call指令
  • putCallIndirect(addr): 寫入一個Call指令
  • putCallNearLabel(labelId): 在前面定義的Label處創建一個Call 指令
  • putLeave(): 創建一個 LEAVE 指令
  • putRet(): 創建一個 RET 指令
  • putRetImm(immValue): 創建一個RET指令
  • putJmpShortLabel(labelId): 創建一個JMP指令,跳轉到labelId標志的位置
  • putJmpNearLabel(labelId): 創建一個JMP指令,跳轉到labelId標志的位置
  • putJmpReg(reg): 創建一個JMP指令
  • putJmpRegPtr(reg): 創建一個JMP指令
  • putJmpRegOffsetPtr(reg, offset): 創建一個JMP指令
  • putJmpNearPtr(address): 創建一個JMP指令
  • putJccShort(labelId, target, hint): 創建一個JCC指令
  • putJccNear(labelId, target, hint): 在labelId處創建一個JCC指令
  • putAddRegImm(reg, immValue)
  • putAddRegReg
  • putAddRegNearPtr(dstReg, srcAddress)
  • putSubRegImm(reg, immValue)
  • putSubRegReg(dstReg, srcReg)
  • putSubRegNearPtr(dstReg, srcAddress)
  • putIncReg(reg)
  • putDecReg(reg)
  • putIncRegPtr(target, reg)
  • putDecRegPtr(target, reg)
  • putLockXaddRegPtrReg(dstReg, srcReg)
  • putLockIncImm32Ptr(target)
  • putLockDecImm32Ptr(target)
  • putAddRegReg(dstReg, srcReg)
  • putAddRegU32(reg, immValue)
  • putShlRegU8(reg, immValue)
  • putShrRegU8(reg, immValue)
  • putXorRegReg(dstReg, srcReg)
  • putMovRegReg(dstReg, srcReg)
  • putMovRegU32(dstReg, immValue)
  • putMovRegU64(dstReg, immValue)
  • putMovRegAddress(dstReg, immValue)
  • putMovRegPtrU32(dstReg, immValue)
  • putMovRegOffsetPtrU32(dstReg, dstOffset, immValue)
  • putMovRegPtrReg(dstReg, srcReg)
  • putMovRegOffsetPtrReg(dstReg, dstOffset, srcReg)
  • putMovRegRegPtr(dstReg, srcReg)
  • putMovRegRegOffsetPtr(dstReg, srcReg, srcOffset)
  • putMovRegBaseIndexScaleOffsetPtr(dstReg, baseReg, indexReg, scale, offset)
  • putMovRegNearPtr(dstReg, srcAddress)
  • putMovNearPtrReg(dstAddress, srcReg)
  • putMovFsU32PtrReg(fsOffset, srcReg)
  • putMovRegFsU32Ptr(dstReg, fsOffset)
  • putMovGsU32PtrReg(fsOffset, srcReg)
  • putMovqXmm0EspOffsetPtr(offset)
  • putMovqEaxOffsetPtrXmm0(offset)
  • putMovdquXmm0EspOffsetPtr(offset)
  • putMovdquEaxOffsetPtr(offset)
  • putLeaRegRegOffset(dstReg, srcReg, srcOffset)
  • putXchgRegRegPtr(leftReg, rightReg)
  • putPushU32(immValue)
  • putPushNearPtr(address)
  • putPushReg(reg)
  • putPopReg(reg)
  • putPushImmPtr(immPtr)
  • putPushax()
  • putPopax()
  • putPushfx()
  • putPopfx()
  • putTestRegReg(regA, regB)
  • putTestRegU32(reg, immValue)
  • putCmpRegI32(reg, immValue)
  • putCmpRegOffsetPtrReg(regA, offset, regB)
  • putCmpImmPtrImmU32(immPtr, immValue)
  • putCmpRegReg(regA, regB)
  • putClc()
  • putStc()
  • putCld()
  • putStd()
  • putCpuid()
  • putLfence()
  • putRdtsc()
  • putPause()
  • putNop()
  • putBreakpoint()
  • putPadding(n)
  • putNopPadding(n)
  • putU8(value)
  • putS8(value)
  • putBytes(data) 從ArrayBuffer中拷貝原始數據

X86Relocator

  • new X86Relocator(inputCode, output): 創建一個代碼重定位器,用以進行代碼從一個位置拷貝到另一個位置的時候進行代碼重定位處理,源地址是 inputCode的NativePointer,output表示結果地址,可以用X86Writer對象來指向目的內存地址
  • reset(inputCode, output): 回收上述的X86Relocator對象
  • dispose(): 內存清理
  • input: 最后一次讀取的指令, 一開始是null,每次調用readOne()會自動改變這個屬性
  • eob: 表示當前是否抵達了塊結尾,比如是否遇到了下列任何一個指令:CALL, JMP, BL, RET
  • eoi: 表示input代表的屬性是否結束,比如可能當前遇到了下列的指令:JMP, B, RET,這些指令之后可能沒有有效的指令了
  • readOne(): 把一條指令讀入relocator的內部緩存,返回目前已經讀入緩存的總字節數,可以持續調用readOne函數來緩存指令,或者立即調用writeOne()或者skipOne(),也可以一直緩存到指定的點,然后一次性調用writeAll()。如果已經到了eoi,則函數返回0, 此時eoi屬性也是true
  • peekNextWriteInsn(): peek一條指令出來,以備寫入或者略過
  • peekNextWriteSource(): 在指定地址peek一條指令出來,以備寫入或者略過
  • skipOne(): 忽略下一條即將寫入的指令
  • skipOneNoLabel(): 忽略下一條即將寫入的指令,如果遇到內部使用的Label則不忽略,這個函數是對skipOne的優化,可以讓重定位范圍覆蓋的更全面
  • writeOne(): 寫入下條緩存指令
  • writeOneNoLabel()
  • writeAll(): 寫入所有緩存的指令

x86枚舉類型

  • 寄存器:xar, xcx, xdx, xbx, tsp, xbp, xsi, xdi, sax, ecx, edx, ebx, esp, ebx, esi, edi, rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15, r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, xip, eip, rip
  • 跳轉指令:jo, jno, jb, jae, je, jne, jbe, ja, js, jns, jp, jnp, jl, jge, jle, jg, jcxz, jecxz, jrcxz
  • 分支提示:no-hint, likely, unlikely
  • 指針類型:byte, sword, qword

ArmWriter(參考X86Writer)

  • new ArmWriter(codeAddress[, {pc: ptr(‘0x1234’)}])
  • reset(codeAddress[, {pc: ptr(‘0x1234’)}])
  • dispose()
  • flush()
  • base
  • code
  • pc
  • offset
  • skip(nBytes)
  • putBImm(target)
  • putLdrRegAddress(reg, address)
  • putLdrRegU32(reg, val)
  • putAddRegRegImm(dstReg, srcReg, immVal)
  • putLdrRegRegImm(dstReg, srcReg, immVal)
  • putNop()
  • putBreakpoint()
  • putInstruction(insn)
  • putBytes(data)

ArmRelocator(參考X86Relocator)

ThumbRelocator(參考X86Relocator)

Arm enum types

  • 寄存器:r0~r15, sp, lr, sb, sl, fp, ip, pc
  • 條件碼:eq, ne, hs, lo, mi, pl, vs, vc, hi, ls, ge, lt, gt, le, al

Arm64Writer(參考X86Writer)

Arm64Relocator(參考X86Relocator)

AArch64 enum types

  • 寄存器:x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 w0 w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12 w13 w14 w15 w16 w17 w18 w19 w20 w21 w22 w23 w24 w25 w26 w27 w28 w29 w30 sp lr fp wsp wzr xzr nzcv ip0 ip1 s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 q16 q17 q18 q19 q20 q21 q22 q23 q24 q25 q26 q27 q28 q29 q30 q31
  • 條件碼:eq ne hs lo mi pl vs vc hi ls ge lt gt le al nv
  • 索引模式:post-adjust signed-offset pre-adjust

MipsWriter(參考X86Writer)

MipsRelocator(參考X86Relocator)

MIPS enum types

  • 寄存器:v0 v1 a0 a1 a2 a3 t0 t1 t2 t3 t4 t5 t6 t7 s0 s1 s2 s3 s4 s5 s6 s7 t8 t9 k0 k1 gp sp fp s8 ra hi lo zero at 0 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM