[TOC]

这篇文章主要介绍几个简单的例子,来逆向分析so,当然有些破解可以在smali里直接修改,但是这里均采用逆向so的方式。

0x01 ARM—HEX转换

1.将ARM指令转换为Hex时,可以使用工具Arm_Asm,如下图:

2.可以根据ARM参考手册,手动转换。比如add的指令格式如下:

当执行add r0,#1时,Rdn为0,imm8为1,所以二进制为00110000 00000001,转换为16进制就是3001,所以Hex code就是0130

3.百度文库上有人上传了一份整理好的thumb指令集,如果不想到ARM参考手册上找,可以看看这个。

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

vis immed_value
n isRn
mis Rm
s isRs
ris register_list
c iscondition      
表一:按指令字母升序排列
01000001 01mmmddd --ADC Rd,Rm
0001 110vvvnn nddd-- ADDRd,Rn,#immed_3
00110ddd vvvvvvvv --ADD Rd,#immed_8
0001 100mmmnn nddd-- ADDRd,Rn,Rm
01000100 hhmmmddd --ADD Rd,Rm  h1h2,h1is msbfor Rd,h2is msbfor Rm
1010 0dddvvvv vvvv-- ADDRd,PC,#immed_8*4
10101ddd vvvvvvvv --ADD Rd,SP,#immed_8*4
1011 00000vvv vvvv-- ADDSP,#immed_7*4
01000000 00mmmddd --AND Rd,Rm
0001 0vvvvvmm mddd-- ASRRd,Rm,#immed_5
01000001 00sssddd --ASR Rd,Rs
1101 ccccvvvv vvvv-- Bccsigned_immed_8
11100vvv vvvvvvvv --B   signed_immed_11
0100 001110mm mddd-- BICRd,Rm
10111110 vvvvvvvv --BKPT immed_8
111h hvvvvvvv vvvv-- BL(X)immed_11
01000111 1hmmmSBZ --BLX Rm
0100 01110Hmm mSBZ-- BXRm
01000010 11mmmnnn --CMN Rn,Rm
0010 1nnnvvvv vvvv-- CMPRn,#immed_8
01000010 10mmmnnn --CMP Rn,Rm
0100 0101hhmm mnnn-- CMPRn,Rm
01000000 01mmmddd --EOR Rd,Rm
1100 1nnnrrrr rrrr-- LDMIARn!,reg_list
01101vvv vvnnnddd --LDR Rd,[Rn+#immed_5*4]
0101 100mmmnn nddd-- LDRRd,[Rn,Rm]
01001ddd vvvvvvvv --LDR Rd,[PC+#immed_5*4]
1001 1dddvvvv vvvv-- LDRRd,[SP,#immed_8*4]
01111vvv vvnnnmmm --LDRB Rd,[Rn,#immed_5*4]
0101 110mmmnn nddd-- LDRVRd,[Rn,Rm]
10001vvv vvnnnddd --LDRH Rd,[Rn,#immed_5*2]
0101 101mmmnn nddd-- LDRHRd,[Rn,Rm]
0101011m mmnnnddd --LDRSB Rd,[Rn,Rm]
0101 111mmmnn nddd-- LDRSHRd,[Rn,Rm]
00000vvv vvmmmnnn --LSL Rd,Rm,#immed_5
0100 000010ss sddd-- LSLRd,Rs
00001vvv vvmmmddd --LSR Rd,Rm,#immed_5
0100 000011ss sddd-- LSRRd,Rs
00100ddd vvvvvvvv --MOV Rd,#immed_8
0001 110000nn nddd-- MOVRd,Rn
01000110 hhmmmddd --MOV Rd,Rm
0100 001101mm mddd-- MULRd,Rm
01000011 11mmmddd --MVN Rd,Rm
0100 001001mm mddd-- NEGRd,Rm
01000011 00mmmddd --ORR Rd,Rm
1011 110Rrrrr rrrr-- POPreg_list
1011010R rrrrrrrr --PUSH reg_list
0100 000111ss sddd-- RORRd,Rs
01000001 10mmmddd --SBC Rd,Rm
1100 0nnnrrrr rrrr-- STMIARn!,reg_list
01100vvv vvnnnddd --STR Rd,[Rn,#immed_5*4]
0101 000mmmnn nddd-- STRRd,[Rn,Rm]
10010ddd vvvvvvvv --STR Rd,[SP,#immed_8*4]
0111 0vvvvvnn nddd-- STRBRd,[Rn,#immed_5]
0101010m mmnnnddd --STRB Rd,[Rn,Rm]
1000 0vvvvvnn nddd-- STRHRd,[Rn,#immed_5*2]
0101001m mmnnnddd --STRH Rd,[Rn,Rm]
0001 111vvvnn nddd-- SUBRd,Rn,#immed_3
00111ddd vvvvvvvv --SUB Rd,#immed_8
0001 101mmmnn nddd-- SUBRd,Rn,Rm
10110000 1vvvvvvv --SUB Sp,#immed_7*4
1101 1111vvvv vvvv-- SWIimmed_8
01000010 00mmmnnn --TST Rn,Rm
表二:按指令机器码升序排列
0000 0vvvvvmm mnnn-- LSLRd,Rm,#immed_5
00001vvv vvmmmddd --LSR Rd,Rm,#immed_5
0001 0vvvvvmm mddd-- ASRRd,Rm,#immed_5
0001100m mmnnnddd --ADD Rd,Rn,Rm
0001 101mmmnn nddd-- SUBRd,Rn,Rm
0001110v vvnnnddd --ADD Rd,Rn,#immed_3
0001 111vvvnn nddd-- SUBRd,Rn,#immed_3
00011100 00nnnddd --MOV Rd,Rn
0010 0dddvvvv vvvv-- MOVRd,#immed_8
00101nnn vvvvvvvv --CMP Rn,#immed_8
0011 0dddvvvv vvvv-- ADDRd,#immed_8
00111ddd vvvvvvvv --SUB Rd,#immed_8
0100 000000mm mddd-- ANDRd,Rm
01000000 01mmmddd --EOR Rd,Rm
0100 000010ss sddd-- LSLRd,Rs
01000000 11sssddd --LSR Rd,Rs
0100 000100ss sddd-- ASRRd,Rs
01000001 01mmmddd --ADC Rd,Rm
0100 000110mm mddd-- SBCRd,Rm
01000001 11sssddd --ROR Rd,Rs
0100 001000mm mnnn-- TSTRn,Rm
01000010 01mmmddd --NEG Rd,Rm
0100 001100mm mddd-- ORRRd,Rm
01000010 10mmmnnn --CMP Rn,Rm
0100 001011mm mnnn-- CMNRn,Rm
01000011 01mmmddd --MUL Rd,Rm
0100 001110mm mddd-- BICRd,Rm
01000011 11mmmddd --MVN Rd,Rm
0100 0100hhmm mddd-- ADDRd,Rm  h1h2,h1is msbfor Rd,h2is msbfor Rm
01000101 hhmmmnnn --CMP Rn,Rm
0100 0110hhmm mddd-- MOVRd,Rm
01000111 0HmmmSBZ --BX Rm
0100 01111hmm mSBZ-- BLXRm
01001ddd vvvvvvvv --LDR Rd,[PC+#immed_5*4]
0101 000mmmnn nddd-- STRRd,[Rn,Rm]
0101001m mmnnnddd --STRH Rd,[Rn,Rm]
0101 010mmmnn nddd-- STRBRd,[Rn,Rm]
0101011m mmnnnddd --LDRSB Rd,[Rn,Rm]
0101 100mmmnn nddd-- LDRRd,[Rn,Rm]
0101101m mmnnnddd --LDRH Rd,[Rn,Rm]
0101 110mmmnn nddd-- LDRVRd,[Rn,Rm]
0101111m mmnnnddd --LDRSH Rd,[Rn,Rm]
0110 0vvvvvnn nddd-- STRRd,[Rn,#immed_5*4]
01101vvv vvnnnddd --LDR Rd,[Rn+#immed_5*4]
0111 1vvvvvnn nmmm-- LDRBRd,[Rn,#immed_5*4]
01110vvv vvnnnddd --STRB Rd,[Rn,#immed_5]
1000 0vvvvvnn nddd-- STRHRd,[Rn,#immed_5*2]
10001vvv vvnnnddd --LDRH Rd,[Rn,#immed_5*2]
1001 0dddvvvv vvvv-- STRRd,[SP,#immed_8*4]
10011ddd vvvvvvvv --LDR Rd,[SP,#immed_8*4]
1010 0dddvvvv vvvv-- ADDRd,PC,#immed_8*4
10101ddd vvvvvvvv --ADD Rd,SP,#immed_8*4
1011 00000vvv vvvv-- ADDSP,#immed_7*4
10110000 1vvvvvvv --SUB Sp,#immed_7*4
1011 010Rrrrr rrrr-- PUSHreg_list
1011110R rrrrrrrr --POP reg_list
1011 1110vvvv vvvv-- BKPTimmed_8
11000nnn rrrrrrrr --STMIA Rn!,reg_list
1100 1nnnrrrr rrrr-- LDMIARn!,reg_list
1101cccc vvvv vvvv-- Bccsigned_immed_8
1101 1111vvvv vvvv-- SWIimmed_8
11100vvv vvvvvvvv --B   signed_immed_11
111h hvvvvvvv vvvv-- BL(X)immed_11

0x02 示例


先对上篇文章中的switch分支进行逆向。

getSwitch()的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

JNIEXPORTjint JNICALL Java_com_example_hellojni_GetSwitch_getSwitch
  (JNIEnv*,jclass,jint a,jint b,jint i){
    switch(i){
        case1:
            returna +b;
            break;
        case2:
            returna -b;
            break;
        case3:
            returna *b;
            break;
        case4:
            returna /b;
            break;
        default:
            returna +b;
            break;
        }
}

1.逆向APK后,使用IDA打开libs目录下的libHelloJNI.so,alt+t找到GetSwitch函数

2.当我们传入的参数是4,2,2是,执行的是a – b,所以返回的结果是2

3.现在,我们将a – b改为a + b,就是要返回结果为6,找到i=2的减法分支,计算add ro,r2,r3的hex为D018

4.然后切换到Hex View,修改Hex为D018,再回到图形模式时,可以看到SUBS R0,R2,R3已经更改为ADDS R0,R2,R3了

5.使用010editor,ctrl+g跳转到104A的地址,修改Hex为D018,然后保存。

6.重新编译成APK,运行时,发现结果已经变为6了。

0x03 内购破解


1.正常情况下,购买金币时,是需要付费的,因为使用的是模拟器,所以显示付费失败。

2.从smali中找到相应的so,IDA打开。找到函数GameCoin,先F5看一下,看到关键点GameMain::GameCoin = v3 + v1,就是当前游戏的金币数量,对应的汇编为:ADDS R0,R0,R6

3.我们知道R0是返回值,R6是计算出的当前金币数,所以我们把此处改为ADDS R0,#255,所以每次都会增加255个金币,Hex为FF30

4.修改后,初始金币为10,点击一次后,增加了255个。

0x04 加密解密


这篇文章是从吾爱破解(http://www.52pojie.cn/thread-396463-1-1.html)看到的,作者是adslxyz。

准备工具:

  1. IntelliJ IDEA
  2. baksmali
  3. AndroidKiller
  4. IDA6.6
  5. Fiddler
  6. 一台Android真机(或者模拟器)
  7. eclipse

手机连上wifi,与电脑处于同一局域网,然后设置好Fiddler,具体步骤不详述。打开宝宝树孕育6.2.1版本,进入登陆窗口,输入账号密码点登陆,在电脑查看封包。可以得到请求URL:http://www.babytree.com/api/muser/login

1
POST:android_id=d77c45cb6182db0e&build_serial=mmmm&local_ts=1438682239&source_channel=pc_pregnancy_tongyong_app_140729&longitude=122.535462&latitude=35.582332&phone_number=buROv8rMh50AdCj7mYdAZwWv9YEpsIR2mClGrO43QK6E5GHko7aEZwtZMT%2F2nvyuUCYsFtx%2Bkz1kwpthbRlpZecGXjkbyaaeIE4ocYjolDyLXLduydRCjnG%2Bk9OEfBr3QCShaiJ2Z%2BeDQIeqgTssdlzZqzFfpHzsg3oWpdF3RbMLF3O93RzGWntJgkgvzjzK7BfwvRF8p8o6Se2l7mX7malpYrn35H2kh4mEAaLaBGoj2naCxcSXCn6X6w9za6hofkhogCtB7PpY6pulzY%2BOhejwG1jdoCpqRxcctRcsFPWNz72SD8Mmqkq6wRVeLYX5ZIoWNafwEJuPobuRtmvhQzg%3D%3D&version=6.2.1&imei=90000451032112&secret=b8a310e750c8af5187197c279a7e0241&email=zLkZURsmEpT8fGCGGNADu2KAZ79zkZI3b%2FMBDh5i1vCAbvFmKoGu7L4nBPsOvnzzsnWtuOJipiITnoRQ5w%2FNTy%2FvEx%2BX%2FO8Ff8jcU2qBAXTzGgd0x1lZ8PsGuan0944ax5n1douoaTC370Hv4A%2F58IhP8rhcQWnO4FzFol8FC%2FXQ%2Fd1%2FoAQOycwdLeBUm0JJ5tRKiaT2nBwD1z7Dn1CShswWCNN2zDCvYgtJ7Ig3JIQipUahL9OaR%2FQnzUzk%2BmaC%2Fm6iRr1q4wkiz9lexJkXFHf1g5VuFqshCNgxdgeDw%2BZHuagedfj5MakZD96Lj%2BtZtRf6GhaLbY29tSL65EdSgsg%3D%3D&password=AhOxbuypr6JYDgpP%2Fw%2BLhVlfzwusSMOaE0137YBOvOAZkk%2BbCHwzfduzqsjasx6zrhZUkZv3aoZ27HDIFAebdgsRS9lh2eeuxXiIxZdkaVPy47UNMdLHo1fFnuUEOCbE144H%2FL%2B2EeY%2BjbE8Ebjg0gXuZ7ziNZ%2Bmrgt4%2FrN8YbizcbyCx7A%2Fue3GD3p58GV3ztnlxWp9d4D1IlMT5V%2B8qxi3VDpQMr0Ghi4MaDTkH%2FV8fcRSCBqutHTDsEs%2F5AfBgksHFT6Dcxl3tpBIIfdgEmU4EZ%2BpqLqVxVFLdjPA0MJOhzE36P8NqTPiKEXzTuJcmaaS5qU34d2dHGYP2wc7Dg%3D%3D&client_type=android&app_id=pregnancy&mac=5c%3A51%5A57%3A55%3A26%3A4d&client_baby_status=1&bpreg_brithday=2100-01-01

分析可知:phone_number,password,email,secret四个参数为加密,其他参数均为固定或者地理位置信息等参数。所以此次目标就为这4个参数

  1. 先把apk拖入AndroidKiller进行分析,可知无壳。
  2. 根据先前分析,可在AndroidKiller中搜索相应字符串查找我们感兴趣的函数搜索muser\login,可知登陆的函数在com.babytree.platform.api.muser中的login.smali中。由图可知Login.java的父类是ApiBase,可以猜想,所有Api的请求所公共的函数都会在此类中生成。关键地方找一个就行,本次分析主要还是以动态调试为主.
  3. 用baksmali将apk反编译成.smali文件
  4. 然后打开IntelliJ IDEA,创建一个java工程.将上一步反编译出的smali文件导入到src文件夹中。如图:

  5. 然后打开eclipse,切换到DDMS视图,选中手机,查看其端口,如图:

    图上可以看到,目标应用的端口为8631.

  6. 切换到IntelliJ IDEA,选择Run-Configurations.
  7. 然后新建一个Remote,填写端口为8631,然后选择source using module’s classpath为我们之前创建的项目,点击OK
  8. 接下来在IntelliJ IDEA中打开Login.smali,快速浏览一下,选择一个感兴趣的地方下断点,这里我选择.method protected a()Ljava/lang/String;在函数起始处下断点后,在手机中输入账号密码,点击登陆,可以看到断点成功断下,如图所示:

    从图中可以看到,左下角为函数的调用堆栈,从 登陆按钮 按下(OnClick),到api.user.login整个的调用过程。展开右下角的p0,可以看到此时已经生成了3个参数,

    说明在断点之前,参数已经生成好了。所以我们按照函数的调用栈,挨个查看调用过的函数。查阅一番后可以发现。Login对象是在LoginActivity中的.method public a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V方法创建的。在new-instance v0, Lcom/babytree/platform/api/muser/Login;处下断点,重新点击登陆按钮,如图:

    此时v1.v2.v3寄存器分别出现了加密的结果,由上可知,分别对应phone_number,password,email三个参数。

    所以加密的函数应该就在上面。往上下断点,重新来一次观察一下加密的过程。

    可知:

    1
    iget-objectv0,p0,Lcom/babytree/apps/pregnancy/activity/LoginActivity;->u:Ljava/lang/String;

    这一句函数调用之后,v0是一串固定字符串。

    1
    invoke-static{p1,v0},Lcom/babytree/apps/pregnancy/h/a;->a(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

    调用之后,v1为加密结果。查看a函数:

    为RSA加密,所以之前的v0就是RSA的pubKey了。在a函数中下断点,看一看加密的参数是什么:

    一切尽收眼底。由此可知phone_number,password,email三个参数的算法。

  9. 接下来找secret的算法。

    回到AndroidKiller搜索关键字,定位到com/babytree/platform/api/ApiCommonParams;大致看一下,这个是API通用参数生成的类。且加载了一个so:api_encrypt.so在secret关键字附近看一下,调用了so的invoke-static {v1, v2}, Lcom/babytree/platform/api/ApiCommonParams;->nativeGetParam(Landroid/content/Context;Ljava/util/List;)Ljava/lang/String;方法。

    我们先在这句上面下个断点,看看传给so函数的是什么参数。断点断下后,看到传递给so的参数是一个List,POST参数中除secret之外的所有参数。参数知道了,调用点知道了。分析一下so,看看需不需要动态调试。载入ida,找到调用的nativeGetParam函数。大致看一下,发现又调用了java的md5函数,好了,调试so的步骤剩下了。

    回到IntelliJ IDEA,在getMD5Str处下断点,继续调试:

    很快我们可以知道,p0中就是进行md5的参数啦。

    分析一下可知,secret就是 MD5(时间戳+所有参数排序+&asdf12341dfas!@#$%()(Ujjlasdflasdfj;asjdf23412313kljajsdflasjdflasjdflajsdf;lajsdf2342234sdfsdfffds)

    总结:

    动态调试apk的方式让我们了解apk运作的过程,通过动静结合的分析方式,快速找到想要的东西~

    0x05 签名校验


    将关键代码放到so中,在底层获取签名信息并验证。因为获取和验证的方法都封闭在更安全的so库里面,能够起到一定意义上的保护作用。实例如下:

    1. 在程序中搜索getSignature,发现并没有调用此函数的地方,猜测在so文件中,搜索loadLibrary。

    2. 在代码中可以查找,可以找到调用的是signaturex.so
    3. 使用IDA打开signaturex.so,然后搜索getSiganture,找到调用此函数的地方。从代码中可以看到,此函数调用了org.cocos2dx.cpp.AppActivity.getSignature

    4. 查看F5代码,发现此函数是判断签名的函数,然后我们双击此函数的调用者,部分代码如下。

    5. 从上图可以看出,只需要修改BEQ loc_11F754,让其不跳转到jjni——>error,就可以绕过签名校验。 查看HEX,使用010editor跳到0011F73E,修改D0为D1。成功绕过签名校验。

    转载自:http://drops.wooyun.org/mobile/10010    原文作者:3xpl0it

Android SO逆向2-实例分析相关推荐

  1. Android中list常用方法,Android中ListActivity用法实例分析

    本文实例分析了Android中ListActivity用法.分享给大家供大家参考,具体如下: 程序如下: import android.app.ListActivity; import android ...

  2. android中view用法,Android中ImageView用法实例分析

    本文实例分析了Android中ImageView用法.分享给大家供大家参考,具体如下: 猜牌游戏大家可能以前都玩过,这里我们用这个小游戏来说明ImageView的用法. 首先,在res/drawabl ...

  3. android listview 分析,Android中ListView用法实例分析

    本文实例分析了Android中ListView用法.分享给大家供大家参考,具体如下: 通过在Layout中添加ListView Widget可以达到在页面布局具有列表效果的交互页面.在这里通过举例来说 ...

  4. android 刷新view位置,Android View刷新机制实例分析

    本文实例讲述了Android View刷新机制.分享给大家供大家参考,具体如下: 一.总体说明 在Android的布局体系中,父View负责刷新.布局显示子View:而当子View需要刷新时,则是通知 ...

  5. Android/Service详解/实例分析

    一. Service简介 Service是android 系统中的四大组件之一(Activity.Service.BroadcastReceiver.ContentProvider),它跟Activi ...

  6. android + javascript 相互通信实例分析

    1.  AndroidManifest.xml中必须使用许可 "android.permission.INTERNET", 否则会出Web page not available错误 ...

  7. Android的事件分发实例分析

    如果对Android的事件分发不熟悉,可以看Android的事件分发 瀑布流 实现的功能:滑动左边的RecyclerView区域,左边的RecyclerView滚动:滑动中间的RecyclerView ...

  8. Android Touch事件原理加实例分析

    Android中有各种各样的事件,以响应用户的操作.这些事件可以分为按键事件和触屏事件.而Touch事件是触屏事件的基础事件,在进行Android开发时经常会用到,所以非常有必要深入理解它的原理机制. ...

  9. Android开发中StackOverflowError错误实例分析

    http://blog.csdn.net/mozhizun/article/details/7051300 http://blog.csdn.net/gaomatrix/article/details ...

最新文章

  1. LINQ via C# 系列文章
  2. db2关闭下一句sql的日志_DB2_数据库日志管理
  3. 虚拟机中访问连接在物理机上的摄像机(使用桥接)
  4. 使用JCache缓存方法结果
  5. 快来一起玩转LiteOS组件:RHas
  6. IPhone之NSXMLParser的使用
  7. 拼多多公布新iPhone SE补贴后价格:2899元起;BOSS直聘回应“App崩了”;Chrome 新测试版发布|极客头条...
  8. 【论文写作】网上选课系统中数据库设计模块如何写
  9. 如何在电脑中使用python_教你怎么在windows上用python获得CPU信息
  10. RetinaFace论文解读 --- RetinaFace: Single-stage Dense Face Localisation in the Wild
  11. 红帽 Linux Redhat6.4安装MySQL 5.1
  12. 晒晒公司发的年货,一家比一家实在,打工人:发了个通知
  13. 关闭jupyter notebook报错
  14. 5G QoS控制原理专题详解-SM策略数据的源头
  15. 计算机科学与技术毕业论文格式,计算机科学与技术毕业论文格式示例.docx
  16. 信息安全-防火墙技术原理与应用
  17. 5G标准中常见缩略词总结(26个英文字母)
  18. H3C防火墙与华为交换机链路聚合配置方法
  19. 于推行结构化电子病历,促进卫生行业“四化”管理及医疗信息互联互通的建议...
  20. 2022年年终总结---新方向,新期待

热门文章

  1. HDU 2897 邂逅明下(简单博弈)
  2. ifconfig详解及设置静态IP的方法
  3. 新的信息论诞生前的若干问题分析
  4. Python列表排序 reverse、sort、sorted 操作方法详解
  5. groovy java_Java 、Groovy、 Scala 的未来会怎样?
  6. 科大星云诗社动态20210311
  7. centos 系统重新安装ssh
  8. 动态服务器值 回放报错 没有关联到_性能测试每天两个知识点-web性能脚本回放不成功的解决方法...
  9. 删除数据库中所有存储过程和函数的sql语句
  10. 高级C语言教程-关键字和运算符