-
-
[原创]移动应用安全与风控——应用分析
-
发表于: 2023-6-19 15:57 7498
-
开始解析分析Android应用之前,我们先来了解其的整体组成。Android应用,即Android Application Package,Android应用程序包,通常都简称为APK。APK的本质是一个后缀被为apk的压缩包,其中包含了程序运行所需的可执行文件和相关资源文件,APK需要安装到Android系统上运行。Android应用标准的结构如下图所示:
Android应用程序中包含的标准文件内容如下图所示:
Android应用程序的逆向分析主要就是使用工具将.apk文件中的DEX文件、配置文件和SO动态库等文件进行逆向反编译。使用Apktool、Jadx和IDA等工具对APK文件进行反编译,分析反编译后的文件找到程序的漏洞。
Android应用程序的四大组件都需要在Manifest配置文件中声明,可通过配置文件中的组件信息快速定位到其所在的代码位置。首先通过配置文件声明的信息定位到程序应用执行入口,然后便可按着程序执行流程就行分析。配置文件中定位执行入口特征如下图所示:
通过配置文件可知该程序的程序执行的入口类为com.test.demo.MainActivity,通过该类的路径很容易定位到定位到反编译后的代码位置,如图所示:
除此之外还可以使用adb命令配合Manifest配置文件声明的组件信息进行安全测试,如组件拒绝服务漏洞等。参考命令如下所示:
Android应用程序除了使用Java/Kotin开发外,还可以使用C/C++开发。使用Java/Kotin开发的部分通常被称为Java层,使用C/C++开发的部分通常常被称为Native层。Java层和Native层是通过JNI进行交互,也就是说JNI接口是两者的连接点,我们在分析应用时只要定位到JNI接口能快速定位相关代码。
Java层和Native层双方都需要注册JNI函数后,双方才能正常进行交互。Java层注册JNI函数的格式可参考如下代码:
Native层注册JNI函数的方式有静态注册和动态注册两种方式。静态注册的方式方便简便,但从攻防的角度考虑静态注册是不安全的。因其是直将Java层注册的函数以“Java_包名_类名_方法名”在Native层进行声明,可以快速通过SO动态库中暴露的JNI函数名定位到其与Java层代码的关联。静态注册具体实现如下图所示:
可通过IDA逆向分析动态库库是查看导出函数,如果其是通过静态注册方式声明的JNI函数便可根据函数名定位到其在Java层的调用位置。IDA分析结果如下图所示:
如果没有安装IDA还可以使用objdump命令查看动态库中的导出函数,具体命令如下图所示:
Native层通过动态方式注册JNI函数的实现起来稍微复杂一点,我们先看一下动态注册的核心代码实现方式。只有知道其实现方式才能在逆向分析时进行快速定位。动态注册核心代码如下图所示:
其中最核心的就是JNINativeMethod结构体,该结构体包含三个参数,这三个参数决定了Java层和Native层Jni函数的映射关系。逆向分析时可通过该结构体快速定位到Native层的JNI导出函数。JNINativeMethod结构体具体参数含义如下:
知道了JNINativeMethod结构体中参数含义就可以快速分析,首先拿到通过反编译Java层代码拿到其生命的Native函数名,然后利用IDA反编译目标SO文件。通过字符串查找功能快速定位Native函数名所在位置,如下图所示:
利用IDA的交叉引用功能跳转到使用该字符串的位置,通过分析发现此处正是动态注册中初始化后的JNINativeMethod结构体。此结构中第三参数即Native层声明的JNI函数,具体如下图所示:
至此你已经初步掌握Android应用的分析方法,可以独立进行移Android应用的逆向分析。
本节要通过一个iOS应用逆向案例讲解iOS应用分析的大致流程。同样开始解析分析iOS应用之前,先来了解其的整体组成。iOS标准应用是后缀IPA的文件,全称为iPhone application archive,通常简称为IPA。IPA的本质也是压缩包,其中包含了程序运行所需的可执行文件和相关资源文件,IPA需要安装到iOS系统上运行。iOS应用标准的结构如下图所示:
iOS应用程序中包含的标准文件内容如下图所示:
iOS应用在提交到AppStore时,苹果官方将对其进行加密处理,加密后的应用是无法直接进行反编译,需要进行解密处理,也就是通常所说的砸壳。砸壳需要使用越狱后的手机配合砸壳工具进行,比较常见砸壳工具有Clutch、frida-ios-dump、dumpdecrypted等,这里咱们使用Clutch工具进行砸壳,具体命令如下图所示:
脱壳结束后便可使用工具对其进行逆向分析,为了快速定位到关键函数。可使用class-dump工具将应用的的头文件导出,具体命令如下:
导出的header文件中有程序声明的函数名或者变量的名字,可通过header中的内容提高分析速度。导出的header文件内容示例如下图所示。
要想看到某个具体函数的实现逻辑可以使用反编译工具IDA或Hopper将解压IPA文件得到执行文件进行饭反编译,然后定位到目标函数即可看到其实现逻辑。此处以函数injectNSURLSessionConfiguration为例,通过反编译工具查看其实现逻辑,如下图所示:
如果自己手里关键信息只有一串字符,无法确定其在哪个函数中调用。可以通过IDA的字符串查找功能定位到字符串在代码中的位置,然后就可以通过IDA交叉引用功能跳转到其调用位置
至此你已经初步掌握iOS应用的分析方法,可以独立进行iOS应用的逆向分析。
# 启动目标应用
adb shell am start
-
n com.test.demo
/
com.test.demo.MainActivity
# 针对空Intent导致的本地拒绝服务测试
adb shell am start
-
n com.test.demo
/
com.test.demo.TestActivity
# 启动目标应用
adb shell am start
-
n com.test.demo
/
com.test.demo.MainActivity
# 针对空Intent导致的本地拒绝服务测试
adb shell am start
-
n com.test.demo
/
com.test.demo.TestActivity
package com.test.demo;
public
class
JniWrapper {
static {
System.loadLibrary(
"native-lib"
);
}
public static native String stringFromJNI();
}
package com.test.demo;
public
class
JniWrapper {
static {
System.loadLibrary(
"native-lib"
);
}
public static native String stringFromJNI();
}
JNIEXPORT jstring JNICALL Java_com_test_demo_JniWrapper_stringFromJNI(
JNIEnv
*
env,
jclass ) {
std::string hello
=
"Hello from JNI"
;
return
env
-
>NewStringUTF(hello.c_str());
}
JNIEXPORT jstring JNICALL Java_com_test_demo_JniWrapper_stringFromJNI(
JNIEnv
*
env,
jclass ) {
std::string hello
=
"Hello from JNI"
;
return
env
-
>NewStringUTF(hello.c_str());
}
$ objdump
-
tT libnative
-
lib.so | grep Java
000088ac
g DF .text
00000074
Java_com_test_demo_JniWrapper_stringFromJNI
$ objdump
-
tT libnative
-
lib.so | grep Java
000088ac
g DF .text
00000074
Java_com_test_demo_JniWrapper_stringFromJNI
static JNINativeMethod method_table[]
=
{
{
"stringFromJNI2"
,
"()Ljava/lang/String;"
, (void
*
)helloJni},
/
/
绑定
};
JNIEXPORT jint JNI_OnLoad(JavaVM
*
vm, void
*
reserved)
{
JNIEnv
*
env
=
nullptr;
jint result
=
-
1
;
if
(vm
-
>GetEnv((void
*
*
) &env, JNI_VERSION_1_4) !
=
JNI_OK)
{
return
result;
}
jclass clazz;
clazz
=
env
-
>FindClass(
"com/test/demo/JniWrapper"
);
if
(clazz
=
=
nullptr)
{
return
JNI_FALSE;
}
if
(env
-
>RegisterNatives(clazz, gMethods, numMethods) <
0
)
{
return
JNI_FALSE;
}
return
JNI_VERSION_1_4;
}
static JNINativeMethod method_table[]
=
{
{
"stringFromJNI2"
,
"()Ljava/lang/String;"
, (void
*
)helloJni},
/
/
绑定
};
JNIEXPORT jint JNI_OnLoad(JavaVM
*
vm, void
*
reserved)
{
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
- [原创]《APP安全实战指南》正式出版上线 3780
- [原创]移动应用安全与风控——正式出版,更名为《APP安全实战指南》 6651
- [原创]移动应用安全与风控——重签名攻击 15526
- [原创]移动应用安全与风控——汇编基础 21849
- [原创]移动应用安全与风控——应用分析 7499