应用安全 --- 逆向技巧 之 ida如何区分哪些是自定义函数,哪些是系统函数
函数分类分析
一、自定义函数(用户编写的业务逻辑)
1. JNI 导出函数(最核心)
text
Java_com_example_helloworld_MainActivity_stringFromJNI
地址: 0x1ED00
特征: Java_包名_类名_方法名 格式,这是唯一的JNI导出函数
这是整个so库对外暴露的唯一接口
2. 未命名的内部函数(sub_xxxx)
text
# 与JNI函数直接相关的核心函数
sub_1EE5C 0x1EE5C 大小:0x24 ← 被JNI函数直接调用,最可疑的核心逻辑
sub_1ECB0 0x1ECB0 大小:0x10
sub_1ECC0 0x1ECC0 大小:0x08
sub_1ECC8 0x1ECC8 大小:0x08
sub_1ECD0 0x1ECD0 大小:0x14
sub_1ECE4 0x1ECE4 大小:0x1C
# 与JNI函数地址相近的辅助函数
sub_1EEF8 0x1EEF8
sub_1EF0C 0x1EF0C
sub_1EF20 0x1EF20
sub_1EF48 0x1EF48
sub_1EF58 0x1EF58
sub_1EF80 0x1EF80
sub_1EF94 0x1EF94
sub_1EFE8 0x1EFE8
sub_1F01C 0x1F01C
sub_1F044 0x1F044
sub_1F070 0x1F070
二、系统/外部函数分类
1. C 标准库函数(libc.so)
text
# 内存操作
malloc 0x049F00
free 0x049F08
realloc 0x04A068
memcpy 0x049F28
memmove 0x049F20
memset 0x049F38
memchr 0x049F30
memcmp 0x049F40
posix_memalign 0x04A010
# 字符串操作
strlen 0x049F18
strcmp 0x04A090
strncmp 0x04A0D0
snprintf 0x049FE0
fprintf 0x04A0D8
fwrite 0x04A108
fflush 0x04A0E0
fputc 0x04A030
# 数值转换
strtol 0x049FF0
strtoul 0x049F80
strtoll 0x049F90
strtoull 0x049FA0
strtod 0x049FC0
strtof 0x049FB0
strtold 0x049FD0
# 宽字符操作
wcslen 0x049F58
wmemcpy 0x049F50
wmemmove 0x049F48
wmemset 0x049F68
wmemchr 0x049F60
wmemcmp 0x049F70
wcstol 0x049FF8
wcstoul 0x049F88
wcstod 0x049FC8
# 其他系统函数
abort 0x04A060
__stack_chk_fail 0x049F10 ← 栈保护
__errno 0x049F78
__strlen_chk 0x04A018
__memmove_chk 0x04A070
__vsnprintf_chk 0x04A078
2. C++ 标准库函数(libc++.so / libstdc++)
text
# 内存管理
operator new(ulong) 0x026EC0
operator new[](ulong) 0x026F5C
operator delete(void*) 0x026F94
operator delete[](void*) 0x026FAC
# std::string 操作(大量内联到.text段)
std::string::basic_string(char const*) 0x1EDAC ← 构造函数
std::string::~string() 0x1F840 ← 析构函数
std::string::__init(char const*,ulong) 0x1FB38
std::string::append(char const*) 0x021000
std::string::assign(char const*) 0x020CA0
std::string::find(...) 0x020D6C
std::string::compare(...) 0x020A78
std::string::reserve(ulong) 0x01FF98
# std::wstring 操作
std::wstring::__init(...) 0x021D04
std::wstring::append(...) 0x023070
std::wstring::assign(...) 0x022D88
# 数值转换
std::to_string(int) 0x024F64
std::to_string(long) 0x025060
std::to_string(double) 0x025FFC
std::to_wstring(int) 0x0255A0
# 异常相关
std::__throw_bad_alloc() 0x026E88
std::bad_alloc::bad_alloc() 0x03ED18
std::logic_error::logic_error() 0x027118
std::runtime_error::runtime_error() 0x027368
std::terminate() 0x028218
std::unexpected() 0x0281FC
3. C++ ABI 异常处理函数(libgcc / unwind)
text
__cxa_throw 0x027A58 ← 抛出异常
__cxa_begin_catch 0x027B8C ← 捕获异常开始
__cxa_end_catch 0x027C2C ← 捕获异常结束
__cxa_rethrow 0x027DBC ← 重新抛出
__cxa_allocate_exception 0x02799C ← 分配异常对象
__cxa_free_exception 0x0279F0 ← 释放异常对象
__cxa_call_unexpected 0x03CD88 ← 意外异常处理
__cxa_demangle 0x028548 ← 符号名还原
__cxa_get_globals 0x028140 ← 获取全局异常状态
__cxa_atexit extern ← 注册退出处理
__cxa_finalize extern ← 析构处理
__gxx_personality_v0 0x03C628 ← C++异常个性函数
# 跳转桩(j_前缀)
j_.__cxa_call_unexpected 0x027A10
j_.__cxa_call_unexpected_0 0x027B54
... (共10个跳转桩)
4. RTTI 和动态类型(C++ 运行时)
text
__dynamic_cast 0x03E08C ← dynamic_cast实现
std::type_info::~type_info() 0x03F1A0
std::bad_cast::bad_cast() 0x03F1B0
std::bad_typeid::bad_typeid()0x03F20C
5. Android 专有函数(libandroid.so / bionic)
text
__system_property_get 0x04A0C8 ← 读取系统属性
android_set_abort_message 0x04A040 ← 设置abort消息
getauxval 0x04A0C0 ← 获取辅助向量值
dl_iterate_phdr 0x04A0F8 ← 遍历程序头
__emutls_get_address 0x03F268 ← TLS模拟实现
6. POSIX 线程函数(libpthread.so)
text
pthread_mutex_lock 0x04A080
pthread_mutex_unlock 0x04A088
pthread_once 0x04A0A0
pthread_key_create 0x04A0B8
pthread_key_delete 0x04A0B0
pthread_getspecific 0x04A098
pthread_setspecific 0x04A0A8
pthread_rwlock_rdlock 0x04A100
pthread_rwlock_wrlock 0x04A0E8
pthread_rwlock_unlock 0x04A0F0
7. 日志函数(syslog)
text
openlog 0x04A048
syslog 0x04A050
closelog 0x04A058
三、汇总对比表
text
┌─────────────────────┬──────────┬──────────────────────────────┐
│ 函数类型 │ 数量 │ 来源库 │
├─────────────────────┼──────────┼──────────────────────────────┤
│ 自定义JNI导出函数 │ 1个 │ 用户代码 │
│ 自定义内部函数(sub_) │ 200+个 │ 用户代码(未命名) │
│ C标准库函数 │ 40+个 │ libc.so (bionic) │
│ C++标准库函数 │ 100+个 │ libc++.so (NDK) │
│ C++ ABI异常函数 │ 20+个 │ libgcc/libunwind │
│ Android专有函数 │ 5个 │ libandroid.so/bionic │
│ POSIX线程函数 │ 10个 │ libpthread.so │
│ 日志函数 │ 3个 │ libc.so (syslog) │
└─────────────────────┴──────────┴──────────────────────────────┘
关键结论:
- 真正的业务逻辑 只有
Java_com_example_helloworld_MainActivity_stringFromJNI和sub_1EE5C这两个函数- 其余
sub_xxxx函数大多是 C++标准库静态链接进来的实现代码- 整个 so 库将
libc++静态链接(函数在.text段而非extern),这是 NDK 编译的典型特征
免责声明
本文档所有内容仅供安全研究、学术交流与技术学习使用,严禁用于任何未经授权的逆向破解、网络攻击、隐私窃取、恶意软件开发及其他违反《中华人民共和国网络安全法》《数据安全法》等法律法规的行为,使用者应确保已获得目标软件权利人的合法授权并自行承担因使用本文档内容所产生的一切法律责任与后果,作者不对任何直接或间接损害承担任何责任,继续阅读即视为您已知悉并同意上述全部条款。
浙公网安备 33010602011771号