-
在
app/src/main/cpp
新建一个C/C++ Source File,type选择.c
。填入代码,点击Sync Now,同步代码。#include <string.h> #include <jni.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <android/log.h> #define TAG "heyTag" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__) /* This is a trivial JNI example where we use a native method * to return a new VM String. See the corresponding Java source * file located at: * * hello-jni/app/src/main/java/com/example/hellojni/HelloJni.java */ JNIEXPORT jstring JNICALL Java_com_heyhu_openso_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) { #if defined(__arm__) #if defined(__ARM_ARCH_7A__) #if defined(__ARM_NEON__) #if defined(__ARM_PCS_VFP) #define ABI "armeabi-v7a/NEON (hard-float)" #else #define ABI "armeabi-v7a/NEON" #endif #else #if defined(__ARM_PCS_VFP) #define ABI "armeabi-v7a (hard-float)" #else #define ABI "armeabi-v7a" #endif #endif #else #define ABI "armeabi" #endif #elif defined(__i386__) #define ABI "x86" #elif defined(__x86_64__) #define ABI "x86_64" #elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */ #define ABI "mips64" #elif defined(__mips__) #define ABI "mips" #elif defined(__aarch64__) #define ABI "arm64-v8a" #else #define ABI "unknown" #endif return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI "."); }
-
在CmakeLists.txt 声明新建的
heyhu.c
文件。add_library( # Sets the name of the library. heyhu # Sets the library as a shared library. # 这里共享, 别的app就可以load此so文件 SHARED # Provides a relative path to your source file(s). heyhu.c ) target_link_libraries( # Specifies the target library. heyhu # Links the target library to the log library # included in the NDK. ${log-lib} )
-
在 Java文件中声明加载的文件, 运行在手机上就可以看到效果了。
static { System.loadLibrary("heyhu"); }
编译本地版
- 编写md5的c代码到本地, 在github中找一个即可。
- 到对应目录下执行gcc -o md5 md5.c`命令进行编译。
./md5 heyhu
得到heyhu
的md5值。
将NDK与其他构建系统配合使用
/Android/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang -target aarch64-linux-android21 md5.clang
,会生成一个a.out
可执行文件。注意c++ 需要使用clang ++
。- 因为我们选择的ABI位arm64-v8a,需要讲此文件push到手机上进行执行。对应关系详见文档。
移植到代码中: heyhu.c
Tips 如果安装后结果不对,可以删除/app/build/outputs/apk下的文件,修改成32位ABI即可
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.heyhu.openso"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
// 现在基本32位
ndk {
abiFilters 'armeabi-v7a'
}
}
}
llvm可以进行汇编、反汇编,机器码反汇编成16进制、指令集(disassemble)。llvm可以把C\C++代码编译成一个SO文件。
调试第三方App满足以下任意一种方式即可,推荐第二种方式:
1. app以debug模式、apk包中debuggable==true(得重打包、或者xposed/frida去hook)
2. 手机是aosp系统,编译成userdebug模式(n5x、sailfish)
bebug的时候 会把llvm的组件lldb中的start_lldb_server.sh push到手机的/data/local/tmp/
中,调试的时候会启动lldb服务。
如果之前使用Android Studio Debug过,它会自动帮我们push。
如果没有需要手动把文件push到手机中,其路径为/Users/zhaoshouxin/Library/Android/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/9.0.8/lib/linux/aarch64或者arm以及x86_64
。aarch64位64位、arm为32位,以app进程为基准,不可混用。push完成后运行./lldb-server
看是否可用。
lldb路径:/Android/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/darwin-x86_64/bin/lldb
与clang在同一目录下,可以把目录设置为环境变量。
在Android Studio bebug模式下,旁边也有lldb切换卡,也可以操作lldb。
手机下启动lldb服务 ./lldb-server platform --listen "*:10086" --server
首先你需要在你的linux系统上安装lldb,然后执行lldb。进入后执行以下命令
platform select remote-android
#ENU7N16709000458 是adb devices显示的设备名称
platform connect connect://ENU7N16709000458:10086
attach -p 30827
详细文章入口。
Capstone/Keystone(反)汇编器
作用:动态反汇编。原理内置Capstone
function dis(address, number) {
for (var i = 0; i < number; i++) {
var ins = Instruction.parse(address);
console.log("address:" + address + "--dis:" + ins.toString());
address = ins.next;
}
}
setImmediate(function(){
var stringFromJniaddr = Module.findExportByName("libroysue.so","Java_com_roysue_easyso1_MainActivity_stringFromJNI")
dis(stringFromJniaddr,10);
})
和lldb打印的一样。
CMakeLists.txt 最下面添加
# gdb 调试
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
app下的build.gradle中android添加
packagingOptions{
doNotStrip "*/armeabi/*.so"
doNotStrip "*/armeabi-v7a/*.so"
doNotStrip "*/x86/*.so"
}
编译成功后,so文件会增加100k左右。
GDB 为C/S的调试架构:
服务端地址为:
Android/sdk/ndk/22.0.7026061/prebuilt/android-arm/gdbserver
客户端使用前人编译好的Pwndocker
进入docker后运行gdb-multiarch,要不然会报错。
$ 手机内启动64/32gdbserver: ./gdbserver 0.0.0.0:23946 --attach target_pip
$ 端口转发: adb forward tcp:23946 tcp:23946
$ docker内运行客户端gdb-multiarch,进入pwndbg
$ 连接服务端target remote 手机ip:23946
$ pwndbg设置架构: set arch arm, set arm fallback-mode thumb
$ c(运行到下一个断点) r(继续运行) ctrl+c暂停 info share查看所有加载的so
$ nm -s so_name 查看所有符号
$ 本地运行: nm -s so_name |grep method 查看 "*method*"方法地址或者使用objection、frida查看
$ 查看该地址汇编: x/20i base_addr
cat /proc/10700/maps |grep heyhu 基址为:d1241000
d1241000-d1247000 r-xp 00000000 103:13 917882
$ b *(base_addr)设置断点 info b 查看断点
$ del 编号 删除断点
$ 步入:f7 步进:f8
$ finish 跳出方法
$ frida主动调用方法和pwndbg同时使用的话需要先启动frida附加(注入器注入之后ptracepid=0)然后再启动gdbserver,在主动调用时候先用pwndbg暂停,
然后frida主动调用, 然后f8就可以一步一步调试了。
$ objection查看so基址:memory list modules
$ memory list exports so_name 查看so的所有符号基址
$ 下两个断点切换状态 ctrl+shift+pageup
$ frida和gdb为什么不能一起调试?
其中一个程序attach目标进程时,TracerPid会被占用,别的进程就附加不上。
cat /proc/10700/status TracerPid: 25096 -> gdb
$ 为什么frida先附加后,gdb可以进行调试?
谁进行调试 TracerPid就是谁的进程,frida注入后 TracerPid为0,因为注入器注入后就dettch了,然后就可以用gdb了。
Objection:Process Architecture arm
查看目标进程的父进程
root 3316 1 4213072 48928 poll_schedule_timeout 7135b2b518 S zygote64
root 3317 1 1552068 40104 poll_schedule_timeout e72ebc4c S zygote
https://developer.android.google.cn/ndk/guides/other_build_systems
https://my.oschina.net/u/4263893/blog/4349408
https://blog.csdn.net/wangyiyungw/article/details/81069631
https://github.com/heyhu/openso
https://lldb.llvm.org/use/map.html
https://github.com/skysider/pwndocker
https://www.cnblogs.com/zhwer/p/12494317.html