FRIDA-API使用篇:rpc、Process、Module、Memory使用方法及示例

摘抄自安全客

前言

大家好,窩又來寫文章了,咱們現(xiàn)在在這篇文章中,我們來對其官方的一些非常常用的API進行學(xué)習(xí)。所謂工欲善其事,必先利其器。想要好好學(xué)習(xí)FRIDA我們就必須對FRIDA API深入的學(xué)習(xí)以對其有更深的了解和使用,通常大部分核心原理也在官方API中寫著,我們學(xué)會來使用一些案例來結(jié)合API的使用。

注意,運行以下任何代碼時都需要提前啟動手機中的frida-server文件。

系列文章目錄搬新“家”了,地址:https://github.com/r0ysue/AndroidSecurityStudy ,接下來窩會努力寫更多喔 ~

1.1 FRIDA輸出打印

1.1.1 console輸出

不論是什么語言都好,第一個要學(xué)習(xí)總是如何輸出和打印,那我們就來學(xué)習(xí)在FRIDA打印值。在官方API有兩種打印的方式,分別是console、send,我們先來學(xué)習(xí)非常的簡單的console,這里我創(chuàng)建一個js文件,代碼示例如下。

function hello_printf() {
    Java.perform(function () {
        console.log("");
        console.log("hello-log");
        console.warn("hello-warn");
        console.error("hello-error");
    });
}
setImmediate(hello_printf,0);

當(dāng)文件創(chuàng)建好之后,我們需要運行在手機中安裝的frida-server文件,在上一章我們學(xué)過了如何安裝在android手機安裝frida-server,現(xiàn)在來使用它,我們在ubuntu中開啟一個終端,運行以下代碼,啟動我們安裝好的frida-server文件。

roysue@ubuntu:~$ adb shell
sailfish:/ $ su
sailfish:/ $ ./data/local/tmp/frida-server

然后執(zhí)行以下代碼,對目標(biāo)應(yīng)用app的進程com.roysue.roysueapplication使用-l命令注入Chap03.js中的代碼1-1以及執(zhí)行腳本之后的效果圖1-1
frida -U com.roysue.roysueapplication -l Chap03.js

代碼1-1 代碼示例

圖1-1 終端執(zhí)行

可以到終點已經(jīng)成功注入了腳本并且打印了hello,但是顏色不同,這是log的級別的原因,在FRIDAconsole中有三個級別分別是log、warn、error

級別 含義
log 正常
warn 警告
error 錯誤

1.1.2 console之hexdump

error級別最為嚴(yán)重其次warn,但是一般在使用中我們只會使用log來輸出想看的值;然后我們繼續(xù)學(xué)習(xí)console的好兄弟,hexdump,其含義:打印內(nèi)存中的地址,target參數(shù)可以是ArrayBuffer或者NativePointer,而options參數(shù)則是自定義輸出格式可以填這幾個參數(shù)offsetlengt、headeransi。

hexdump代碼示例以及執(zhí)行效果如下。

var libc = Module.findBaseAddress('libc.so');
console.log(hexdump(libc, {
  offset: 0,
  length: 64,
  header: true,
  ansi: true
}));
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
00000000  7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00  .ELF............
00000010  03 00 28 00 01 00 00 00 00 00 00 00 34 00 00 00  ..(.........4...
00000020  34 a8 04 00 00 00 00 05 34 00 20 00 08 00 28 00  4.......4. ...(.
00000030  1e 00 1d 00 06 00 00 00 34 00 00 00 34 00 00 00  ........4...4...

1.1.3 send

send是在python層定義的on_message回調(diào)函數(shù),jscode內(nèi)所有的信息都被監(jiān)控script.on('message', on_message),當(dāng)輸出信息的時候on_message函數(shù)會拿到其數(shù)據(jù)再通過format轉(zhuǎn)換, 其最重要的功能也是最核心的是能夠直接將數(shù)據(jù)以json格式輸出,當(dāng)然數(shù)據(jù)是二進制的時候也依然是可以使用send,十分方便,我們來看代碼1-2示例以及執(zhí)行效果。

# -*- coding: utf-8 -*-
import frida
import sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

jscode = """
    Java.perform(function () 
    {
        var jni_env = Java.vm.getEnv();
        console.log(jni_env);
        send(jni_env);
    });
 """

process = frida.get_usb_device().attach('com.roysue.roysueapplication')
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()

運行腳本效果如下:

roysue@ubuntu:~/Desktop/Chap09$ python Chap03.py 
[object Object]
[*] {'handle': '0xdf4f8000', 'vm': {}}

可以看出這里兩種方式輸出的不同的效果,console直接輸出了[object Object],無法輸出其正常的內(nèi)容,因為jni_env實際上是一個對象,但是使用send的時候會自動將對象轉(zhuǎn)json格式輸出。通過對比,我們就知道send的好處啦~

1.2 FRIDA變量類型

學(xué)完輸出之后我們來學(xué)習(xí)如何聲明變量類型。

索引 API 含義
1 new Int64(v) 定義一個有符號Int64類型的變量值為v,參數(shù)v可以是字符串或者以0x開頭的的十六進制值
2 new UInt64(v) 定義一個無符號Int64類型的變量值為v,參數(shù)v可以是字符串或者以0x開頭的的十六進制值
3 new NativePointer(s) 定義一個指針,指針地址為s
4 ptr(“0”) 同上

代碼示例以及效果

Java.perform(function () {
    console.log("");
    console.log("new Int64(1):"+new Int64(1));
    console.log("new UInt64(1):"+new UInt64(1));
    console.log("new NativePointer(0xEC644071):"+new NativePointer(0xEC644071));
    console.log("new ptr('0xEC644071'):"+new ptr(0xEC644071));
});
    輸出效果如下:
    new Int64(1):1
    new UInt64(1):1
    new NativePointer(0xEC644071):0xec644071
    new ptr('0xEC644071'):0xec644071

frida也為Int64(v)提供了一些相關(guān)的API

索引 API 含義
1 add(rhs)、sub(rhs)、and(rhs)、or(rhs)、xor(rhs) 加、減、邏輯運算
2 shr(N)、shl(n) 向右/向左移位n位生成新的Int64
3 Compare(Rhs) 返回整數(shù)比較結(jié)果
4 toNumber() 轉(zhuǎn)換為數(shù)字
5 toString([radix=10]) 轉(zhuǎn)換為可選基數(shù)的字符串(默認(rèn)為10)

我也寫了一些使用案例,代碼如下。

function hello_type() {
    Java.perform(function () {
        console.log("");
        //8888 + 1 = 8889
        console.log("8888 + 1:"+new Int64("8888").add(1));
        //8888 - 1 = 8887
        console.log("8888 - 1:"+new Int64("8888").sub(1));
        //8888 << 1 = 4444
        console.log("8888 << 1:"+new Int64("8888").shr(1));
        //8888 == 22 = 1 1是false
        console.log("8888 == 22:"+new Int64("8888").compare(22));
        //轉(zhuǎn)string
        console.log("8888 toString:"+new Int64("8888").toString());
    });
}

代碼執(zhí)行效果如圖1-2。


圖1-2 Int64 API

1.3 RPC遠(yuǎn)程調(diào)用

可以替換或插入的空對象,以向應(yīng)用程序公開RPC樣式的API。該鍵指定方法名稱,該值是導(dǎo)出的函數(shù)。此函數(shù)可以返回一個純值以立即返回給調(diào)用方,或者承諾異步返回。也就是說可以通過rpc的導(dǎo)出的功能使用在python層,使python層與js交互,官方示例代碼有Node.js版本與python版本,我們在這里使用python版本,代碼如下。

1.3.1 遠(yuǎn)程調(diào)用代碼示例

import frida

def on_message(message, data):
    if message['type'] == 'send':
        print(message['payload'])
    elif message['type'] == 'error':
        print(message['stack'])

session = frida.get_usb_device().attach('com.roysue.roysueapplication')

source = """
    rpc.exports = {
    add: function (a, b) {
        return a + b;
    },
    sub: function (a, b) {
        return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(a - b);
        }, 100);
        });
    }
    };
"""

script = session.create_script(source)
script.on('message', on_message)
script.load()
print(script.exports.add(2, 3))
print(script.exports.sub(5, 3))
session.detach()

1.3.2 遠(yuǎn)程調(diào)用代碼示例詳解

官方源碼示例是附加在目標(biāo)進程為iTunes,再通過將rpc./agent.js文件讀取到source,進行使用。我這里修改了附加的目標(biāo)的進程以及直接將rpc的代碼定義在source中。我們來看看這段是咋運行的,仍然先對目標(biāo)進程附加,然后在寫js中代碼,也是source變量,通過rpc.exports關(guān)鍵字定義需要導(dǎo)出的兩個函數(shù),上面定義了add函數(shù)和sub函數(shù),兩個的函數(shù)寫作方式不一樣,大家以后寫按照add方法寫就好了,sub稍微有點復(fù)雜。聲明完函數(shù)之后創(chuàng)建了一個腳本并且注入進程,加載了腳本之后可以到print(script.exports.add(2, 3))以及print(script.exports.sub(5, 3)),在python層直接調(diào)用。add的返回的結(jié)果為5sub則是2,下見下圖1-3

圖1-3 執(zhí)行python腳本

1.4 Process對象

我們現(xiàn)在來介紹以及使用一些Process對象中比較常用的api~

1.4.1 Process.id

Process.id:返回附加目標(biāo)進程的PID

1.4.2 Process.isDebuggerAttached()

Process.isDebuggerAttached():檢測當(dāng)前是否對目標(biāo)程序已經(jīng)附加

1.4.3 Process.enumerateModules()

枚舉當(dāng)前加載的模塊,返回模塊對象的數(shù)組。
Process.enumerateModules()會枚舉當(dāng)前所有已加載的so模塊,并且返回了數(shù)組Module對象,Module對象下一節(jié)我們來詳細(xì)說,在這里我們暫時只使用Module對象的name屬性。

function frida_Process() {
    Java.perform(function () {
        var process_Obj_Module_Arr = Process.enumerateModules();
        for(var i = 0; i < process_Obj_Module_Arr.length; i++) {
            console.log("",process_Obj_Module_Arr[i].name);
        }
    });
}
setImmediate(frida_Process,0);

我來們開看看這段js代碼寫了啥:在js中能夠直接使用Process對象的所有api,調(diào)用了Process.enumerateModules()方法之后會返回一個數(shù)組,數(shù)組中存儲N個叫Module的對象,既然已經(jīng)知道返回了的是一個數(shù)組,很簡單我們就來for循環(huán)它便是,這里我使用下標(biāo)的方式調(diào)用了Module對象的name屬性,nameso模塊的名稱。見下圖1-4。

1.4.4 Process.enumerateThreads()

Process.enumerateThreads():枚舉當(dāng)前所有的線程,返回包含以下屬性的對象數(shù)組:

索引 屬性 含義
1 id 線程id
2 state 當(dāng)前運行狀態(tài)有running, stopped, waiting, uninterruptible or halted
3 context 帶有鍵pc和sp的對象,它們是分別為ia32/x64/arm指定EIP/RIP/PC和ESP/RSP/SP的NativePointer對象。也可以使用其他處理器特定的密鑰,例如eax、rax、r0、x0等。

使用代碼示例如下:

function frida_Process() {
    Java.perform(function () {
       var enumerateThreads =  Process.enumerateThreads();
       for(var i = 0; i < enumerateThreads.length; i++) {
        console.log("");
        console.log("id:",enumerateThreads[i].id);
        console.log("state:",enumerateThreads[i].state);
        console.log("context:",JSON.stringify(enumerateThreads[i].context));
        }
    });
}
setImmediate(frida_Process,0);

獲取當(dāng)前是所有線程之后返回了一個數(shù)組,然后循環(huán)輸出它的值,如下圖1-4。

1.4.5 Process.getCurrentThreadId()

Process.getCurrentThreadId():獲取此線程的操作系統(tǒng)特定 ID 作為數(shù)字

1.5 Module對象

3.4章節(jié)中Process.EnumererateModules()方法返回了就是一個Module對象,咱們這里來詳細(xì)說說Module對象,先來瞧瞧它都有哪些屬性。

1.5.1 Module對象的屬性

索引 屬性 含義
1 name 模塊名稱
2 base 模塊地址,其變量類型為NativePointer
3 size 大小
4 path 完整文件系統(tǒng)路徑

除了屬性我們再來看看它有什么方法。

1.5.2 Module對象的API

索引 API 含義
1 Module.load() 加載指定so文件,返回一個Module對象
2 enumerateImports() 枚舉所有Import庫函數(shù),返回Module數(shù)組對象
3 enumerateExports() 枚舉所有Export庫函數(shù),返回Module數(shù)組對象
4 enumerateSymbols() 枚舉所有Symbol庫函數(shù),返回Module數(shù)組對象
5 Module.findExportByName(exportName)、Module.getExportByName(exportName) 尋找指定so中export庫中的函數(shù)地址
6 Module.findBaseAddress(name)、Module.getBaseAddress(name) 返回so的基地址

1.5.3 Module.load()

frida-12-5版本中更新了該API,主要用于加載指定so文件,返回一個Module對象。

使用代碼示例如下:

function frida_Module() {
    Java.perform(function () {
         //參數(shù)為so的名稱 返回一個Module對象
         const hooks = Module.load('libhello.so');
         //輸出
         console.log("模塊名稱:",hooks.name);
         console.log("模塊地址:",hooks.base);
         console.log("大小:",hooks.size);
         console.log("文件系統(tǒng)路徑",hooks.path);
    });
}
setImmediate(frida_Module,0);

輸出如下:
模塊名稱: libhello.so
模塊地址: 0xdf2d3000
大小: 24576
文件系統(tǒng)路徑 /data/app/com.roysue.roysueapplication-7adQZoYIyp5t3G5Ef5wevQ==/lib/arm/libhello.so

1.5.4 Process.EnumererateModules()

咱們這一小章節(jié)就來使用Module對象,把上章的Process.EnumererateModules()對象輸出給它補全了,代碼如下。

function frida_Module() {
    Java.perform(function () {

        var process_Obj_Module_Arr = Process.enumerateModules();
        for(var i = 0; i < process_Obj_Module_Arr.length; i++) {
            if(process_Obj_Module_Arr[i].path.indexOf("hello")!=-1)
            {
                console.log("模塊名稱:",process_Obj_Module_Arr[i].name);
                console.log("模塊地址:",process_Obj_Module_Arr[i].base);
                console.log("大小:",process_Obj_Module_Arr[i].size);
                console.log("文件系統(tǒng)路徑",process_Obj_Module_Arr[i].path);
            }
         }
    });
}
setImmediate(frida_Module,0);

輸出如下:
模塊名稱: libhello.so
模塊地址: 0xdf2d3000
大小: 24576
文件系統(tǒng)路徑 /data/app/com.roysue.roysueapplication-7adQZoYIyp5t3G5Ef5wevQ==/lib/arm/libhello.so

這邊如果去除判斷的話會打印所有加載的so的信息,這里我們就知道了哪些方法返回了Module對象了,然后我們再繼續(xù)深入學(xué)習(xí)Module對象自帶的API。

1.5.5 enumerateImports()

該API會枚舉模塊中所有中的所有Import函數(shù),示例代碼如下。

function frida_Module() {
    Java.perform(function () {
        const hooks = Module.load('libhello.so');
        var Imports = hooks.enumerateImports();
        for(var i = 0; i < Imports.length; i++) {
            //函數(shù)類型
            console.log("type:",Imports[i].type);
            //函數(shù)名稱
            console.log("name:",Imports[i].name);
            //屬于的模塊
            console.log("module:",Imports[i].module);
            //函數(shù)地址
            console.log("address:",Imports[i].address);
         }
    });
}
setImmediate(frida_Module,0);

輸出如下:
[Google Pixel::com.roysue.roysueapplication]-> type: function
name: __cxa_atexit
module: /system/lib/libc.so
address: 0xf58f4521
type: function
name: __cxa_finalize
module: /system/lib/libc.so
address: 0xf58f462d                                                                                                                                           
type: function
name: __stack_chk_fail
module: /system/lib/libc.so
address: 0xf58e2681
...

1.5.6 enumerateExports()

該API會枚舉模塊中所有中的所有Export函數(shù),示例代碼如下。

function frida_Module() {
    Java.perform(function () {
        const hooks = Module.load('libhello.so');
        var Exports = hooks.enumerateExports();
        for(var i = 0; i < Exports.length; i++) {
            //函數(shù)類型
            console.log("type:",Exports[i].type);
            //函數(shù)名稱
            console.log("name:",Exports[i].name);
            //函數(shù)地址
            console.log("address:",Exports[i].address);
         }
    });
}
setImmediate(frida_Module,0);

輸出如下:
[Google Pixel::com.roysue.roysueapplication]-> type: function
name: Java_com_roysue_roysueapplication_hellojni_getSum
address: 0xdf2d411b
type: function
name: unw_save_vfp_as_X
address: 0xdf2d4c43
type: function
address: 0xdf2d4209
type: function
...

1.5.7 enumerateSymbols()

代碼示例如下。

function frida_Module() {
    Java.perform(function () {
        const hooks = Module.load('libc.so');
        var Symbol = hooks.enumerateSymbols();
        for(var i = 0; i < Symbol.length; i++) {
            console.log("isGlobal:",Symbol[i].isGlobal);
            console.log("type:",Symbol[i].type);
            console.log("section:",JSON.stringify(Symbol[i].section));
            console.log("name:",Symbol[i].name);
            console.log("address:",Symbol[i].address);
         }
    });
}
setImmediate(frida_Module,0);

輸出如下:
isGlobal: true
type: function
section: {"id":"13.text","protection":"r-x"}
name: _Unwind_GetRegionStart
address: 0xf591c798
isGlobal: true
type: function
section: {"id":"13.text","protection":"r-x"}
name: _Unwind_GetTextRelBase
address: 0xf591c7cc
...

1.5.8 Module.findExportByName(exportName), Module.getExportByName(exportName)

返回so文件中Export函數(shù)庫中函數(shù)名稱為exportName函數(shù)的絕對地址。

代碼示例如下。

function frida_Module() {
    Java.perform(function () {
        Module.getExportByName('libhello.so', 'c_getStr')
        console.log("Java_com_roysue_roysueapplication_hellojni_getStr address:",Module.findExportByName('libhello.so', 'Java_com_roysue_roysueapplication_hellojni_getStr'));
        console.log("Java_com_roysue_roysueapplication_hellojni_getStr address:",Module.getExportByName('libhello.so', 'Java_com_roysue_roysueapplication_hellojni_getStr'));
    });
}
setImmediate(frida_Module,0);

輸出如下:
Java_com_roysue_roysueapplication_hellojni_getStr address: 0xdf2d413d
Java_com_roysue_roysueapplication_hellojni_getStr address: 0xdf2d413d

1.5.9 Module.findBaseAddress(name)、Module.getBaseAddress(name)

返回name模塊的基地址。

代碼示例如下。

function frida_Module() {
    Java.perform(function () {
        var name = "libhello.so";
        console.log("so address:",Module.findBaseAddress(name));
        console.log("so address:",Module.getBaseAddress(name));
    });
}
setImmediate(frida_Module,0);

輸出如下:
so address: 0xdf2d3000
so address: 0xdf2d3000

1.6 Memory對象

Memory的一些API通常是對內(nèi)存處理,譬如Memory.copy()復(fù)制內(nèi)存,又如writeByteArray寫入字節(jié)到指定內(nèi)存中,那我們這章中就是學(xué)習(xí)使用Memory API向內(nèi)存中寫入數(shù)據(jù)、讀取數(shù)據(jù)。

1.6.1 Memory.scan搜索內(nèi)存數(shù)據(jù)

其主要功能是搜索內(nèi)存中以address地址開始,搜索長度為size,需要搜是條件是pattern,callbacks搜索之后的回調(diào)函數(shù);此函數(shù)相當(dāng)于搜索內(nèi)存的功能。

我們來直接看例子,然后結(jié)合例子講解,如下圖1-5

如果我想搜索在內(nèi)存中112A地址的起始數(shù)據(jù)要怎么做,代碼示例如下。

function frida_Memory() {
    Java.perform(function () {
        //先獲取so的module對象
        var module = Process.findModuleByName("libhello.so"); 
        //??是通配符
        var pattern = "03 49 ?? 50 20 44";
        //基址
        console.log("base:"+module.base)
        //從so的基址開始搜索,搜索大小為so文件的大小,搜指定條件03 49 ?? 50 20 44的數(shù)據(jù)
        var res = Memory.scan(module.base, module.size, pattern, {
            onMatch: function(address, size){
                //搜索成功
                console.log('搜索到 ' +pattern +" 地址是:"+ address.toString());  
            }, 
            onError: function(reason){
                //搜索失敗
                console.log('搜索失敗');
            },
            onComplete: function()
            {
                //搜索完畢
                console.log("搜索完畢")
            }
          });
    });
}
setImmediate(frida_Memory,0);

先來看看回調(diào)函數(shù)的含義,onMatch:function(address,size):使用包含作為NativePointer的實例地址的address和指定大小為數(shù)字的size調(diào)用,此函數(shù)可能會返回字符串STOP以提前取消內(nèi)存掃描。onError:Function(Reason):當(dāng)掃描時出現(xiàn)內(nèi)存訪問錯誤時使用原因調(diào)用。onComplete:function():當(dāng)內(nèi)存范圍已完全掃描時調(diào)用。

我們來來說上面這段代碼做了什么事情:搜索libhello.so文件在內(nèi)存中的數(shù)據(jù),搜索以pattern條件的在內(nèi)存中能匹配的數(shù)據(jù)。搜索到之后根據(jù)回調(diào)函數(shù)返回數(shù)據(jù)。

我們來看看執(zhí)行之后的效果圖1-6。

我們要如何驗證搜索到底是不是圖1-5112A地址,其實很簡單。so的基址是0xdf2d3000,而搜到的地址是0xdf2d412a,我們只要df2d412a-df2d3000=112A。就是說我們已經(jīng)搜索到了!

1.6.2 搜索內(nèi)存數(shù)據(jù)Memory.scanSync

功能與Memory.scan一樣,只不過它是返回多個匹配到條件的數(shù)據(jù)。
代碼示例如下。

function frida_Memory() {
    Java.perform(function () {
        var module = Process.findModuleByName("libhello.so"); 
        var pattern = "03 49 ?? 50 20 44";
        var scanSync = Memory.scanSync(module.base, module.size, pattern);
        console.log("scanSync:"+JSON.stringify(scanSync));
    });
}
setImmediate(frida_Memory,0);

輸出如下,可以看到地址搜索出來是一樣的
scanSync:[{"address":"0xdf2d412a","size":6}]

1.6.3 內(nèi)存分配Memory.alloc

在目標(biāo)進程中的堆上申請size大小的內(nèi)存,并且會按照Process.pageSize對齊,返回一個NativePointer,并且申請的內(nèi)存如果在JavaScript里面沒有對這個內(nèi)存的使用的時候會自動釋放的。也就是說,如果你不想要這個內(nèi)存被釋放,你需要自己保存一份對這個內(nèi)存塊的引用。

使用案例如下

function frida_Memory() {
    Java.perform(function () {
        const r = Memory.alloc(10);
        console.log(hexdump(r, {
            offset: 0,
            length: 10,
            header: true,
            ansi: false
        }));
    });
}
setImmediate(frida_Memory,0);

以上代碼在目標(biāo)進程中申請了10字節(jié)的空間~我們來看執(zhí)行腳本的效果圖1-7。

可以看到在0xdfe4cd40處申請了10個字節(jié)內(nèi)存空間~

也可以使用:
Memory.allocUtf8String(str) 分配utf字符串
Memory.allocUtf16String 分配utf16字符串
Memory.allocAnsiString 分配ansi字符串

1.6.4 內(nèi)存復(fù)制Memory.copy

如同c api memcp一樣調(diào)用,使用案例如下。

function frida_Memory() {
    Java.perform(function () {
        //獲取so模塊的Module對象
        var module = Process.findModuleByName("libhello.so"); 
        //條件
        var pattern = "03 49 ?? 50 20 44";
        //搜字符串 只是為了將so的內(nèi)存數(shù)據(jù)復(fù)制出來 方便演示~
        var scanSync = Memory.scanSync(module.base, module.size, pattern);
        //申請一個內(nèi)存空間大小為10個字節(jié)
        const r = Memory.alloc(10);
        //復(fù)制以module.base地址開始的10個字節(jié) 那肯定會是7F 45 4C 46...因為一個ELF文件的Magic屬性如此。
        Memory.copy(r,module.base,10);
        console.log(hexdump(r, {
            offset: 0,
            length: 10,
            header: true,
            ansi: false
        }));
    });
}
setImmediate(frida_Memory,0);

輸出如下。
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
e8142070  7f 45 4c 46 01 01 01 00 00 00                    .ELF......

module.base中復(fù)制10個字節(jié)的內(nèi)存到新年申請的r內(nèi)

1.6.6 寫入內(nèi)存Memory.writeByteArray

將字節(jié)數(shù)組寫入一個指定內(nèi)存,代碼示例如下:

function frida_Memory() {     
    Java.perform(function () {
        //定義需要寫入的字節(jié)數(shù)組 這個字節(jié)數(shù)組是字符串"roysue"的十六進制
        var arr = [ 0x72, 0x6F, 0x79, 0x73, 0x75, 0x65];
        //申請一個新的內(nèi)存空間 返回指針 大小是arr.length
        const r = Memory.alloc(arr.length);
        //將arr數(shù)組寫入R地址中
        Memory.writeByteArray(r,arr);
        //輸出
        console.log(hexdump(r, {
            offset: 0,
            length: arr.length,
            header: true,
            ansi: false
        }));  
    });
}
setImmediate(frida_Memory,0);

輸出如下。
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
00000000  72 6f 79 73 75 65                                roysue

1.6.7 讀取內(nèi)存Memory.readByteArray

將一個指定地址的數(shù)據(jù),代碼示例如下:

function frida_Memory() {     
    Java.perform(function () {
        //定義需要寫入的字節(jié)數(shù)組 這個字節(jié)數(shù)組是字符串"roysue"的十六進制
        var arr = [ 0x72, 0x6F, 0x79, 0x73, 0x75, 0x65];
        //申請一個新的內(nèi)存空間 返回指針 大小是arr.length
        const r = Memory.alloc(arr.length);
        //將arr數(shù)組寫入R地址中
        Memory.writeByteArray(r,arr);
        //讀取r指針,長度是arr.length 也就是會打印上面一樣的值
        var buffer = Memory.readByteArray(r, arr.length);
        //輸出
        console.log("Memory.readByteArray:");
        console.log(hexdump(buffer, {
            offset: 0,
            length: arr.length,
            header: true,
            ansi: false
        }));
      });  
    });
}
setImmediate(frida_Memory,0);

輸出如下。
[Google Pixel::com.roysue.roysueapplication]-> Memory.readByteArray:
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
00000000  72 6f 79 73 75 65                                roysue

結(jié)語

在這篇中我們學(xué)會了在FRIDACLI中如何輸出想要輸出格式,也學(xué)會如何聲明變量,一步步的學(xué)習(xí)。在逐步的學(xué)習(xí)的過程,總是會遇到不同的問題。歌曲<奇跡再現(xiàn)>我相信你一定聽過吧~,新的風(fēng)暴已經(jīng)出現(xiàn),怎么能夠停止不前..遇到問題不要怕,總會解決的。

咱們會在下一篇中來學(xué)會如何使用FRIDA中的Java對象、Interceptor對象、NativePointer對象NativeFunction對象、NativeCallback對象咱們拭目以待吧~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容