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

歡迎訪問 生活随笔!

生活随笔

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

Android

进阶Frida--Android逆向之动态加载dex Hook(三)

發(fā)布時間:2025/3/15 Android 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 进阶Frida--Android逆向之动态加载dex Hook(三) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前段時間看到有朋友在問在怎么使用frida去hook動態(tài)加載的dex之類的問題,確實關于如何hook動態(tài)加載的dex,網(wǎng)上的資料很少,更不用說怎么使用frida去hook動態(tài)加載的dex了。(frida的官方文檔已經(jīng)無力吐槽...)后來偶然的情況下發(fā)現(xiàn)了一道CTF題目可以作為很好的案例,所以花了很多心思將文章寫下來分享給大家,涉及的內容比較多,我打算用上下篇將frida hook動態(tài)加載的dex的方法將清楚,上篇主要是引導及一些前置的知識。

?

?

目錄

  • ?????????文章涉及內容及使用到的工具
  • ?????????????????0x00 使用到的工具
  • ?????????????????0x01 涉及知識點
  • ?????????apk的安裝和分析
  • ?????????????????0x00 apk安裝及使用
  • ?????????????????0x01 靜態(tài)代碼分析
  • ?????????????????0x02 Robust熱修復框架原理
  • ?????????JavaScript代碼構造
  • ?????????????????0x00 hook點分析
  • ?????????????????0x01 hook代碼構造
  • ?????????????????0x02 執(zhí)行py腳本獲取結果
  • ?????????總結

?


?

博客同步:訪問?(一些web安全的研究會直接發(fā)布到這上面)

文章涉及內容及使用到的工具

0x00 使用到的工具

  • ADT(Android Developer Tools)
  • Jadx-gui
  • JEB
  • frida
  • apktool
  • 010 editor
  • 天天模擬器(genymotion,實體機等亦可)

0x01 涉及知識點

  • Robust熱修復框架原理
  • Java 反射
  • Robust類 hook
  • frida基礎hook

apk的安裝和分析

文章使用的是DDCTF2018的android逆向第二題Hello Baby Dex

?

示例地址:下載

0x00 apk安裝及使用

程序功能很簡單,就是輸入密碼并驗證,錯誤會Toast出一些信息,按這個慣性,可能得輸入某些正確的值才能獲取flag吧,廢話不多說,直接反編譯看看。

0x01 靜態(tài)代碼分析

一般情況下,我是直接扔到jadx中去看反編譯后的代碼,可是這次jadx反編譯出來的結果有很多錯誤,這里我就不貼圖了,大家可以嘗試一下,看看是什么錯誤。后面放到jeb工具中,便沒有報錯,所以查看反編譯的效果時,大家可以多嘗試幾個反編譯器對比效果,查看AndroidManifest.xml,找到了程序的入口MainActivity。

1

2

3

4

5

6

<activity android:name="cn.chaitin.geektan.crackme.MainActivity">

???????????<intent-filter>

???????????????<action android:name="android.intent.action.MAIN"?/>

???????????????<category android:name="android.intent.category.LAUNCHER"?/>

???????????</intent-filter>

???????</activity>

在此同時,在cmd中通過apktool d [apk],再將apk文件反編譯出來。

接下來直接定位到MainActivity的onCreate()方法,可以看到代碼是混淆過的,閱讀上稍微有點小困難,但是在onCreate()方法中,我們還是可以發(fā)現(xiàn)一些可疑的方法及變量,比如PatchProxy,changeQuickRedirect等。

?

繼續(xù)搜索有用的信息,我們可以發(fā)現(xiàn)在onCreate()方法的else邏輯中調用了this.runRobust(),并且我們發(fā)現(xiàn)在類中每一個方法里面都會存在一些changeQuickRedirect變量,以及isSupport(),accessDispatch()方法。

上面的先放一邊,我們來看看runRobust()方法中寫了什么,在else條件語句中可以看到它實例化了一些對象。

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

private void runRobust() {

?//固定格式的changeQuickRedirect,isSupport,accessDispatch

???????int?v4?=?4;

???????Object[] v0?=?new?Object[0];

???????ChangeQuickRedirect v2?=?MainActivity.changeQuickRedirect;

???????Class[] v5?=?new Class[0];

???????Class v6?=?Void.TYPE;

???????MainActivity v1?=?this;

???????boolean v0_1?=?PatchProxy.isSupport(v0, v1, v2, false, v4, v5, v6);

???????if(v0_1) {

???????????v0?=?new?Object[0];

???????????v2?=?MainActivity.changeQuickRedirect;

???????????v5?=?new Class[0];

???????????v6?=?Void.TYPE;

???????????v1?=?this;

???????????PatchProxy.accessDispatch(v0, v1, v2, false, v4, v5, v6);

???????}

???????else?{

???????????Context v1_1?=?this.getApplicationContext();

???????????//實例化PatchManipulateImp類

???????????PatchManipulateImp v2_1?=?new PatchManipulateImp();

???????????//以及實例化PatchExecutor類

???????????PatchExecutor v0_2?=?new PatchExecutor(v1_1, ((PatchManipulate)v2_1), new GeekTanCallBack());

???????????v0_2.start();

???????}

???}

跟進PatchManipulateImp類,可以發(fā)現(xiàn)在fetchPatchList方法中,它調用了getAssets().open("GeekTan.BMP");從資源文件夾中,加載了一個BMP的圖片文件,并將這個BMP文件內容寫入GeekTan.jar中。

?

具體代碼如下:

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

????try?{

????????v10?=?arg17.getAssets().open("GeekTan.BMP");

????????v8?=?new?File(arg17.getCacheDir()?+?File.separator?+?"GeekTan"?+?File.separator?+?"GeekTan.jar");

????????if(!v8.getParentFile().exists()) {

????????????v8.getParentFile().mkdirs();

????????}

????}

????catch(Exception v9) {

????????goto label_171;

????}

//將v8通過FileOutputStream方法賦值v12

????try?{

????????v12?=?new FileOutputStream(v8);

????}

????catch(Throwable v0_3) {

????????goto label_17;

????}

?

????int?v0_4?=?1024;

????try?{

????????byte[] v7?=?new byte[v0_4];

????????while(true) {

????????//v10是GeekTan.BMP賦值v11,

????????????int?v11?=?v10.read(v7);

????????????if(v11 <=?0) {

????????????????break;

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

??//最終寫入到v12中,而v12是new FileOutputStream(v8);

????????????((OutputStream)v12).write(v7,?0, v11);

????????}

????}

????catch(Throwable v0_3) {

????????goto label_88;

????}

顯然這個BMP文件很可疑,我們放到010 editor中看看二進制的內容,發(fā)現(xiàn)它真正的文件格式是一個壓縮包,并且可以直觀的看到在壓縮包中存在一個dex文件。

同時我們還可以發(fā)現(xiàn),fetchPatchList()方法中實例化了一個Patch對象。

1

Patch v13?=?new Patch();

并在fetchPatchList()方法的最后,調用

1

2

v0_6?=?"cn.chaitin.geektan.crackme.PatchesInfoImpl";

v13.setPatchesInfoImplClassFullName(v0_6);

回到runRobust(),接下來實例化了PatchExecutor類,可以看到第二個參數(shù)即為PatchManipulateImp類的實例。

1

2

3

Context v1_1?=?this.getApplicationContext();

PatchManipulateImp v2_1?=?new PatchManipulateImp();

PatchExecutor v0_2?=?new PatchExecutor(v1_1, ((PatchManipulate)v2_1), new GeekTanCallBack());

這個PatchExecutor類是在com.meituan.robust包下的,這是一個第三方的包,目前官方已經(jīng)將其開源,因為直接看混淆的代碼還是比較費時的,在此暫停上面的分析,簡單學習一下Robust。

0x02 Robust熱修復框架原理

我們先簡單了解一下Robust是什么,Robust是美團出的一款熱修復框架,可以在github上面下載它的最新的源碼。

?

Robust的基本原理,我們主要了解這4個步驟就能清晰明白了。
1. 將apk代碼中每個函數(shù)都在編譯打包階段自動的插入一段代碼。
例如,原函數(shù):

1

2

3

public?long?getIndex() {

????????return?100;

????}

它會被處理成這樣:

1

2

3

4

5

6

7

8

9

10

11

12

13

//在該類中聲明一個接口變量changeQuickRedirect

public static ChangeQuickRedirect changeQuickRedirect;

?

//在要修復的方法中添加以下邏輯代碼

????public?long?getIndex() {

????????if(changeQuickRedirect !=?null) {

????????????//PatchProxy中封裝了獲取當前className和methodName的邏輯,并在其內部最終調用了changeQuickRedirect的對應函數(shù)

????????????if(PatchProxy.isSupport(new?Object[0], this, changeQuickRedirect, false)) {

????????????????return?((Long)PatchProxy.accessDispatch(new?Object[0], this, changeQuickRedirect, false)).longValue();

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

????????}

????????return?100L;

????}

可以看到Robust為每個class增加了個類型為ChangeQuickRedirect的靜態(tài)成員,而在每個方法前都插入了使用changeQuickRedirect相關的邏輯,當changeQuickRedirect不為null時,會執(zhí)行到accessDispatch方法從而替換掉之前老的邏輯,達到修復的目的。

?

2.生成需要修復的類及方法的類文件并打包成dex。
接下來你可能已經(jīng)將需要修復的類及方法寫好了,這個時候調用Robust的autopatch文件夾中的類及方法會生成如下主要文件:PatchesInfoImpl.java,xxxPatchControl.java(其中xxx為原類的名字)。

?

PatchesInfoImpl.java的內是由PatchesInfoFactory類的createPatchesInfoClass生成的,這是它生成PatchesInfoImpl邏輯,可以看到,這個類其實是用拼接得到的。

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

private CtClass createPatchesInfoClass() {

???????try?{

???????//創(chuàng)建PatchesInfoImpl類

???????????CtClass ctPatchesInfoImpl?=?classPool.makeClass(Config.patchPackageName?+?".PatchesInfoImpl");

???????????ctPatchesInfoImpl.getClassFile().setMajorVersion(ClassFile.JAVA_7);

???????????ctPatchesInfoImpl.setInterfaces(new CtClass[]{classPool.get("com.meituan.robust.PatchesInfo")});

???????????StringBuilder methodBody?=?new StringBuilder();

???????????//拼接類中的內容

???????????methodBody.append("public java.util.List getPatchedClassesInfo() {");

???????????methodBody.append("? java.util.List patchedClassesInfos = new java.util.ArrayList();");

???????????for?(int?i?=?0; i < Config.modifiedClassNameList.size(); i++) {

???????????????if?(Constants.OBSCURE) {

???????????????????methodBody.append("com.meituan.robust.PatchedClassInfo patchedClass"?+?i?+?" = new com.meituan.robust.PatchedClassInfo(\""?+ReadMapping.getInstance().getClassMappingOrDefault(Config.modifiedClassNameList.get(i)).getValueName()?+?"\",\""?+NameManger.getInstance().getPatchControlName(Config.modifiedClassNameList.get(i).substring(Config.modifiedClassNameList.get(i).lastIndexOf('.')?+?1))?+?"\");");

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

???????????????????methodBody.append("com.meituan.robust.PatchedClassInfo patchedClass"?+?i?+?" = new com.meituan.robust.PatchedClassInfo(\""?+?Config.modifiedClassNameList.get(i)?+?"\",\""?+NameManger.getInstance().getPatchControlName(Config.modifiedClassNameList.get(i).substring(Config.modifiedClassNameList.get(i).lastIndexOf('.')?+?1))?+?"\");");

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

???????????????methodBody.append("patchedClassesInfos.add(patchedClass"?+?i?+?");");

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

???????????methodBody.append(Constants.ROBUST_UTILS_FULL_NAME?+?".isThrowable=!"?+?Config.catchReflectException?+?";");

???????????methodBody.append("return patchedClassesInfos;\n"?+

???????????????????"??? }");

???????????CtMethod m?=?make(methodBody.toString(), ctPatchesInfoImpl);

???????????ctPatchesInfoImpl.addMethod(m);

???????????return?ctPatchesInfoImpl;

???????} catch (Exception e) {

???????????e.printStackTrace();

???????????throw new RuntimeException(e);

???????}

???}

生成的PatchesInfoImpl類型及類內容如下。

1

2

3

4

5

6

7

8

9

10

public?class?PatchesInfoImpl implements PatchesInfo {

????public?List?getPatchedClassesInfo() {

????????List?arrayList?=?new ArrayList();

????????//PatchedClassInfo("原來的類","修復后的類control");

????????arrayList.add(new PatchedClassInfo("cn.chaitin.geektan.crackme.MainActivity",?"cn.chaitin.geektan.crackme.MainActivityPatchControl"));

????????arrayList.add(new PatchedClassInfo("cn.chaitin.geektan.crackme.MainActivity$1",?"cn.chaitin.geektan.crackme.MainActivity$1PatchControl"));

????????EnhancedRobustUtils.isThrowable?=?false;

????????return?arrayList;

????}

}

另外還會生成一個xxxPatchControl類,通過PatchesControlFactory的createControlClass()方法生成,具體的邏輯和生成PatchesInfoImpl類類似,大家可以自行去查看源代碼,其中每個Control類中都存在以下靜態(tài)成員變量和方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

public?class?xxxPatchControl implements ChangeQuickRedirect

{

?

public static final String MATCH_ALL_PARAMETER?=?"(\\w*\\.)*\\w*";

?

private static final?Map<Object,?Object> keyToValueRelation?=?new WeakHashMap();

?

//獲取函數(shù)的參數(shù)的方法

public?Object?getRealParameter(Object?obj){..具體邏輯..}

?

//判斷是否支持修復

public boolean isSupport(String methodName,?Object[] paramArrayOfObject)

{..具體邏輯.}

?

//執(zhí)行到accessDispatch方法替換舊的類方法

public?Object?accessDispatch(String methodName,?Object[] paramArrayOfObject) {.具體邏輯..}

}

?

//解決boolean被優(yōu)化成byte的問題

private static?Object?fixObj(Object?booleanObj) {.具體邏輯..}

?

}

將含有PatchesInfoImpl.java和xxxPatchControl.java,以及xxxPatch.java(具體修復的類)打包成dex文件。

?

3.動態(tài)加載dex文件,以反射的方式修改替換舊類。

?

在此,我們回到剛剛暫停的地方,我們跟進PatchExecutor類看看。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

//可以看到PatchExecutor繼承線程類Thread

public?class?PatchExecutor extends Thread {

????protected Context context;

????protected PatchManipulate patchManipulate;

????protected RobustCallBack robustCallBack;

????//構造函數(shù)

????public PatchExecutor(Context context, PatchManipulate patchManipulate, RobustCallBack robustCallBack) {

????????this.context?=?context.getApplicationContext();

????????this.patchManipulate?=?patchManipulate;

????????this.robustCallBack?=?robustCallBack;

????}

????public void run() {

????????try?{

????????????//拉取補丁列表

????????????List<Patch> patches?=?fetchPatchList();

????????????//應用補丁列表

????????????applyPatchList(patches);

????????} catch (Throwable t) {

????????????Log.e("robust",?"PatchExecutor run", t);

????????????robustCallBack.exceptionNotify(t,?"class:PatchExecutor,method:run,line:36");

????????}

????}

????...

????}

在run方法中,主要做了2件事。

?

1.獲取補丁列表。

1

2

3

4

5

List<Patch> patches?=?fetchPatchList();

//PatchManipulateImp類的fetchPatchList方法

protected?List<Patch> fetchPatchList() {

????????return?patchManipulate.fetchPatchList(context);

????}

2.應用補丁。

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

applyPatchList(patches);

?

protected void applyPatchList(List<Patch> patches) {

???????if?(null?==?patches || patches.isEmpty()) {

???????????return;

???????}

???????Log.d("robust",?" patchManipulate list size is "?+?patches.size());

???????for?(Patch p : patches) {

???????????if?(p.isAppliedSuccess()) {

???????????????Log.d("robust",?"p.isAppliedSuccess() skip "?+?p.getLocalPath());

???????????????continue;

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

???????????if?(patchManipulate.ensurePatchExist(p)) {

???????????????boolean currentPatchResult?=?false;

???????????????try?{

???????????????//真正應用補丁的方法patch()

???????????????????currentPatchResult?=?patch(context, p);

???????????????} catch (Throwable t) {

???????????????????robustCallBack.exceptionNotify(t,?"class:PatchExecutor method:applyPatchList line:69");

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

???????????????if?(currentPatchResult) {

???????????????????//設置patch 狀態(tài)為成功

???????????????????p.setAppliedSuccess(true);

???????????????????//統(tǒng)計PATCH成功率 PATCH成功

???????????????????robustCallBack.onPatchApplied(true, p);

?

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

???????????????????//統(tǒng)計PATCH成功率 PATCH失敗

???????????????????robustCallBack.onPatchApplied(false, p);

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

?

???????????????Log.d("robust",?"patch LocalPath:"?+?p.getLocalPath()?+?",apply result "?+?currentPatchResult);

?

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

???????}

???}

跟進patch()方法,我們具體分析一下。

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

protected boolean patch(Context context, Patch patch) {

????//驗證patch的hash

????????if?(!patchManipulate.verifyPatch(context, patch)) {

????????????robustCallBack.logNotify("verifyPatch failure, patch info:"?+?"id = "?+?patch.getName()?+?",md5 = "?+patch.getMd5(),?"class:PatchExecutor method:patch line:107");

????????????return?false;

????????}

????//調用DexClassLoader動態(tài)加載dex

????????DexClassLoader classLoader?=?new DexClassLoader(patch.getTempPath(), context.getCacheDir().getAbsolutePath(),

????????????????null, PatchExecutor.class.getClassLoader());

????????patch.delete(patch.getTempPath());

?

????????Class patchClass, oldClass;

?

????????Class patchsInfoClass;

????????PatchesInfo patchesInfo?=?null;

????????try?{

????????//動態(tài)加載PatchesInfoImpl,獲取要patch的類

????????????patchsInfoClass?=?classLoader.loadClass(patch.getPatchesInfoImplClassFullName());

????????????patchesInfo?=?(PatchesInfo) patchsInfoClass.newInstance();

????????????Log.d("robust",?"PatchsInfoImpl ok");

????????} catch (Throwable t) {

????????????robustCallBack.exceptionNotify(t,?"class:PatchExecutor method:patch line:108");

????????????Log.e("robust",?"PatchsInfoImpl failed,cause of"?+?t.toString());

????????????t.printStackTrace();

????????}

?

????????if?(patchesInfo?==?null) {

????????????robustCallBack.logNotify("patchesInfo is null, patch info:"?+?"id = "?+?patch.getName()?+?",md5 = "?+patch.getMd5(),?"class:PatchExecutor method:patch line:114");

????????????return?false;

????????}

?

????????//classes need to patch

?

????????//獲取要打補丁的類patchedClasses?

????????List<PatchedClassInfo> patchedClasses?=?patchesInfo.getPatchedClassesInfo();

????????if?(null?==?patchedClasses || patchedClasses.isEmpty()) {

????????????robustCallBack.logNotify("patchedClasses is null or empty, patch info:"?+?"id = "?+?patch.getName()?+?",md5 = "?+patch.getMd5(),?"class:PatchExecutor method:patch line:122");

????????????return?false;

????????}

?

??????????//循環(huán)類名,將patchedClasses中的類打補丁

????????for?(PatchedClassInfo patchedClassInfo : patchedClasses) {

????????????String patchedClassName?=?patchedClassInfo.patchedClassName;

????????????String patchClassName?=?patchedClassInfo.patchClassName;

????????????if?(TextUtils.isEmpty(patchedClassName) || TextUtils.isEmpty(patchClassName)) {

????????????????robustCallBack.logNotify("patchedClasses or patchClassName is empty, patch info:"?+?"id = "?+?patch.getName()?+",md5 = "?+?patch.getMd5(),?"class:PatchExecutor method:patch line:131");

????????????????continue;

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

????????????Log.d("robust",?"current path:"?+?patchedClassName);

????????????try?{

????????????//將oldClass的changeQuickRedirectField的值設置為null

????????????????oldClass?=?classLoader.loadClass(patchedClassName.trim());

????????????????Field[] fields?=?oldClass.getDeclaredFields();

????????????????Log.d("robust",?"oldClass :"?+?oldClass?+?"???? fields "?+?fields.length);

????????????????Field changeQuickRedirectField?=?null;

????????????????for?(Field field : fields) {

????????????????????if?(TextUtils.equals(field.getType().getCanonicalName(), ChangeQuickRedirect.class.getCanonicalName()) && TextUtils.equals(field.getDeclaringClass().getCanonicalName(), oldClass.getCanonicalName())) {

????????????????????????changeQuickRedirectField?=?field;

????????????????????????break;

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

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

????????????????if?(changeQuickRedirectField?==?null) {

????????????????????robustCallBack.logNotify("changeQuickRedirectField? is null, patch info:"?+?"id = "?+?patch.getName()?+",md5 = "?+?patch.getMd5(),?"class:PatchExecutor method:patch line:147");

????????????????????Log.d("robust",?"current path:"?+?patchedClassName?+?" something wrong !! can? not find:ChangeQuickRedirect in"?+?patchClassName);

????????????????????continue;

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

????????????????Log.d("robust",?"current path:"?+?patchedClassName?+?" find:ChangeQuickRedirect "?+?patchClassName);

????????????????try?{

?????????????//動態(tài)加載補丁類

????????????????????patchClass?=?classLoader.loadClass(patchClassName);

????????????????????Object?patchObject?=?patchClass.newInstance();

????????????????????changeQuickRedirectField.setAccessible(true);

?????????????//將它的changeQuickRedirectField設置為patchObject實例。

????????????????????changeQuickRedirectField.set(null, patchObject);

????????????????????Log.d("robust",?"changeQuickRedirectField set sucess "?+?patchClassName);

????????????????} catch (Throwable t) {

????????????????????Log.e("robust",?"patch failed! ");

????????????????????t.printStackTrace();

????????????????????robustCallBack.exceptionNotify(t,?"class:PatchExecutor method:patch line:163");

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

????????????} catch (Throwable t) {

????????????????Log.e("robust",?"patch failed! ");

????????????????t.printStackTrace();

????????????????robustCallBack.exceptionNotify(t,?"class:PatchExecutor method:patch line:169");

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

????????}

????????Log.d("robust",?"patch finished ");

????????return?true;

????}

4.isSupport和accessDispatch
接下來我們再來看看onCreate()中的代碼,雖然混淆后代碼看起來很冗長,但是通過剛剛我們對Robust原理的簡單分析,現(xiàn)在已經(jīng)可以清晰的知道,這其實就是isSupport()和accessDispatch()。

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

public void onCreate(Bundle arg13) {

????????int?v4?=?3;

????????Object[] v0?=?new?Object[1];

????????v0[0]?=?arg13;

????????ChangeQuickRedirect v2?=?MainActivity.changeQuickRedirect;

????????Class[] v5?=?new Class[1];

????????Class v1?=?Bundle.class;

????????v5[0]?=?v1;

????????Class v6?=?Void.TYPE;

????????MainActivity v1_1?=?this;

????????boolean v0_1?=?PatchProxy.isSupport(v0, v1_1, v2, false, v4, v5, v6);

????????if(v0_1) {

????????????v0?=?new?Object[1];

????????????v0[0]?=?arg13;

????????????v2?=?MainActivity.changeQuickRedirect;

????????????v5?=?new Class[1];

????????????v1?=?Bundle.class;

????????????v5[0]?=?v1;

????????????v6?=?Void.TYPE;

????????????v1_1?=?this;

????????????PatchProxy.accessDispatch(v0, v1_1, v2, false, v4, v5, v6);

????????}

????????else?{

?

???????????.....

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

我們看看源碼中的isSupport()具體做了什么。

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

public static boolean isSupport(Object[] paramsArray,?Object?current, ChangeQuickRedirect changeQuickRedirect, boolean isStatic,?int?methodNumber, Class[] paramsClassTypes, Class returnType) {

????????//Robust補丁優(yōu)先執(zhí)行,其他功能靠后

????????if?(changeQuickRedirect?==?null) {

????????????//不執(zhí)行補丁,輪詢其他監(jiān)聽者

????????????if?(registerExtensionList?==?null || registerExtensionList.isEmpty()) {

????????????????return?false;

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

????????????for?(RobustExtension robustExtension : registerExtensionList) {

????????????????if?(robustExtension.isSupport(new RobustArguments(paramsArray, current, isStatic, methodNumber, paramsClassTypes, returnType))) {

????????????????????robustExtensionThreadLocal.set(robustExtension);

????????????????????return?true;

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

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

????????????return?false;

????????}

????????//獲取?classMethod?=?className?+?":"?+?methodName?+?":"?+?isStatic?+?":"?+?methodNumber;

????????String?classMethod?=?getClassMethod(isStatic, methodNumber);

????????if?(TextUtils.isEmpty(classMethod)) {

????????????return?false;

????????}

????????Object[] objects?=?getObjects(paramsArray, current, isStatic);

????????try?{

????????/*調用changeQuickRedirect.isSupport,還記得這個changeQuickRedirect

????????嗎,他是在第3步中changeQuickRedirectField.set(null, patchObject);

????????得到的補丁類的實例。*/

????????????return?changeQuickRedirect.isSupport(classMethod, objects);

????????} catch (Throwable t) {

????????????return?false;

????????}

????}

通過上面的分析,可以知道只有當存在補丁的類changeQuickRedirect.isSupport()才會返回值。這個時候我們把剛剛第二步打包的dex反編譯看看,我們可以看到在xxxPatchControl類中存在isSupport,它返回的值其實就是methodNumber。

1

2

3

public boolean isSupport(String methodName,?Object[] paramArrayOfObject) {

????????return?"3:6:".contains(methodName.split(":")[3]);

????}

accessDispatch()方法,替換原方法。

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 static?Object?accessDispatch(Object[] paramsArray,?Object?current, ChangeQuickRedirect changeQuickRedirect, boolean isStatic,?int?methodNumber, Class[] paramsClassTypes, Class returnType) {

?

???????//如果changeQuickRedirect為null...

????????if?(changeQuickRedirect?==?null) {

????????????RobustExtension robustExtension?=?robustExtensionThreadLocal.get();

????????????robustExtensionThreadLocal.remove();

????????????if?(robustExtension !=?null) {

????????????????notify(robustExtension.describeSelfFunction());

????????????????return?robustExtension.accessDispatch(new RobustArguments(paramsArray, current, isStatic, methodNumber, paramsClassTypes, returnType));

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

????????????return?null;

????????}

????????//同樣獲取?classMethod?=?className?+?":"?+?methodName?+?":"?+?isStatic?+?":"?+?methodNumber;

????????String?classMethod?=?getClassMethod(isStatic, methodNumber);

????????if?(TextUtils.isEmpty(classMethod)) {

????????????return?null;

????????}

????????notify(Constants.PATCH_EXECUTE);

?

????????Object[] objects?=?getObjects(paramsArray, current, isStatic);

?

????????//返回changeQuickRedirect.accessDispatch。

????????return?changeQuickRedirect.accessDispatch(classMethod, objects);

????}

具體看看PatchControl類中的accessDispatch。

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

public?Object?accessDispatch(String methodName,?Object[] paramArrayOfObject) {

????try?{

????????MainActivityPatch mainActivityPatch;

????????//判斷classMethod的isStatic是否為false,其實在調用accessDispatch傳遞的就是false。

????????if?(methodName.split(":")[2].equals("false")) {

????????????MainActivityPatch mainActivityPatch2;

????????????if?(keyToValueRelation.get(paramArrayOfObject[paramArrayOfObject.length?-?1])?==?null) {

????????????????mainActivityPatch2?=?new MainActivityPatch(paramArrayOfObject[paramArrayOfObject.length?-?1]);

????????????????keyToValueRelation.put(paramArrayOfObject[paramArrayOfObject.length?-?1], null);

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

????????????????mainActivityPatch2?=?(MainActivityPatch) keyToValueRelation.get(paramArrayOfObject[paramArrayOfObject.length?-1]);

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

????????????mainActivityPatch?=?mainActivityPatch2;

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

????????????mainActivityPatch?=?new MainActivityPatch(null);

????????}

????????//根據(jù)methodNumber,選取要執(zhí)行的patch方法。

????????Object?obj?=?methodName.split(":")[3];

????????if?("3".equals(obj)) {

????????????mainActivityPatch.onCreate((Bundle) paramArrayOfObject[0]);

????????}

????????if?("6".equals(obj)) {

????????????return?mainActivityPatch.Joseph(((Integer) paramArrayOfObject[0]).intValue(), ((Integer) paramArrayOfObject[1]).intValue());

????????}

????} catch (Throwable th) {

????????th.printStackTrace();

????}

????return?null;

}

花了比較多的篇幅把Robust的基本原理給大家介紹了,接下來完全回到這個apk。

JavaScript代碼構造

0x00 hook點分析

經(jīng)過我們對Robust的分析,我們現(xiàn)在已經(jīng)比較清晰的知道了我們需要攻克的難點,它是通過Robust熱修復框架將一些方法熱修復了,所以我們這里必須知道,它修復了哪些類及方法,當然在上面我們已經(jīng)零星看到了一些細節(jié),現(xiàn)在我們來具體看看。

?

我們先將assets文件夾下的GeekTan.BMP改成GeekTan.rar,并解壓,得到dex文件直接扔到jadx中分析。在PatchesInfoImpl類中可以看到2個要被修復的類信息。

先看MainActivityPatchControl類,我們看到在accessDispatch(),onCreate()和Joseph()方法將會通過判斷ethodNumber來選取。

繼續(xù)查看MainActivity$1PatchControl類,同樣發(fā)現(xiàn)onClick被修復了。

所以這個時候,我們必須知道onClick真正執(zhí)行的邏輯是什么。查看MainActivity$1Patch類中的真正的onClick方法。很明顯的兩句提示語,可以明確我們要的答案就在這里。

仔細分析onClick方法,可以發(fā)現(xiàn)很多invokeReflectStaticMethod,getFieldValue,invokeReflectMethod方法,同樣我們還能發(fā)現(xiàn)flag就在這里面。

1

2

3

4

5

6

7

//flag是通過append將字符串以及Joseph(int,int)的返回值拼接構成的。

String?str?=?"DDCTF{";

str?=?(String) EnhancedRobustUtils.invokeReflectMethod("Joseph", obj2, getRealParameter(new?Object[]{new Integer(5), new Integer(6)}), new Class[]{Integer.TYPE, Integer.TYPE}, MainActivity.class);

str?=?(String) EnhancedRobustUtils.invokeReflectMethod("Joseph", obj2, getRealParameter(new?Object[]{new Integer(7), new Integer(8)}), new Class[]{Integer.TYPE, Integer.TYPE}, MainActivity.class);

str?=?"}";

//最終將我們輸入的值與上面構造的equals比較,判斷是否準確。

if?(((Boolean) EnhancedRobustUtils.invokeReflectMethod("equals", obj, getRealParameter(new?Object[]{str2}), new Class[]{Object.class}, String.class)).booleanValue()) {...}

通過上面的分析,可以發(fā)現(xiàn)hook有2個思路:
1.hook?EnhancedRobustUtils類下的方法獲取方法執(zhí)行的返回值。
2.hook 動態(tài)加載的類MainActivityPatch的Joseph方法,直接調用它獲取返回值。(下篇)

0x01 hook代碼構造

先來看看EnhancedRobustUtils類下的方法invokeReflectMethod。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public static?Object?invokeReflectMethod(String methodName,?Object?targetObject,?Object[] parameters, Class[] args, Class declaringClass) {

????????try?{

????????????//可以看到這里是通過反射的方法拿到類實例

????????????Method method?=?getDeclaredMethod(targetObject, methodName, args, declaringClass);

????????????//代入?yún)?shù),調用方法

????????????return?method.invoke(targetObject, parameters);

????????} catch (Exception e) {

????????????e.printStackTrace();

????????}

????????if?(isThrowable) {

????????????throw new RuntimeException("invokeReflectMethod error "?+?methodName?+?"?? parameter?? "?+?parameters?+?" targetObject "?+?targetObject.toString()?+?"? args? "?+?args);

????????}

????????return?null;

????}

我們再看看invokeReflectConstruct。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public static?Object?invokeReflectConstruct(String className,?Object[] parameter, Class[] args) {

????????try?{

????????????//通過Class.forName(className)反射得到一個Class對象

????????????Class clazz?=?Class.forName(className);

????????????//獲得構造器

????????????Constructor constructor?=?clazz.getDeclaredConstructor(args);

????????????constructor.setAccessible(true);

????????????//返回該類的實例

????????????return?constructor.newInstance(parameter);

????????} catch (Exception e) {

????????????e.printStackTrace();

????????}

????????if?(isThrowable) {

????????????throw new RuntimeException("invokeReflectConstruct error "?+?className?+?"?? parameter?? "?+?parameter);

????????}

????????return?null;

????}

很簡單,通過反射得到類的實例及方法,最終通過invoke代入?yún)?shù)執(zhí)行方法。這里很幸運,我們發(fā)現(xiàn)這個EnhancedRobustUtils 是Robust自帶的類,并不是動態(tài)加載的。
那hook就非常簡單了,我們只需要簡單的hook?invokeReflectMethod獲取Joseph的返回值,以及equals的參數(shù)即可。
完整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

Java.perform(function(){

//獲得EnhancedRobustUtils類的wapper

????????var robust?=?Java.use("com.meituan.robust.utils.EnhancedRobustUtils");

//hook invokeReflectMethod方法

????????robust.invokeReflectMethod.implementation?=?function(v1,v2,v3,v4,v5){

????????//不破壞原來的邏輯,只在原來的邏輯中打印出Joseph,equals的值

????????????var result?=?this.invokeReflectMethod(v1,v2,v3,v4,v5);

????????????if(v1=="Joseph"){

????????????????console.log("functionName:"+v1);

????????????????console.log("functionArg3:"+v3);

????????????????console.log("functionArg4:"+v4);

????????????????send(v4);

????????????????console.log("return:"+result);

????????????????console.log("-----------------------------------------------------")

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

?

????????????else?if(v1=="equals"){

????????????????console.log("functionName:"+v1);

????????????????console.log("functionArg3:"+v3);

????????????????console.log("functionArg4:"+v4);

????????????????send(v4);

????????????????console.log("return:"+result);

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

????????????return?result;

????????}

});

0x02 執(zhí)行py腳本獲取結果

1

2

3

4

5

1.?打開模擬器,adb shell進入終端并啟動frida。

2.?開啟端口轉發(fā)。

???adb forward tcp:27043?tcp:27043

???adb forward tcp:27043?tcp:27043

3.?啟動應用后,執(zhí)行Python腳本。

?

最終獲得結果如下:

總結

上篇我們主要講了robust的原理,并采用第一種方法Robust自帶的EnhancedRobustUtils工具類來hook,得到我們想要的答案,從做題的角度來說是一種很好很快的辦法,但是從學習的角度,可能這道題用hook DexClassLoader的方式更有趣味和意義,下篇我會詳細介紹怎么hook DexClassLoader動態(tài)加載的類及方法,來獲得最終答案:D

?

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

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的进阶Frida--Android逆向之动态加载dex Hook(三)的全部內容,希望文章能夠幫你解決所遇到的問題。

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