日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

初识Frida--Android逆向之Java层hook (一)

發布時間:2025/3/15 Android 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 初识Frida--Android逆向之Java层hook (一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

?

目錄

  • ?????????0x00 文中用到的工具
  • ?????????0x01 hook示例的安裝與分析
  • ?????????????????安裝
  • ?????????????????源代碼分析
  • ?????????0x02 frida自帶的Messages機制與進程交互
  • ?????????0x03 Javascript代碼構造與執行
  • ?????????????????方法一:獲取calc()返回值
  • ?????????????????方法二:修改cnt的值為1000
  • ?????????0x04 總結

?


?

博客同步:訪問

0x00 文中用到的工具

  • Frida
  • jadx-gui 一個強大的android反編譯工具
  • genymotion模擬器
  • Python2.7以及frida-python庫
  • radare2 反匯編器
  • pycharm

0x01 hook示例的安裝與分析

Frida官網給我們了一個ctf的示例,就以此為例子,開始學習frida在android逆向的使用。
rps.apk?下載地址

安裝

使用genymotion等類似android模擬器安裝好打開,發現這是一個石頭剪刀布的游戲應用,簡單的玩了一下,沒什么特別的,直接分析代碼吧,看看到底想干什么。

源代碼分析

使用jadx-gui反編譯,發現app沒有加殼和混淆,當然一來就加殼和混淆的話對我們就太不友好了,接下分析就簡單了,直接看java代碼。當然也可以使用androidkiller,jeb等其他強大的反編譯工具。

?

在MainActivity中找到OnCreate()方法,可以看到只是簡單的聲明了button控件以及對應的監聽器。

1

2

3

4

5

6

7

8

9

10

11

protected void onCreate(Bundle savedInstanceState) {

??????super.onCreate(savedInstanceState);

??????setContentView(R.layout.activity_main);

??????this.P?=?(Button) findViewById(R.id.button);

??????this.S?=?(Button) findViewById(R.id.button3);

??????this.r?=?(Button) findViewById(R.id.buttonR);

??????this.P.setOnClickListener(this);

??????this.r.setOnClickListener(this);

??????this.S.setOnClickListener(this);

??????this.flag?=?0;

??}

繼續查看button的onclick方法,可以看出cpu是通過隨機數組出的,其判斷輸贏的方法在this.showMessageTask中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

public void onClick(View v) {

??????if?(this.flag !=?1) {

??????????this.flag?=?1;

??????????((TextView) findViewById(R.id.textView3)).setText("");

??????????TextView tv?=?(TextView) findViewById(R.id.textView);

??????????TextView tv2?=?(TextView) findViewById(R.id.textView2);

??????????this.m?=?0;

??????????this.n?=?new Random().nextInt(3);??//隨機數0,1,2

??????????tv2.setText(new String[]{"CPU: Paper",?"CPU: Rock",?"CPU: Scissors"}[this.n]);?//隨機出石頭,剪刀,布

??????????if?(v?==?this.P) {

??????????????tv.setText("YOU: Paper");

??????????????this.m?=?0;

??????????}

??????????if?(v?==?this.r) {

??????????????tv.setText("YOU: Rock");

??????????????this.m?=?1;

??????????}

??????????if?(v?==?this.S) {

??????????????tv.setText("YOU: Scissors");

??????????????this.m?=?2;

??????????}

??????????this.handler.postDelayed(this.showMessageTask,?1000);//輸贏判斷方法

??????}

??}

跟進分析showMessageTask,可以看到如果贏了mainActivity.cnt會+1,但是一旦輸了cnt就會置0,而獲取flag的要求是我們得獲勝1000次,...... :(

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

private final Runnable showMessageTask?=?new Runnable() {

????????public void run() {

????????????TextView tv3?=?(TextView) MainActivity.this.findViewById(R.id.textView3);

????????????MainActivity mainActivity;

????????????//我方:布 CPU:石頭?or?我方:石頭 CUP:剪刀 ,則為贏

????????????if?(MainActivity.this.n?-?MainActivity.this.m?==?1) {

????????????????mainActivity?=?MainActivity.this;

????????????????mainActivity.cnt++;

????????????????tv3.setText("WIN! +"?+?String.valueOf(MainActivity.this.cnt));

?????????????//反過來當然是輸咯

????????????}?else?if?(MainActivity.this.m?-?MainActivity.this.n?==?1) {

????????????????MainActivity.this.cnt?=?0;

????????????????tv3.setText("LOSE +0");

?????????????//一樣則打平

????????????}?else?if?(MainActivity.this.m?==?MainActivity.this.n) {

????????????????tv3.setText("DRAW +"?+?String.valueOf(MainActivity.this.cnt));

?????????????//我布? cup:剪刀

????????????}?else?if?(MainActivity.this.m < MainActivity.this.n) {

????????????????MainActivity.this.cnt?=?0;

????????????????tv3.setText("LOSE +0");

????????????}?else?{

????????????????mainActivity?=?MainActivity.this;

????????????????mainActivity.cnt++;

????????????????tv3.setText("WIN! +"?+?String.valueOf(MainActivity.this.cnt));

????????????}

????????????//獲勝1000次則能夠獲取flag

????????????if?(1000?==?MainActivity.this.cnt) {

????????????????tv3.setText("SECCON{"?+?String.valueOf((MainActivity.this.cnt?+?MainActivity.this.calc())?*?107)?+?"}");

????????????}

????????????MainActivity.this.flag?=?0;

????????}

????};

簡單分析一下獲取flag需要的條件,總結有3個辦法:

  • 分析calc()方法能算出答案,但這個方法在so中,得分析匯編代碼才行,當然可以嘗試使用ida pro,F5查看C代碼分析,前提是算法不難。

  • 獲取calc函數的返回值,從而計算答案。

  • 還有一個方法就是,直接將MainActivity.this.cnt的值構造成1000。

接下來就用frida,使用后兩種思路來解這個簡單的示例。但在這之前得先了解Frida自帶的Messages機制,了解frida怎么從通過一個python腳本發送和接收message消息是一個提升理解frida的好方法。

0x02 frida自帶的Messages機制與進程交互

先來看看一個Messages的模板,這里用到的語言分別是python和javascript,他們之間的關系是python作為載體,javascript作為在android中真正執行代碼。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import?frida, sys

?

//hook代碼,采用javascript編寫

jscode?=?"""

//javascript代碼,重點

"""

?

//自定義回調函數

def?on_message(message, data):

????if?message['type']?==?'send':

????????print("[*] {0}".format(message['payload']))

????else:

????????print(message)

?

#重點的4行代碼

process?=?frida.get_usb_device().attach('應用完整包名')

script?=?process.create_script(jscode)

script.on('message', on_message)

script.load()

sys.stdin.read()

當然如果是對此簡單的使用,只需要編寫jscode,以及填寫你要hook的應用完整包名就行了,不過如果單純只會用可能在以后會被模板限制,所以一探究竟還是很有必要。
可以在cmd中,使用python終端的help()函數找到frida庫的源代碼的絕對路徑。

接下來就來具體看看這幾句代碼做了什么事情。

1

2

3

4

5

process?=?frida.get_usb_device().attach('應用完整包名')

script?=?process.create_script(jscode)

script.on('message', on_message)

script.load()

sys.stdin.read()

首先使用了frida.get_usb_device(),返回了一個_get_device函數,跟進_get_device方法。

1

2

def?get_usb_device(timeout?=?0):

????return?_get_device(lambda?device: device.type?==?'tether', timeout)

在_get_device中,通過get_device_manager()實例化DeviceManager類,并調用該類中的enumerate_devices()方法。

1

2

3

4

5

6

7

8

9

10

def?_get_device(predicate, timeout):

????mgr?=?get_device_manager()????????????????//獲取設備管理

????def?find_matching_device():???????????????//尋找匹配設備

????????usb_devices?=?[device?for?device?in?mgr.enumerate_devices()?if?predicate(device)]

????????if?len(usb_devices) >?0:

????????????return?usb_devices[0]

????????else:

????????????return?None

????device?=?find_matching_device()

???...省略

get_device_manager()代碼

1

2

3

4

5

6

def?get_device_manager():

????global?_device_manager

????if?_device_manager?is?None:

????????from?.?import?core

????????_device_manager?=?core.DeviceManager(_frida.DeviceManager())

????return?_device_manager

DeviceManager中enumerate_devices()方法,可以看到enumerate_devices()方法實際上是返回了一個Device()類的實例化對象List。

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

class?DeviceManager(object):

????def?__init__(self, impl):

????????self._impl?=?impl

?

????def?__repr__(self):

????????return?repr(self._impl)

?

????//返回了一個Device()類的實例化。

????def?enumerate_devices(self):

????????return?[Device(device)?for?device?in?self._impl.enumerate_devices()]

?

????def?add_remote_device(self, host):

????????return?Device(self._impl.add_remote_device(host))

?

????def?remove_remote_device(self, host):

????????self._impl.remove_remote_device(host)

?

????def?get_device(self, device_id):

????????devices?=?self._impl.enumerate_devices()

????????if?device_id?is?None:

????????????return?Device(devices[0])

????????for?device?in?devices:

????????????if?device.id?==?device_id:

????????????????return?Device(device)

????????raise?_frida.InvalidArgumentError("unable to find device with id %s"?%?device_id)

?

????def?on(self, signal, callback):

????????self._impl.on(signal, callback)

?

????def?off(self, signal, callback):

????????self._impl.off(signal, callback)

繼續跟進Device類中的,就找到了attach()方法。在attach方法這是設置斷點,看看傳入的數據。

?

?

接下來提供的“應用完整名”是通過self._pid_of()函數去找到對應的進程號pid,然后將pid后通過Session類初始化。到此第一句代碼過程就算是明白了,最終得到的是一個對應進程號pid的Session實例化對象process。

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

class?Device(object):

????def?__init__(self, device):

????????self.id?=?device.id

????????self.name?=?device.name

????????self.icon?=?device.icon

????????self.type?=?device.type

????????self._impl?=?device

?

????def?__repr__(self):

????????return?repr(self._impl)

?

????...節省空間刪除部分方法,詳細內容可自行查看源碼

?

????def?kill(self, target):

????????self._impl.kill(self._pid_of(target))

?

????//返回了一個Session的實例化對象

????def?attach(self, target):

????????return?Session(self._impl.attach(self._pid_of(target)))

?

????def?inject_library_file(self, target, path, entrypoint, data):

????????return?self._impl.inject_library_file(self._pid_of(target), path, entrypoint, data)

?

????def?inject_library_blob(self, target, blob, entrypoint, data):

????????return?self._impl.inject_library_blob(self._pid_of(target), blob, entrypoint, data)

?

????def?on(self, signal, callback):

????????self._impl.on(signal, callback)

?

????def?off(self, signal, callback):

????????self._impl.off(signal, callback)

?

????def?_pid_of(self, target):

????????if?isinstance(target, numbers.Number):

????????????return?target

????????else:

????????????return?self.get_process(target).pid

第二句,緊接著process.create_script(jscode),可以看到它返回一個Script類的實例化,參數不確定。

1

2

def?create_script(self,?*args,?**kwargs):

????????return?Script(self._impl.create_script(*args,?**kwargs))

跟進Script類,可以找到on()方法,在on方法中可以設置自定義回調函數。

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

class?Script(object):

????def?__init__(self, impl):

????????self.exports?=?ScriptExports(self)

?

????????self._impl?=?impl

????????self._on_message_callbacks?=?[]

????????self._log_handler?=?self._on_log

?

????????self._pending?=?{}

????????self._next_request_id?=?1

????????self._cond?=?threading.Condition()

?

????????impl.on('destroyed',?self._on_destroyed)

????????impl.on('message',?self._on_message)

?

???...節省空間刪除部分類方法,詳細內容可自行查看源碼

?

????def?load(self):

????????self._impl.load()

?

???//設置自定義回調函數

????def?on(self, signal, callback):

????????if?signal?==?'message':

????????????self._on_message_callbacks.append(callback)

????????else:

????????????self._impl.on(signal, callback)

?

在IDE中可以看到_on_message_callbacks中存放的on_message函數地址。


接下來調用load()方法,在服務端就啟動javascript腳本了,至于在frida-server服務端怎么執行的,可逆向研究一下frida-server,它才是真正的核心。

0x03 Javascript代碼構造與執行

現在就來使用frida實現剛剛試想的方法。

方法一:獲取calc()返回值

第一種思路就是直接獲取calc的返回值,從native函數定義上知道它的返回值是int類型,當然直接獲取calc函數的返回值是解出問題最簡單的方法。

1

public native?int?calc();

那怎么獲取calc()函數的返回值呢,這個函數在MainActivity類中,直接引用該類下的calc()方法,不就ok了嗎,原理是這樣,下面就來構造一下Javascript代碼。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//Java.Perform 開始執行JavaScript腳本。

Java.perform(function () {

//定義變量MainActivity,Java.use指定要使用的類

????var MainActivity?=?Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');

????//hook該類下的onCreate方法,重新實現它

????MainActivity.onCreate.implementation?=?function () {

????????send("Hook Start...");

????????//調用calc()方法,獲取返回值

????????var returnValue?=?this.calc();

????????send("Return:"+returnValue);

????????var result?=?(1000+returnValue)*107;

????????//解出答案

????????send("Flag:"+"SECCON{"+result.toString()+"}");

????}

});

JavaScript代碼就是這樣,如果不是很理解,學習一下JavaScript基礎即可,下面看看完整的python腳本。

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

import?frida, sys

?

def?on_message(message, data):

????if?message['type']?==?'send':

????????print("[*] {0}".format(message['payload']))

????else:

????????print(message)

?

jscode?=?"""

Java.perform(function () {

????var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');

????MainActivity.onCreate.implementation = function () {

????????send("Hook Start...");

????????var returnValue = this.calc();

????????send("Return:"+returnValue);

????????var result = (1000+returnValue)*107;

????????send("Flag:"+"SECCON{"+result.toString()+"}");

????}

});

"""

?

process?=?frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors')

script?=?process.create_script(jscode)

script.on('message', on_message)

script.load()

sys.stdin.read()

接下來運行一下,看看能否成功。

?

步驟如下:

  • 啟動模擬器,使用adb push將對應架構的frida-server文件push到模擬器中
    /data/local/tmp目錄下。
  • adb shell 進入/data/local/tmp目錄,啟動frida-server。
  • 開啟端口轉發
    adb forward tcp:27043 tcp:27043
    adb forward tcp:27042 tcp:27042
  • 啟動應用后,在命令行等執行python腳本。
  • 因為hook的是應用的onCreate方法,執行python腳本的前提是應用首先啟動,這樣才能attach到該應用,所以還得返回模擬器桌面重新啟動應用,這樣它才會執行hook的onCreate()方法,結果如下。

    方法二:修改cnt的值為1000

    第二種思路也比較簡單,我們需要修改cnt的值,但如果直接修改cnt的初始值為1000的話,在游戲中可能存在不確定因素,比如輸了會置0,贏了cnt值就變成1001了,所以還得控制一下輸贏,而輸贏的條件是電腦出什么,所以最終hook的方法就在onClick中。
    從onClick()中可以知道,控制輸贏的在于修改this.n 和 this.m的值,再來看看源代碼。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    public void onClick(View v) {

    ???????if?(this.flag !=?1) {

    ???????????this.flag?=?1;

    ???????????((TextView) findViewById(R.id.textView3)).setText("");

    ???????????TextView tv?=?(TextView) findViewById(R.id.textView);

    ???????????TextView tv2?=?(TextView) findViewById(R.id.textView2);

    ???????????this.m?=?0;

    ???????????//控制電腦出拳

    ???????????this.n?=?new Random().nextInt(3);

    ???????????tv2.setText(new String[]{"CPU: Paper",?"CPU: Rock",?"CPU: Scissors"}[this.n]);

    ???????????if?(v?==?this.P) {

    ???????????????tv.setText("YOU: Paper");

    ???????????????this.m?=?0;

    ???????????}

    ???????????if?(v?==?this.r) {

    ???????????????tv.setText("YOU: Rock");

    ???????????????this.m?=?1;

    ???????????}

    ???????????if?(v?==?this.S) {

    ???????????????tv.setText("YOU: Scissors");

    ???????????????this.m?=?2;

    ???????????}

    ???????????this.handler.postDelayed(this.showMessageTask,?1000);

    ???????}

    來看JavaScript代碼怎么寫吧

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    Java.perform(function () {

    ????var MainActivity?=?Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');

    ????//hook onClick方法,此處要注意的是onClick方法是傳遞了一個View參數v

    ????MainActivity.onClick.implementation?=?function (v) {

    ????????send("Hook Start...");

    ????????//調用onClick,模擬點擊事件

    ????????this.onClick(v);

    ????????//修改參數

    ????????this.n.value?=?0;

    ????????this.m.value?=?2;

    ????????this.cnt.value?=?999;

    ????????send("Success!")

    ????}

    });

    完整python代碼

    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

    import?frida, sys

    ?

    def?on_message(message, data):

    ????if?message['type']?==?'send':

    ????????print("[*] {0}".format(message['payload']))

    ????else:

    ????????print(message)

    ?

    jscode?=?"""

    Java.perform(function () {

    ????var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');

    ????MainActivity.onClick.implementation = function (v) {

    ????????send("Hook Start...");

    ????????this.onClick(v);

    ????????this.n.value = 0;

    ????????this.m.value = 2;

    ????????this.cnt.value = 999;

    ????????send("Success!")

    ????}

    });

    """

    ?

    process?=?frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors')

    script?=?process.create_script(jscode)

    script.on('message', on_message)

    script.load()

    sys.stdin.read()

    執行python腳本,任意點擊按鈕,答案就出來了。

    ?

    當然,如果so中的calc()函數算法不難的前提,直接使用ida pro或者radare2分析匯編代碼也是可以的。這里給出用radare2反匯編出來的代碼。可以看到,calc()函數就單純的返回了int值7。

    0x04 總結

    • 一般分析流程
      1.反編譯apk,分析代碼尋找hook點。
      2.編寫js代碼,調用類的方法或者替換。
      3.在python中執行即可。
      下面一篇會更詳細介紹frida的使用。

    https://bbs.pediy.com/thread-227232.htm

    總結

    以上是生活随笔為你收集整理的初识Frida--Android逆向之Java层hook (一)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。