原文 :https://lichao890427.github.io/wiki/android%20reverse%20engineering/php
Android程序的特色相比在于使用混淆方式打包,将包名、类名、函数名改为不易看懂的字母,从而使生成的apk小不少(android studio提供了release编译方式,使用proguard混淆),所以反编译apk最多的工做在于重构这些名称,这一点和pc上一致,对于android native程序(jni)则和pc上基本一致,不一样之处在于常见的是arm汇编。html
对于apk的反编译,因为资源和xml都进行了编码,所以反编译时必然要解析相应的resource.arsc/AndroidManifest.xml等文件,对于作过保护处理的apk一般会在这里作手脚干扰Apktool、dex2jar等反编译工具所以颇有必要掌握编译、调试这些工具源码的方法(见“如何编译、调试apktool和dex2jar”)java
轻量级反编译,反编译jar/class等java字节码文件(能力通常),提供简单的搜索能力python
dex2jar是一个工具包,反编译dex和jar,还提供了一些其它的功能,每一个功能使用一个bat批处理或 sh 脚原本包装,只需在Windows 系统中调用 bat文件、在Linux 系统中调用 sh 脚本便可。在bat中调用相应的jar主类完成特定功能,例如d2j-dex2jar.bat中的内容是:@"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.Dex2jarCmd %*
。经常使用的有dex2jar jar2dex dex2smali smali2dex
linux
Android package,android安装程序文件,本质上是压缩包,解压获得classes.dex、resources.arsc、AndroidManifest.xml、so文件以及资源文件android
aapt d xmltree 1.apk AndroidManifest.xml
N: android=http://schemas.android.com/apk/res/android
E: manifest (line=2)
A: android:versionCode(0x0101021b)=(type 0x10)0x1
A: android:versionName(0x0101021c)="1.0" (Raw: "1.0")
A: package="com.ibotpeaches.issue767" (Raw: "com.ibotpeaches.issue767")
A: platformBuildVersionCode=(type 0x10)0x17 (Raw: "23")
A: platformBuildVersionName="6.0-2438415" (Raw: "6.0-2438415")
E: uses-sdk (line=0)
A: android:minSdkVersion(0x0101020c)=(type 0x10)0x16
A: android:targetSdkVersion(0x01010270)=(type 0x10)0x17
E: application (line=3)
A: android:theme(0x01010000)=@0x7f090083
A: android:label(0x01010001)=@0x7f060015
A: android:icon(0x01010002)=@0x7f030000
A: android:debuggable(0x0101000f)=(type 0x12)0xffffffff
A: android:allowBackup(0x01010280)=(type 0x12)0xffffffff
A: android:supportsRtl(0x010103af)=(type 0x12)0xffffffff
E: activity (line=4)
A: android:theme(0x01010000)=@0x7f090030
A: android:label(0x01010001)=@0x7f060015
A: android:name(0x01010003)="com.ibotpeaches.issue767.MainActivity" (Raw
: "com.ibotpeaches.issue767.MainActivity")
E: intent-filter (line=5)
E: action (line=6)
A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "andr
oid.intent.action.MAIN")
E: category (line=7)
A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw:
"android.intent.category.LAUNCHER")
E: meta-data (line=10)
A: android:name(0x01010003)="large.int.value" (Raw: "large.int.value")
A: android:value(0x01010024)="9999999999999999999999" (Raw: "99999999999
99999999999")
查看xml => aapt d xmltree 1.apk AndroidManifest.xml
查看resource => aapt d resources 1.apk (resource.arsc)ios
Dalvik Executable,Dalvik可执行文件,从java class文件转换而来的字节码,Classes.Dex经过dex2jar转换成java字节码(有损),或者dex2smali转换成darvik汇编(无损)——smali字节码,其形式以下 c++
Java Archive,java归档文件,能够直接解压获得class文件git
dex转odex:/system/bin/dexopt
dexopt-wrapper 1.apk 1.odexgithub
Android归档文件,压缩包格式,包含
Linux动态连接库文件,包含arm64 arm mips mips64 x86 x86-64几个平台
设备通讯、调试工具,经常使用法:
adb devices 列出当前设备
adb –s d24eb3ab [命令] 指定设备执行命令
adb push 源 目标 非root机器能够设置路径为/data/local/tmp
adb pull 源 目标
adb shell 执行终端
adb logcat 查看日志(/system/logcat为服务器)
adb jdwp 查看远程jdwp进程
adb forward tcp:主机端口 tcp:远程端口 把主机端口消息转发手机端口(端口对应进程) 用于ida调试
adb forward tcp:主机端口 jdwp:远程进程ID 把主机端口消息转发手机jdwp进程 用于jdb调试
adb install [apkpath] 安装apk
adb uninstall [packagename] 卸载apk 注意会完全清理,删除/data/app下的备份apk
adb remount 将/system从新映射为读写,以便进行系统区文件操做
adb root 使adb以root方式启动,便于push/pull/remount
APK资源管理工具,用于增删查改APK中的文件、资源等,对于分析编译后的Resource.arsc, AndroidManifest.xml格式较有价值,一般也能够用winrar对apk/jar进行解压
打印xml树 aapt d xmltree 1.apk AndroidManifest.xml
打印资源 aapt d resources 1.apk
添加文件 aapt a 1.apk AndroidManifest.xml
删除文件 aapt r 1.apk AndroidManifest.xml
Android远程命令,am执行调试、运行功能,pm执行安装、卸载功能
am start -D -n "b.myapp/b.myapp.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
am startservice -n com.android.music/com.android.music.MediaPlaybackService
am force-stop com.example.administrator.myapplication
am kill com.example.administrator.myapplication
am kill all
adb shell am broadcast -a com.android.test
pm install –r 1.apk
pm uninstall packagename
pm list package
pm list package com.qihoo
pm disable packagename
(禁用后,图标消失,对该应用的操做都无效)在android studio中能够采用运行调试或进程附加方式调试,支持条件断点、一次断点、对单线程下断,有6种断点:
TypeCh | TypEn | Description |
---|---|---|
行断点 | Java Line Breakpoints | 在(java/c)源码某行下断 |
Java类成员变量访问断点 | Java Field Watchpoints | 相似于内存访问断点,在读和写java类成员变量时断下 |
Java类方法断点 | Java Method Breakpoints | 在进入java层函数或退出函数时断下 |
Java异常断点 | Java Exception Breakpoints | 发生java层捕获或未捕获异常时断下 |
异常断点 | Exception Breakpoints | 抛异常或捕获异常时断下 |
符号断点 | Symbolic Breakpoints | (c/java)符号断点 |
应用市场有不少这种软件,须要Root权限。解决没有USB数据线的状况下的调试
C:\Users\Administrator>adb connect 192.168.0.103:5555
connected to 192.168.0.103:5555
此时能够用adt调试
不须要调试的通常过程 :使用反编译工具获得源代码,修改调试标识,修改机器码,最后回编译签名:
反编译apk:apktool d file.apk –o path
回编译apk:apktool b path –o file.apk
java -jar apktool.jar d -d input.apk -o out
,加上-d选项以后反编译出的文件后缀为.java,而不是.smali,每一个.java文件立马都伪形成了一个类,语句全都是“a=0;”这一句,smali语句成为注释,作这些都是为了后面欺骗idea、eclipse、android studio这些ide的apktool b –d path –o input.apk
java –jar signapk.jar testkey.x509.pem testkey.pk8 input.apk output.apk
点评:这种方式只能够用来分析加密很弱的App,前提是apktool能够成功反编译
jdb是一个支持java代码级调试的工具,它是由java jdk提供的,能够设置断点、查看堆栈、计算表达式、动态修改类字节码、调试&跟踪、修改变量值、线程操做,断点包括:(源码)行断点、符号断点、成员变量访问断点。每一个java程序(windows/ios/android)均可以用jdwp协议进行调试,Android Studio/Eclipse的调试也是创建在该协议基础之上,下面以实例说明:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.ok).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.b.com")); intent.setClassName("com.android.browser","com.android.browser.BrowserActivity"); startActivity(intent); } }); } }
adb shell am start -D -n "b.myapp/b.myapp.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
<1> main[1] where
[1] android.app.Activity.startActivity (Activity.java:3,490)
[2] b.myapp.MainActivity$1.onClick (MainActivity.java:21)
[3] android.view.View.performClick (View.java:4,084)
[4] android.view.View$PerformClick.run (View.java:16,966)
[5] android.os.Handler.handleCallback (Handler.java:615)
[6] android.os.Handler.dispatchMessage (Handler.java:92)
[7] android.os.Looper.loop (Looper.java:137)
[8] android.app.ActivityThread.main (ActivityThread.java:4,745)
[9] java.lang.reflect.Method.invokeNative (本机方法)
<1> main[1] print intent
intent = "Intent { act=android.intent.action.VIEW dat=http://www.b.com cmp=
com.android.browser/.BrowserActivity }"
<1> main[1] use D:\Android\sdk\sources\android-18 //参考设备android版本
<1> main[1] use D:\test\MyApplication\app\src\main\java
<1> main[1] list
3,421 * @hide Implement to provide correct calling token.
3,422 */
3,423 public void startActivityAsUser(Intent intent, UserHandle user) {
3,424 startActivityAsUser(intent, null, user);
3,425 => }
3,426
3,427 /**
3,428 * @hide Implement to provide correct calling token.
3,429 */
3,430 public void startActivityAsUser(Intent intent, Bundle options, User
Handle user) {
> use D:\test\MyApplication\app\src\main\java
stop at b.myapp.MainActivity:18
正在延迟断点b.myapp.MainActivity:18。
将在加载类后设置。
> resume
已恢复全部线程。
> 设置延迟的断点b.myapp.MainActivity:18
断点命中: "线程=<1> main", b.myapp.MainActivity.onCreate(), 行=18 bci=12
18 int j = 0;
只要链接到jdb就会致使app运行起来,此时若是想断在初始化这部分就没有办法了,不过jdb提供初始命令脚本
echo suspend > jdb.ini
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8601
此时,app仍然处于等调试器状态,而虫子已经变绿,此时能够下断点,而后resume恢复全部线程 附加后会变绿色虫子
> > stop in b.myapp.MainActivity.onCreate(android.os.Bundle)
正在延迟断点b.myapp.MainActivity.onCreate(android.os.Bundle)。
将在加载类后设置。
>resume
已恢复全部线程。
断点命中: "线程=<1> main", b.myapp.MainActivity.onCreate(), 行=13 bci=0
<1> main[1] where
[1] b.myapp.MainActivity.onCreate (MainActivity.java:13)
[2] android.app.Activity.performCreate (Activity.java:5,372)
[3] android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1,1
04)
[4] android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2,25
8)
[5] android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2,350
)
[6] android.app.ActivityThread.access$700 (ActivityThread.java:160)
[7] android.app.ActivityThread$H.handleMessage (ActivityThread.java:1,317)
[8] android.os.Handler.dispatchMessage (Handler.java:99)
[9] android.os.Looper.loop (Looper.java:137)
[10] android.app.ActivityThread.main (ActivityThread.java:5,454)
stop in:断点
step:步入(源码行)
stepi:单入(指令)
step up:执行到返回
cont:恢复运行
next:步过
输出表达式:print/eval
jdb最大缺点在于难用,因此有人用python封装了一次,工具名AndBug
Usage: gdbserver [OPTIONS] COMM PROG [ARGS ...]
gdbserver [OPTIONS] --attach COMM PID
gdbserver [OPTIONS] --multi COMM
隐藏用法:gdbserver [OPTIONS] +SOCKETFILE --attach PID 会在本地创建socket文件通讯
Options:
--debug Enable general debugging output.
--remote-debug Enable remote protocol debugging output.
--version Display version information and exit.
--wrapper WRAPPER -- Run WRAPPER to start new programs.
--once Exit after the first connection has closed.
使用方式:
启动模式远程调试:gdbserver --debug --remote-debug :23946 /system/test.out [参数]
附加模式远程:gdbserver –debug –remote-debug –attach :23946 1234
Adb forward tcp:23946 tcp:23946 转发端口
IDA中选择Remote GDB Debugger附加便可
usage: strace [-CdDffhiqrtttTvVxx] [-a column] [-e expr] ... [-o file]
[-p pid] ... [-s strsize] [-u username] [-E var=val] ...
[command [arg ...]]
or: strace -c [-D] [-e expr] ... [-O overhead] [-S sortby] [-E var=val] ...
[command [arg ...]]
-c --统计每一系统调用的所执行的时间,次数和出错的次数等.
-C -- like -c but also print regular output while processes are running
-f --跟踪由fork调用所产生的子进程.
-F --尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-i --输出系统调用的入口指针
-q --禁止输出关于脱离的消息
-r --打印出相对时间关于,,每个系统调用
-T --显示每一调用所耗的时间
-v --输出全部的系统调用.一些调用关于环境变量,状态,输入输出等调用因为使用频繁,默认不输出
-x --以十六进制形式输出非标准字符串
-a设置返回值的输出位置.默认 为40.
-e expr -指定一个表达式,用来控制如何跟踪.: option=[!]all or option=[!]val1[,val2]...
options: trace, abbrev, verbose, raw, signal, read, or write
-o file --将strace的输出写入文件filename
-O overhead -- set overhead for tracing syscalls to OVERHEAD usecs
-p pid --跟踪指定的进程pid.
-D -- run tracer process as a detached grandchild, not as parent
-s strsize --指定输出的字符串的最大长度.默认为32.文件名一直所有输出
-S sortby -- sort syscall counts by: time, calls, name, nothing (default time)
-u username --以username 的UID和GID执行被跟踪的命令
-E var=val -- put var=val in the environment for command
-E var -- remove var from the environment for command
使用方式:
Strace –f ProcessA 启动跟踪
Strace –f –p 234 附加跟踪
-e trace=file -e trace=process -e trace=network
该程序是一个shell脚本,执行过程以下:
adb shell am start -D -n com.example.hellojni/.HelloJni 启动app并等待调试器
ps | grep hellojni 获得PID 3569
adb shell run-as com.example.hellojni /data/data/com.example.hellojni/lib/gdbserver +debug-socket --attach (3569)PID
将PID与文件映射创建调试连接(c层)
adb forward tcp:5039 localfilesystem:/data/data/com.example.hellojni/debug-socket将调试连接和本地端口创建连接(c层)
adb forward tcp:65534 jdwp:(3569)PID 将本地端口和进程创建链接(java层)
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=65534 使用jdb调试java层
arm-linux-androideabi-gdb.exe
target remote :5039 使用gdb调试c层
set breakpoint pending on
(使用前关掉腾讯的AndroidServer.exe,不然连不上!!!),在工程目录下(有AndroidManifest.xml),命令行运行%NDK_ROOT%\ndk-gdb-py.cmd --start --verbose
,输出下面字符即为成功:
Android NDK installation path: D:/Android/AndroidNDK/android-ndk-r10e
ADB version found: Android Debug Bridge version 1.0.32
Using ADB flags:
Using auto-detected project path: .
Found package name: com.example.hellojni
ABIs targetted by application: arm64-v8a armeabi armeabi-v7a armeabi-v7a-hard mips mips64 x86 x86_64
Device API Level: 19
Device CPU ABIs: armeabi-v7a armeabi
Compatible device ABI: armeabi-v7a
Using gdb setup init: ./libs/armeabi-v7a/gdb.setup
Using toolchain prefix: D:/Android/AndroidNDK/android-ndk-r10e/toolchains/arm-linux-androideabi-4.8/prebuilt/windows/bin/arm-linux-androideabi
Using app out directory: ./obj/local/armeabi-v7a
Found debuggable flag: true
Found device gdbserver: /data/data/com.example.hellojni/lib/gdbserver
Found data directory: '/data/data/com.example.hellojni'
Found first launchable activity: .HelloJni
Launching activity: com.example.hellojni/.HelloJni
## COMMAND: adb_cmd shell am start -D -n com.example.hellojni/.HelloJni
## COMMAND: adb_cmd shell sleep 2.000000
Found running PID: 9139
## COMMAND: adb_cmd shell run-as com.example.hellojni /data/data/com.example.hellojni/lib/gdbserver --attach +debug-socket 9139 [BACKGROUND]
Launched gdbserver succesfully.
Setup network redirection
## COMMAND: adb_cmd forward tcp:5039 localfilesystem:/data/data/com.example.hellojni/debug-socket
Attached; pid = 9139
Listening on Unix socket debug-socket
## COMMAND: adb_cmd pull /system/bin/app_process ./obj/local/armeabi-v7a/app_process
79 KB/s (9488 bytes in 0.117s)
Pulled app_process from device/emulator.
## COMMAND: adb_cmd pull /system/bin/linker ./obj/local/armeabi-v7a/linker
585 KB/s (63596 bytes in 0.106s)
Pulled linker from device/emulator.
## COMMAND: adb_cmd pull /system/lib/libc.so ./obj/local/armeabi-v7a/libc.so
1184 KB/s (310584 bytes in 0.256s)
Pulled /system/lib/libc.so from device/emulator.
Set up JDB connection, using jdb command: C:\Program Files\Java\jdk1.8.0_66\bin\jdb.exe
## COMMAND: adb_cmd forward tcp:65534 jdwp:9139
--------------------./obj/local/armeabi-v7a/gdb.setup---------------
GNU gdb (GDB) 7.7
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i586-pc-mingw32msvc --target=arm-linux-android".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://source.android.com/source/report-bugs.html>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
Remote debugging from host 9.11.5.0
warning: Could not load shared library symbols for 112 libraries, e.g. libstdc++.so.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
0x400daa80 in __futex_syscall3 () from D:\Android\AndroidNDK\android-ndk-r10e\samples\hello-jni\obj\local\armeabi-v7a\libc.so
(gdb)
点评:该工具要求环境极为苛刻且不稳定,不建议使用
操做步骤:
gdbserver --attach *:111 1234
adb forward tcp:111 tcp:111
target remote 127.0.0.1:111
(gdb) set solib-search-path C:/Users/lichao/2/
Reading symbols from C:\Users\lichao\2\linker...(no debugging symbols found)...done.
Loaded symbols for C:\Users\lichao\2\linker
Reading symbols from C:\Users\lichao\2\libc.so...(no debugging symbols found)...done.
Loaded symbols for C:\Users\lichao\2\libc.so
Reading symbols from C:\Users\lichao\2\libstdc++.so...(no debugging symbols found)...done.
Loaded symbols for C:\Users\lichao\2\libstdc++.so
Reading symbols from C:\Users\lichao\2\libm.so...(no debugging symbols found)...done.
Loaded symbols for C:\Users\lichao\2\libm.so
Reading symbols from C:\Users\lichao\2\liblog.so...(no debugging symbols found)...done.
Loaded symbols for C:\Users\lichao\2\liblog.so
Reading symbols from C:\Users\lichao\2\libcutils.so...(no debugging symbols found)...done.
Loaded symbols for C:\Users\lichao\2\libcutils.so
Reading symbols from C:\Users\lichao\2\libgccdemangle.so...(no debugging symbols found)...done.
Loaded symbols for C:\Users\lichao\2\libgccdemangle.so
Reading symbols from C:\Users\lichao\2\libcorkscrew.so...(no debugging symbols found)...done.
(gdb) bt
#0 0x400e50e0 in fork () from C:\Users\lichao\2\libc.so
#1 0x76886ca0 in Java_com_example_hellojni_HelloJni_stringFromJNI () from C:\Users\lichao\2\libhello-jni.so
#2 0x416b8350 in dvmPlatformInvoke () from C:\Users\lichao\2\libdvm.so
#3 0x416e8fd2 in dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*) () from C:\Users\lichao\2\libdvm.so
#4 0x416ea9ba in dvmResolveNativeMethod(unsigned int const*, JValue*, Method const*, Thread*) () from C:\Users\lichao\2\libdvm.so
#5 0x416c1828 in dvmJitToInterpNoChain () from C:\Users\lichao\2\libdvm.so
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) list
adb push gdb /data/bin
adb shell
./data/bin/gdb
GNU gdb 6.7
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=arm-none-linux-gnueabi --target=".
(gdb)
点评:该方法速度快,但很差查看符号
adb push android_server /data/local/tmp/
cd /data/local/tmp/
chmod 755 android_server
./android_server
adb forward tcp:23946 tcp:23946
Remote ARMLinux/Android debugger
,端口23946,调试便可,成功之后显示Accepting connection from 127.0.0.1...
./gdbserver –attach :1234 [pid]
adb forward tcp:1234 tcp:1234
arm-linux-androideabi-gdb.exe
target remote :1234
set step-mode on
set disassemble-next on
catch load 1.so
0xb6cdf480 in __epoll_pwait () from E:\aaa\libc.so
=> 0xb6cdf480 <__epoll_pwait+28>: 1e ff 2f 91 bxls lr
(gdb) bt
#0 0xb6cdf480 in __epoll_pwait () from E:\aaa\libc.so
#1 0xb6cb70ca in epoll_pwait () from E:\aaa\libc.so
#2 0xb6cb70d8 in epoll_wait () from E:\aaa\libc.so
#3 0xb6f06bd6 in android::Looper::pollInner(int) () from E:\aaa\libutils.so
#4 0xb6f06e52 in android::Looper::pollOnce(int, int*, int*, void**) () from E:\aaa\libutils.so
#5 0xb6e4d41c in android::NativeMessageQueue::pollOnce(_JNIEnv*, _jobject*, int) () from E:\aaa\libandroid_runtime.so
#6 0x732e056e in ?? ()
GikDbg 是一款移动平台的汇编级调试器,它基于 OllyDbg ,GDB 以及 LLVM 实现而来。OllyDbg 现已普遍用于 PC 平台软件安全领域,GikDbg 是 OllyDbg 向移动平台转移的产物,它能够协助您完成诸如应用调试分析,应用安全评估,应用漏洞挖掘等移动安全领域。What features can GikDbg support? http://gikir.com/product.php
GikDbg for IOS
GikDbg for Android
gikdbg.art-Gikir Debugger for Android RunTime, 是Android平台的32位汇编级调试器。此处的Android RunTime既指DVM RunTime又指ART RunTime,所以无论是运行dalvik虚拟机仍是运行本地代码的art都可以使用gikdbg.art进行程序的二进制调试分析。不一样之处在于dalvik虚拟机的运行时只能调试so动态库,而art运行时不只能调试so动态库,还能调试系统镜像oat,可执行程序dex这样的文件。另外,gikdbg-Gikir Debugger for iPhone OS,是调试越狱苹果设备的32位汇编级调试器,同窗们莫搞混淆了哈,它须要一些复杂点的服务端和客户端的配置,而gikdbg.art在正常状况下是不须要手工配置的,因此别去找android server了。对于静态分析,能够执行/ART Debug/View/ELF Data…,/ART Debug/View/ELF Code…两个菜单打开本地so,oat,dex文件。
Step 0.前置说明
手机端:Android模拟器,Android 4.4.2 ART 运行时;(真机与DVM运行时是同样的)
PC端:ParallelDesktop虚拟机,Windows 8.0,gikdbg.art v1.0.build140601.3;
PS:非root环境的设备因为权限的缘由会有不少问题,不推荐使用!
Step 1.链接设备
运行模拟器,打开gikdbg.art.exe,执行/ART Debug/Device菜单,咱们就能够来到以下界面:
若是模拟器已经运行了,可是设备列表中没有,则等待一段时间后执行右键的Refresh菜单。而后双击或者右键Login就能够登录选中的设备了。对于第一次Login该设备,会询问你是否上传依赖的文件到/data/local,这一步若是否认了的话将不能使用调试功能。上传文件这个步骤目前已知的问题是对于非root的设备,每每由于权限的缘由上传不成功,通常状况下/data/local/tmp目录没有问题,可是有些设备又没有/data/local/tmp目录,所以咱们只有设置/data/local为目标路径,这个问题目前还不知道好的解决办法。概括一下就是:非root的机器没法在其/data/local下建立咱们依赖的文件夹以及上传文件,若是咱们将其迁移至/data/local/tmp这个目录下,又有部分设备没有这个文件夹,就更没有办法上传了。
对于这类上传失败的同窗,能够想办法手工将$(GIKDBG.ART)/adb/android/gdb传至/data/local/gikir_android-xxxx/gdb这个位置,其中xxxx是GUID。
若是尚未安装该apk文件的,则能够在ADB Shell中执行$install –r命令选择gikdebugee.apk进行安装.
Step 2.选择进程
登录成功后执行,确保模拟器的gikdebugee.apk运行正常,而后执行/ART Debug/File/Attach就能够获得以下进程列表,选中咱们的gikdebugee进程,双击或者执行Attach按钮
以后咱们就会看到以下加载输出:
等gdb加载完毕以后咱们就能够进入熟悉的CPU主窗口了:
Step 3.选择模块
咱们的目的是调试apk里面的so动态库,所以执行/ART Debug/View/Module切换到模块列表,选中咱们要调试的模块,双击它
Step 4.击中断点
本例中找到要调试的函数getNativeString,咱们能够用CTRL+F查找到它,找到以后F2下断点,F9运行它,而后在设备中操做按钮则该方法将被断点击中,F8运行3步
adb push %NDK%\prebuilt\android-arm\gdbserver\gdbserver /system/bin
chmod 777 /system/bin/gdbserver
adb push test.out /system/bin
chmod 777 /system/bin/test.out
gdbserver :2345 /system/bin/test.out(若附加调试则提供进程号)
adb forward tcp:2345 tcp:2345
gdb >
gdb > target remote :2345
用ida分析so,并在JNI_OnLoad下断点,动态附加后,ida会自动rebase,使用gdb 的catch load命令捕获
参照前几节
adb shell am start -D -n com.example.hellojni/.HelloJni 启动app并等待调试器
ps | grep hellojni 获得PID 3569
adb shell run-as com.example.hellojni /data/data/com.example.hellojni/lib/gdbserver +debug-socket --attach (3569)PID
将PID与文件映射创建调试连接(c层)
adb forward tcp:5039 localfilesystem:/data/data/com.example.hellojni/debug-socket将调试连接和本地端口创建连接(c层)
adb forward tcp:65534 jdwp:(3569)PID 将本地端口和进程创建链接(java层)
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=65534 使用jdb调试java层
arm-linux-androideabi-gdb.exe
target remote :5039 使用gdb调试c层
set breakpoint pending on
am start -D -n com.example.hellojni/.HelloJni
gdbserver --attach :1234 10863
adb forward tcp:1234 tcp:1234
arm-linux-androideabi-gdb Target remote :1234
root@ja3gchnduos:/ # am start -D -n com.example.hellojni/.HelloJni
Starting: Intent { cmp=com.example.hellojni/.HelloJni }
root@ja3gchnduos:/ # ps | grep hello
u0_a165 10863 3593 869292 16088 ffffffff 40077a08 S com.example.hellojni
root@ja3gchnduos:/ # gdbserver --attach :1234 10863
Attached; pid = 10863
Listening on port 1234
set solib-search-path c:/1
catch load libhello-jni.so
Catchpoint 1
Inferior loaded C:\Users\lichao\sumsing\libhello-jni.so
0x40036b8c in rtld_db_dlactivity () from C:\Users\lichao\sumsing\linker
Android底层为linux层,gdb用于调试linux应用层,而kgdb用于调试linux内核层
kgdb的android版本下载:http://github.com/dankex/kgdb-android
经常使用Hook框架:
360手机卫士在非root状况下卸载后弹出浏览器。因而有2种常见可能,一种是intent跳转,一种是执行am命令,后者能够在java层和jni层实现,若是是java层考虑进行hook,jni层考虑修改am.jar
将/system/bin/am更名,发现没法弹窗,因而肯定是经过第二种方式实现,为了肯定调用层级,尝试修改(反编译成smali->加入logcat输出打印回溯栈和接收参数->回编译)/system/framework/am.jar,(能够经过本身再另外一个app中实现一样的功能,经过反编译获得smali代码)再次反编译后内容以下:
public static void main(String[] args) {
String v0 = "";
int v3 = args.length;
int v2;
for(v2 = 0; v2 < v3; ++v2) {
v0 = String.valueOf(v0) + " " + args[v2];
}
Log.d("my god", v0);
Log.d("my god", Log.getStackTraceString(new Throwable()));
}
在卸载瞬间拿到输出:
start -n com.android.browser/.BrowserActivity -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html?u=100&id=76bb84de8f53b53f57dd3cedfe966091&v=6.3.1.1048&s=1&model=SE0gTk9URSAxTFRF&sdk=19&ch=200222&wid=9fa298f35aec4232c26048442f36dc59 --user 0
java.lang.Throwable
at com.android.commands.am.Am.main(Am.java:30)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:245)
at dalvik.system.NativeStart.main(Native Method)
发现命令行是 am start –n com.android.browser/.BrowserActivity -a android.intent.action.VIEW
经过字符串搜索,定位到java层关键代码,使用android hook框架cydia substrate,挂钩java.lang.Runtime类的exec函数,定位到调用栈:
content:/data/user/0/com.qihoo360.mobilesafe/files/so_libs/um.0.2 com.qihoo360.mobilesafe --execute am start -n com.android.browser/.BrowserActivity -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html?u=100\&id=7b55c26b779bd111dfed8b02bb00131c\&v=5.5.0.1041\&s=1\&model=TmV4dXMgUw\&sdk=19\&at=KTvooEkhHMzgJ13AXfMkJINnhrmyyNdu\&ch=200222 --user 0
java.lang.Throwable
at com.example.emptytest.Main$1$1.invoked(Main.java:68)
at com.saurik.substrate.MS$2.invoked(MS.java:68)
at java.lang.Runtime.exec(Native Method)
at egv.a(360MobileSafe:257)
at egv.a(360MobileSafe:66)
at com.qihoo360.mobilesafe.ui.index.MobileSafeApplication.p(360MobileSafe:1223)
at com.qihoo360.mobilesafe.ui.index.MobileSafeApplication.onCreate(360MobileSafe:799)
启动不久,360启动linux程序/data/data/com.qihoo360.mobilesafe/com.qihoo360.mobilesafe/files/so_libs/um.0.2
,并将弹窗任务以参数形式传递给该程序,程序中对/data/data/com.qihoo360.mobilesafe文件夹的删除操做进行挂钩,以实现卸载后弹窗机制
(gdb) disass /r 0x401148b8,0x40114900
Dump of assembler code from 0x401148b8 to 0x401148c8:
=> 0x401148b8: 0c 70 a0 e1 mov r7, r12
0x401148bc: 01 0a 70 e3 cmn r0, #4096 ; 0x1000
0x401148c0: 1e ff 2f 91 bxls lr
0x401148c4: 00 00 60 e2 rsb r0, r0, #0
0x401148c8: 0e 70 00 ea b 0x40130908
End of assembler dump.
print expr
print ”%d” a
dprintf 动态插入printf函数
dprintf location,format string,arg1,arg2,...
info registers
info args
info locals
x
set *(unsigned int*)0x800000000=0x00000000
break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION] clear [LOCATION]
break *0x4000000 绝对地址
break 12 行号
break func1 函数
clear *0x40000000
clear 12
clear func1
tbreak [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
break func1 thread1 if i==0
watch/awatch/ rwatch [-l|-location] EXPRESSION 变化/读写/读断点
(若是EXPRESSION不是绝对地址,则须要用-l计算表达式)
watch *0x40000000==0x90909090
watch –l *$pc
watch i (有源码,变量i的值有变化时中止)
break-range START-LOCATION, END-LOCATION
break-range 1.c:5, 1.c:10 在1.c的第5行和第10行之间下断
break-range +5, +10 在当前行+5和当前行+10之间下断
普通硬断 hbreak [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
临时硬断 thbreak [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
xbreak
普通补断 catch [assert|catch|exception|exec|fork|load|rethrow|signal|syscall|throw|unload|vfork]
临时捕断 tcatch [assert|catch|exception|exec|fork|load|rethrow|signal|syscall|throw|unload|vfork]
如何使程序在so加载时刻断下??(网上很多人用奇葩方式,仍是对gdb不了解)
catch load 1.so
strace [LOCATION] [IF CONDITION]
bt [N] 显示N层调用栈
bt full 显示所有调用栈
行为 | 命令 |
---|---|
运行时中断 | Ctrl+C |
结束程序 | kill |
单步步过 | next |
单步步入 | step |
单步步过(指令级) | nexti |
单步步入(指令级) | stepi |
继续运行 | continue |
执行到当前函数指定位置 | advance |
分离进程 | detach |
强制跳转 | jump |
reverse-continue reverse-next reverse-search reverse-stepi reverse-finish reverse-next reverse-step
info shared
能够用于作二进制对比
(gdb) load C:/Users/lichao/2/libadnative.so 0x50000000
Loading section .interp, size 0x13 lma 0x50000134
Loading section .dynsym, size 0x1210 lma 0x50000148
Loading section .dynstr, size 0x2061 lma 0x50001358
Loading section .hash, size 0x8a8 lma 0x500033bc
Loading section .rel.dyn, size 0x10a0 lma 0x50003c64
Loading section .rel.plt, size 0x1b0 lma 0x50004d04
Loading section .plt, size 0x29c lma 0x50004eb4
Loading section .text, size 0xe7a0 lma 0x50005150
Loading section .ARM.extab, size 0x8e8 lma 0x500138f0
Loading section .ARM.exidx, size 0xd80 lma 0x500141d8
Loading section .rodata, size 0x11bc lma 0x50014f58
Loading section .data.rel.ro.local, size 0x738 lma 0x50018098
Loading section .fini_array, size 0x8 lma 0x500187d0
Loading section .init_array, size 0x14 lma 0x500187d8
Loading section .data.rel.ro, size 0x508 lma 0x500187f0
Loading section .dynamic, size 0xf8 lma 0x50018cf8
Loading section .got, size 0x210 lma 0x50018df0
Loading section .data, size 0x1c lma 0x50019000
Start address 0x0, load size 94044
Transfer rate: 188 KB/sec, 2541 bytes/write.
file c:/1.so
gcore
进程空间inferior,用于调试多个进程,fork函数会自动添加进程空间
操做 | 指令 |
---|---|
添加进程空间 | add-inferior |
复制进程空间 | clone-inferior 1 |
删除进程空间 | remove-inferior 1 |
切换进程空间 | inferior 2 |
分离进程空间 | detach inferior 2 |
maintenance translate-address [address]
info functions [regex] 定位地址
info symbol address 定位文件
info variables [regex] 全局静态符号
目标系统:! [command]
主机系统:shell [command]
info threads
info vtbl