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

freakish發表於2017-10-22

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) 來監控任何來自目標程式的訊息,訊息監控可以來自 scriptsession 兩個方面,比如,如果你想要監控目標程式的退出,可以使用下面這個語句 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:
    • 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.enumerateThreadSync(): enumerateThreads()的同步版本,返回執行緒物件陣列
  • Process.findModuleByAddress(address), Process.getModuleByAddress(address), Process.findModuleByName(name), Process.getModuleByName(name): 根據地址或者名稱來查詢模組,如果找不到這樣的模組,find開頭的函式返回 null,get開頭的函式會丟擲異常。
  • Process.enumerateModules(callbacks): 列舉已經載入的模組,列舉到模組之後呼叫回撥物件:
    • onMatch: function(module): 列舉到一個模組的時候呼叫,module物件包含如下欄位:
      1. name, 模組名
      2. base, 基地址
      3. size,模組大小
      4. path,模組路徑
      5. 函式可以返回 stop 來停止列舉 。
    • onComplete: function(): 當所有的模組列舉完成的時候呼叫。
  • Process.enumerateModulesSync(): enumerateModules() 函式的同步版本,返回模組物件陣列
  • Process.findRangeByAddress(address), Process.getRangeByAddress(address): 返回一個記憶體塊物件, 如果在這個address找不到記憶體塊物件,那麼 findRangeByAddress() 返回 nullgetRangeByAddress 則丟擲異常。
  • Process.numerateRanges(protection | specifier, callbacks): 列舉指定 protection 型別的記憶體塊,以指定形式的字串給出:rwx,而 rw- 表示最少是可讀可寫,也可以用分類符,裡面包含 protection 這個Key,取值就是前面提到的rwx,還有一個 coalesce 這個Key,表示是否要把位置相鄰並且屬性相同的記憶體塊合併給出結果,列舉過程中回撥 callbacks 物件:
    • onMatch: function(range): 每次列舉到一個記憶體塊都回撥回來,其中Range物件包含如下屬性:
      1. base:基地址
      2. size:記憶體塊大小
      3. protection:保護屬性
      4. file:(如果有的話)記憶體對映檔案:
        4.1 path,檔案路徑
        4.2 offset,檔案內偏移
      5. 如果要停止列舉過程,直接返回 stop 即可
    • onComplete: function(): 所有記憶體塊列舉完成之後會回撥
  • Process.enumerateRangesSync(protection | specifier): enumerateRanges()函式的同步版本,返回記憶體塊陣列
  • 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,包含 pcsp 的NativePointer,分別代表指令指標和堆疊指標
    • nativeContext,基於作業系統定義的異常上下文資訊的NativePointer,在 context 裡面的資訊不夠用的時候,可以考慮用這個指標,但是一般不建議使用(譯者注:估計是考慮到可移植性或者穩定性
    • 捕獲到異常之後,怎麼使用就看你自己了,比如可以把異常資訊寫到日誌裡面,然後傳送個資訊給主控端,然後同步等待主控端的響應之後處理,或者直接修改異常資訊裡面包含的暫存器的值,嘗試恢復掉異常,繼續執行。如果你處理了異常資訊,那麼這個異常回撥裡面你要返回 true,Frida會把異常交給程式異常處理函式處理,如果到最後都沒人去處理這個異常,就直接結束目標程式。

Module

  • Module.emuerateImports(name, callbacks): 列舉模組 name 的匯入表,列舉到一個匯入項的時候回撥callbacks, callbacks包含下面2個回撥:
    • onMatch: function(imp): 列舉到一個匯入項到時候會被呼叫,imp包含如下的欄位:
      1. type,匯入項的型別, 取值範圍是 function或者variable
      2. name,匯入項的名稱
      3. module,模組名稱
      4. address,匯入項的絕對地址
      5. 以上所有的屬性欄位,只有 name 欄位是一定會有,剩餘的其他欄位不能保證都有,底層會盡量保證每個欄位都能給出資料,但是不能保證一定能拿到資料,onMatch函式可以返回字串 stop 表示要停止列舉。
    • onComplete: function(): 當所有的匯入表項都列舉完成的時候會回撥
  • Module.eumerateImportsSync(name): enumerateImports()的同步版本
  • Module.emuerateExports(name, callbacks): 列舉指定模組 name 的匯出表項,結果用 callbacks 進行回撥:
    • onMatch: function(exp): 其中 exp 代表列舉到的一個匯出項,包含如下幾個欄位:
      1. type,匯出項型別,取值範圍是 function或者variable
      2. name,匯出項名稱
      3. address,匯出項的絕對地址,NativePointer
      4. 函式返回 stop 的時候表示停止列舉過程
    • onComplete: function(): 列舉完成回撥
  • Module.enumerateExportsSync(): Module.enumerateExports()的同步版本
  • 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,各個引數詳細如下:
    • pattern 比如使用13 37 ?? ff來匹配0x13開頭,然後跟著0x37,然後是任意位元組內容,接著是0xff這樣的記憶體塊
    • callbacks 是掃描函式回撥物件:
      1. onMatch: function(address, size): 掃描到一個記憶體塊,起始地址是address,大小size的記憶體塊,返回 stop 表示停止掃描
      2. onError: function(reason): 掃描記憶體的時候出現記憶體訪問異常的時候回撥
      3. onComplete: function(): 記憶體掃描完畢的時候呼叫
  • Memory.scanSync(address, size, pattern): 記憶體掃描 scan() 的同步版本
  • 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 指標陣列的形式返回。
    1. 如果你是在 Interceptor.onEnter或者Interceptor.onLeave 中呼叫這個函式的話,那就必須要把 this.context 作為引數傳入,這樣就能拿到更佳精準的堆疊呼叫資訊,如果省略這個引數不傳,那就意味著從當前堆疊的位置開始抓取,這樣的抓取效果可能不會很好,因為有不少V8引擎的棧幀的干擾。
    2. 第二個可選引數 backtracer,表示使用哪種型別的堆疊抓取演算法,目前的取值範圍是 Backtracer.FUZZYBacktracer.ACCURATE,目前後者是預設模式。精確抓取模式下,如果如果程式是偵錯程式友好(比如是標準編譯器編譯的結果,沒有什麼反除錯技巧)或者有符號表的支援,抓取效果是最好的,而模糊抓取模式下,抓取器會在堆疊上嘗試抓取,並且會猜測裡面包含的返回地址,也就是說中間可能包含一些錯誤的資訊,但是這種模式基本能在任何二進位制程式裡面工作:
  • Thread.sleep(delay): 執行緒暫停 delay 秒執行

下篇繼續

  • 一篇文章寫太長發現瀏覽器容易崩潰,所以下篇繼續

相關文章