在移动互联网时代,设备安全检测是风控体系中的重要一环。掌握前沿风控技术是每个安全人员的重要的必修课程之一。Overt 项目致力于收集并集成当前公开的检测技术于一身,构建了一个全面、高效、安全的Android设备安全检测体系。
本文将从技术原理、架构设计、模块实现等多个维度,深入解析Overt项目的核心技术和实现细节,为安全从业者提供一份全面的技术参考。通过详细分析JVM获取、ClassLoader遍历、Linker解析、SSL证书验证、TEE检测等关键技术难点,帮助读者理解当前的Android设备安全检测技术,所以赶快来领取顶级攻防游戏的入场券吧。
Overt采用分层架构设计,确保核心检测逻辑的安全性和性能:
Root检测是Android设备安全检测的核心环节,通过检测系统中是否存在Root权限获取工具来判断设备是否已被Root。传统的Root检测往往容易被绕过,因此需要采用更底层、更隐蔽的检测方式。
1.1 自定义Syscall实现
1.2 自定义文件操作实现
1.3 Root文件检测实现
技术优势:
技术难点解析:
在Android Native库的__attribute__((constructor))初始化阶段,Java层尚未完全启动,此时获取JVM实例和Context对象面临巨大挑战。
2.1 JVM获取机制
2.2 跨线程JNI环境管理
关键技术点:
2.3 Context创建机制
技术难点解析:
Android的ClassLoader体系复杂,包括BootClassLoader、PathClassLoader、DexClassLoader等,需要深度遍历所有ClassLoader来检测异常类加载。
3.1 标准API遍历方式
3.2 ART内部API遍历方式
技术难点解析:
Android的动态链接器(linker)管理所有加载的共享库,通过遍历linker内部数据结构可以检测异常库加载和Hook行为。
4.1 Linker内部结构解析
4.2 库遍历机制
4.3 库完整性校验
技术难点解析:
SSL证书检测是验证网络通信安全性的重要手段,本项目通过 mbedtls 库实现完整的TLS握手和证书验证流程。
5.1 自定义HTTPS客户端
5.2 证书指纹验证
5.3 mbedtls静态库编译
技术难点解析:
TEE(Trusted Execution Environment)是Android设备的重要安全特性,它提供了一个与主操作系统隔离的安全执行环境。TEE检测能够从硬件层面验证设备的安全状态,通过解析Android KeyStore Attestation证书来获取设备的可信启动状态、安全级别等关键信息。
TEE检测基于Android KeyStore的Attestation功能,通过生成的密钥对,获取包含设备安全状态的证书链,然后使用纯C++实现解析证书中的TEE扩展信息,避免依赖Java三方库,提高安全性和性能。
6.1 获取TEE证书(JNI桥接)
6.2 C++ ASN.1解析器实现
6.3 TEE扩展解析
6.4 RootOfTrust信息解析
6.5 主解析函数
技术优势:
技术难点解析:
ASN.1复杂性:需要实现ASN.1 DER编码解析,包括标签、长度、值的解析
证书结构:理解X.509证书和TEE扩展的复杂结构,包括TBSCertificate、扩展序列等
OID匹配:正确解析和匹配TEE attestation扩展OID(1.3.6.1.4.1.11129.2.1.17)
Keymaster标签:理解Keymaster标签格式,正确解析RootOfTrust序列
内存安全:确保ASN.1解析过程中的边界检查,防止缓冲区溢出
Overt采用线程池机制实现异步任务调度,支持CPU核心绑定、优先级提升和智能任务管理。通过zThreadPool实现所有检测任务的并发执行,提高检测效率。
7.1 线程池初始化
7.2 CPU核心绑定与优先级提升
7.3 周期性任务管理
技术优势:
技术难点解析:
为了实现更高的防护能力,Overt 自实现了非标准的 string、vector、map 等基础 api,并通过config.h中的宏替换机制实现了标准API与非标API之间的无缝切换,这是一种编译时切换策略。
8.1 宏控制机制
8.2 宏映射机制
8.3 自定义API实现特点
技术优势:
技术难点解析:
侧信道检测通过比较不同系统调用的执行时间来判断是否存在调试工具或Hook框架。当系统被Hook时,系统调用的执行时间会显著增加。
实现原理:
检测应用签名信息,验证应用是否被重新签名或篡改。
检测Frida等调试工具注入的进程,分析进程状态和内存映射。
检测调试工具常用的端口监听,如Frida默认端口27042。
检测系统时间篡改、启动时间异常等。
检测系统日志中的可疑记录,如Zygisk痕迹。
扫描本地网络中的设备,检测同一网络中的其他Overt设备。
其实是否选择开源还是纠结了挺长时间的,因为最开始是打算开源的,但写着写着就突然理解了为什么例如 NativeTest、hunter、momo 等检测项目为什么选择闭源,确实倾注了很多心血。每一个技术细节的打磨,每一次检测逻辑的优化,从单一功能到完整的检测体系,每一个功能都来之不易。希望本项目能为其它同学提供有价值的技术参考,无论选择开源还是闭源,最重要的是保持对技术的热爱和对安全的执着,这才是行业发展的真正动力。
1bfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6D9P5s2A6Q4x3X3c8B7K9h3q4F1k6r3q4F1i4K6u0r3e0%4k6W2M7Y4b7`.
[原创]自动化采集Android系统级设备指纹对抗&如何四两拨千斤?
[原创]Android风控详细解读以及对照工具
[原创]KernelSU检测之“时间侧信道攻击”
珍惜 hunter
472K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6y4j5X3g2V1i4K6u0V1g2p5I4e0i4K6u0r3L8h3u0W2k6s2c8D9M7H3`.`.
017K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6$3N6X3t1J5x3o6j5H3i4K6u0r3d9$3g2&6b7i4c8@1k6i4y4@1j5i4c8A6L8$3^5`.
444K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6$3N6X3t1J5x3o6j5H3i4K6u0r3h3s2m8G2M7$3g2V1c8r3g2@1k6h3y4@1L8%4t1`.
012K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1P5i4S2A6j5h3!0J5N6h3&6Q4x3V1k6d9N6i4u0#2
5adK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2j5K6y4K6M7K6m8J5i4K6u0r3L8$3u0X3N6i4y4Z5k6h3q4V1k6i4u0Q4x3X3g2Z5
┌─────────────────────────────────────┐
│ Java层 (UI层) │
│ ├── MainActivity │
│ │ └── onCardInfoUpdated (静态回调) │
│ ├── InfoCardContainer │
│ ├── InfoCard │
│ └── MainApplication │
│ └── System.loadLibrary("overt") │
├─────────────────────────────────────┤
│ JNI桥接层 │
│ ├── native-lib.cpp │
│ │ ├── init_ (constructor) │
│ │ └── JNI_OnLoad │
│ └── zManager (设备信息管理中心) │
│ ├── round_tasks (周期性任务) │
│ └── notice_java (通知Java层) │
├─────────────────────────────────────┤
│ Native C++层 │
│ ├── 检测模块 (z*Info.cpp) │
│ │ ├── zRootStateInfo │
│ │ ├── zTeeInfo │
│ │ ├── zClassLoaderInfo │
│ │ ├── zLinkerInfo │
│ │ ├── zPackageInfo │
│ │ ├── zSystemSettingInfo │
│ │ ├── zSystemPropInfo │
│ │ ├── zSignatureInfo │
│ │ ├── zPortInfo │
│ │ ├── zTimeInfo │
│ │ ├── zSslInfo │
│ │ ├── zLocalNetworkInfo │
│ │ ├── zLogcatInfo │
│ │ ├── zSideChannelInfo │
│ │ └── zProcInfo │
│ ├── 核心工具类 (zcore模块) │
│ │ ├── zJavaVm (JVM管理) │
│ │ │ └── getEnv (跨线程JNI环境) │
│ │ ├── zLinker (动态链接器) │
│ │ │ ├── find_lib │
│ │ │ ├── get_libpath_list │
│ │ │ └── check_lib_crc │
│ │ ├── zClassLoader (类加载器) │
│ │ │ ├── traverseClassLoader │
│ │ │ ├── checkGlobalRef │
│ │ │ └── checkWeakGlobalRef │
│ │ ├── zHttps (HTTPS客户端) │
│ │ │ ├── performRequest │
│ │ │ └── verifyCertificatePinning │
│ │ ├── zTee (TEE检测) │
│ │ │ └── parse_tee_certificate │
│ │ ├── zFile (文件操作) │
│ │ ├── zElf (ELF解析) │
│ │ ├── zJson (JSON处理) │
│ │ ├── zCrc32 (CRC校验) │
│ │ ├── zThreadPool (线程池) │
│ │ └── zLog (日志记录) │
│ └── 基础库层 (zlibc/zstd模块) │
│ ├── syscall.h (自定义系统调用) │
│ ├── zLibc (自定义libc函数) │
│ ├── zStdString (自定义string) │
│ ├── zStdVector (自定义vector) │
│ └── zStdMap (自定义map) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Java层 (UI层) │
│ ├── MainActivity │
│ │ └── onCardInfoUpdated (静态回调) │
│ ├── InfoCardContainer │
│ ├── InfoCard │
│ └── MainApplication │
│ └── System.loadLibrary("overt") │
├─────────────────────────────────────┤
│ JNI桥接层 │
│ ├── native-lib.cpp │
│ │ ├── init_ (constructor) │
│ │ └── JNI_OnLoad │
│ └── zManager (设备信息管理中心) │
│ ├── round_tasks (周期性任务) │
│ └── notice_java (通知Java层) │
├─────────────────────────────────────┤
│ Native C++层 │
│ ├── 检测模块 (z*Info.cpp) │
│ │ ├── zRootStateInfo │
│ │ ├── zTeeInfo │
│ │ ├── zClassLoaderInfo │
│ │ ├── zLinkerInfo │
│ │ ├── zPackageInfo │
│ │ ├── zSystemSettingInfo │
│ │ ├── zSystemPropInfo │
│ │ ├── zSignatureInfo │
│ │ ├── zPortInfo │
│ │ ├── zTimeInfo │
│ │ ├── zSslInfo │
│ │ ├── zLocalNetworkInfo │
│ │ ├── zLogcatInfo │
│ │ ├── zSideChannelInfo │
│ │ └── zProcInfo │
│ ├── 核心工具类 (zcore模块) │
│ │ ├── zJavaVm (JVM管理) │
│ │ │ └── getEnv (跨线程JNI环境) │
│ │ ├── zLinker (动态链接器) │
│ │ │ ├── find_lib │
│ │ │ ├── get_libpath_list │
│ │ │ └── check_lib_crc │
│ │ ├── zClassLoader (类加载器) │
│ │ │ ├── traverseClassLoader │
│ │ │ ├── checkGlobalRef │
│ │ │ └── checkWeakGlobalRef │
│ │ ├── zHttps (HTTPS客户端) │
│ │ │ ├── performRequest │
│ │ │ └── verifyCertificatePinning │
│ │ ├── zTee (TEE检测) │
│ │ │ └── parse_tee_certificate │
│ │ ├── zFile (文件操作) │
│ │ ├── zElf (ELF解析) │
│ │ ├── zJson (JSON处理) │
│ │ ├── zCrc32 (CRC校验) │
│ │ ├── zThreadPool (线程池) │
│ │ └── zLog (日志记录) │
│ └── 基础库层 (zlibc/zstd模块) │
│ ├── syscall.h (自定义系统调用) │
│ ├── zLibc (自定义libc函数) │
│ ├── zStdString (自定义string) │
│ ├── zStdVector (自定义vector) │
│ └── zStdMap (自定义map) │
└─────────────────────────────────────┘
#define __asm_syscall(...) \
__asm__ __volatile__("svc #0" \
: "=r"(x0) : __VA_ARGS__ : "memory", "cc")
__attribute__((always_inline))
static inline long __syscall4(long n, long a, long b, long c, long d) {
register long x8 __asm__("x8") = n;
register long x0 __asm__("x0") = a;
register long x1 __asm__("x1") = b;
register long x2 __asm__("x2") = c;
register long x3 __asm__("x3") = d;
__asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3));
return x0;
}
#define SYS_openat 56
#define SYS_stat 4
#define SYS_access 21
#define SYS_readlink 89
#define SYS_newfstatat 79
#define __asm_syscall(...) \
__asm__ __volatile__("svc #0" \
: "=r"(x0) : __VA_ARGS__ : "memory", "cc")
__attribute__((always_inline))
static inline long __syscall4(long n, long a, long b, long c, long d) {
register long x8 __asm__("x8") = n;
register long x0 __asm__("x0") = a;
register long x1 __asm__("x1") = b;
register long x2 __asm__("x2") = c;
register long x3 __asm__("x3") = d;
__asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3));
return x0;
}
#define SYS_openat 56
#define SYS_stat 4
#define SYS_access 21
#define SYS_readlink 89
#define SYS_newfstatat 79
int nonstd_open(const char *pathname, int flags, ...) {
LOGE("nonstd_open called: pathname='%s', flags=0x%x", pathname ? pathname : "NULL", flags);
if (!pathname) {
LOGV("open: NULL pathname");
return -1;
}
mode_t mode = 0;
if (flags & O_CREAT) {
va_list args;
va_start(args, flags);
mode = va_arg(args, mode_t);
va_end(args);
}
int fd = (int)__syscall4(SYS_openat, AT_FDCWD, (long)pathname, flags, mode);
LOGV("open: returned fd=%d", fd);
return fd;
}
int nonstd_access(const char* __path, int __mode) {
LOGV("nonstd_access called: path='%s', mode=0x%x", __path ? __path : "NULL", __mode);
if (!__path) {
LOGV("access: NULL path");
errno = EFAULT;
return -1;
}
long result = __syscall2(SYS_access, (uintptr_t)__path, (long)__mode);
if (result < 0) {
errno = -result;
LOGV("access: failed, errno=%d", errno);
return -1;
}
LOGV("access: success");
return 0;
}
int nonstd_open(const char *pathname, int flags, ...) {
LOGE("nonstd_open called: pathname='%s', flags=0x%x", pathname ? pathname : "NULL", flags);
if (!pathname) {
LOGV("open: NULL pathname");
return -1;
}
mode_t mode = 0;
if (flags & O_CREAT) {
va_list args;
va_start(args, flags);
mode = va_arg(args, mode_t);
va_end(args);
}
int fd = (int)__syscall4(SYS_openat, AT_FDCWD, (long)pathname, flags, mode);
LOGV("open: returned fd=%d", fd);
return fd;
}
int nonstd_access(const char* __path, int __mode) {
LOGV("nonstd_access called: path='%s', mode=0x%x", __path ? __path : "NULL", __mode);
if (!__path) {
LOGV("access: NULL path");
errno = EFAULT;
return -1;
}
long result = __syscall2(SYS_access, (uintptr_t)__path, (long)__mode);
if (result < 0) {
errno = -result;
LOGV("access: failed, errno=%d", errno);
return -1;
}
LOGV("access: success");
return 0;
}
map<string, map<string, string>> get_root_state_info(){
LOGD("get_root_file_info called");
map<string, map<string, string>> info;
const char* paths[] = {
"/sbin/su",
"/system/bin/su",
"/system/xbin/su",
"/data/local/xbin/su",
"/data/local/bin/su",
"/system/sd/xbin/su",
"/system/bin/failsafe/su",
"/data/local/su",
"/system/xbin/mu",
"/system_ext/bin/su",
"/apex/com.android.runtime/bin/suu",
};
for (const char* path : paths) {
LOGI("Checking path: %s", path);
zFile file(path);
if(file.exists()){
LOGI("Black file exists: %s", path);
info[path]["risk"] = "error";
info[path]["explain"] = "black file but exist";
}
}
return info;
}
map<string, map<string, string>> get_root_state_info(){
LOGD("get_root_file_info called");
map<string, map<string, string>> info;
const char* paths[] = {
"/sbin/su",
"/system/bin/su",
"/system/xbin/su",
"/data/local/xbin/su",
"/data/local/bin/su",
"/system/sd/xbin/su",
"/system/bin/failsafe/su",
"/data/local/su",
"/system/xbin/mu",
"/system_ext/bin/su",
"/apex/com.android.runtime/bin/suu",
};
for (const char* path : paths) {
LOGI("Checking path: %s", path);
zFile file(path);
if(file.exists()){
LOGI("Black file exists: %s", path);
info[path]["risk"] = "error";
info[path]["explain"] = "black file but exist";
}
}
return info;
}
zJavaVm::zJavaVm() {
LOGD("Constructor called");
zElf libart = zLinker::getInstance()->find_lib("libart.so");
auto *JNI_GetCreatedJavaVMs = (jint (*)(JavaVM **, jsize, jsize *))libart.find_symbol("JNI_GetCreatedJavaVMs");
LOGI("JNI_GetCreatedJavaVMs: %p", JNI_GetCreatedJavaVMs);
if (JNI_GetCreatedJavaVMs == nullptr) {
LOGE("GetCreatedJavaVMs not found");
return;
}
JavaVM* vms[10];
jsize num_vms = 0;
if (JNI_GetCreatedJavaVMs(vms, 1, &num_vms) != JNI_OK || num_vms == 0) {
LOGE("GetCreatedJavaVMs failed");
return;
}
LOGI("GetCreatedJavaVMs num_vms %d", num_vms);
jvm = vms[0];
LOGI("JVM initialized successfully");
}
zJavaVm::zJavaVm() {
LOGD("Constructor called");
zElf libart = zLinker::getInstance()->find_lib("libart.so");
auto *JNI_GetCreatedJavaVMs = (jint (*)(JavaVM **, jsize, jsize *))libart.find_symbol("JNI_GetCreatedJavaVMs");
LOGI("JNI_GetCreatedJavaVMs: %p", JNI_GetCreatedJavaVMs);
if (JNI_GetCreatedJavaVMs == nullptr) {
LOGE("GetCreatedJavaVMs not found");
return;
}
JavaVM* vms[10];
jsize num_vms = 0;
if (JNI_GetCreatedJavaVMs(vms, 1, &num_vms) != JNI_OK || num_vms == 0) {
LOGE("GetCreatedJavaVMs failed");
return;
}
LOGI("GetCreatedJavaVMs num_vms %d", num_vms);
jvm = vms[0];
LOGI("JVM initialized successfully");
}
JNIEnv* zJavaVm::getEnv(){
LOGI("getEnv is called");
if(jvm == nullptr){
LOGE("JVM is not initialized");
return nullptr;
}
int tid = __syscall0(SYS_gettid);
{
std::lock_guard<std::mutex> lock(env_map_mutex);
auto it = thread_env_map.find(tid);
if (it != thread_env_map.end()) {
LOGI("getEnv: Found existing JNIEnv for thread %p", it->second);
return it->second;
}
}
JNIEnv* env = nullptr;
if (jvm->AttachCurrentThread((JNIEnv **) &env, nullptr) != JNI_OK) {
LOGE("Failed to attach current thread to JVM");
return nullptr;
}
{
std::lock_guard<std::mutex> lock(env_map_mutex);
thread_env_map[tid] = env;
LOGI("getEnv: Created new JNIEnv %p for thread %d", env, tid);
}
return env;
}
JNIEnv* zJavaVm::getEnv(){
LOGI("getEnv is called");
if(jvm == nullptr){
LOGE("JVM is not initialized");
return nullptr;
}
int tid = __syscall0(SYS_gettid);
{
std::lock_guard<std::mutex> lock(env_map_mutex);
auto it = thread_env_map.find(tid);
if (it != thread_env_map.end()) {
LOGI("getEnv: Found existing JNIEnv for thread %p", it->second);
return it->second;
}
}
JNIEnv* env = nullptr;
if (jvm->AttachCurrentThread((JNIEnv **) &env, nullptr) != JNI_OK) {
LOGE("Failed to attach current thread to JVM");
return nullptr;
}
{
std::lock_guard<std::mutex> lock(env_map_mutex);
thread_env_map[tid] = env;
LOGI("getEnv: Created new JNIEnv %p for thread %d", env, tid);
}
return env;
}
jobject createNewContext(JNIEnv* env) {
LOGD("createNewContext called");
if (env == nullptr) {
LOGD("createNewContext: env is null");
return nullptr;
}
jclass clsActivityThread = env->FindClass("android/app/ActivityThread");
if (clsActivityThread == nullptr) {
LOGE("Failed to find ActivityThread class");
return nullptr;
}
jmethodID m_currentAT = env->GetStaticMethodID(clsActivityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
if (m_currentAT == nullptr) {
LOGE("Failed to find currentActivityThread method");
return nullptr;
}
jobject at = env->CallStaticObjectMethod(clsActivityThread, m_currentAT);
if (at == nullptr) {
LOGE("Failed to get current ActivityThread");
return nullptr;
}
jfieldID fid_mBoundApp = env->GetFieldID(clsActivityThread, "mBoundApplication", "Landroid/app/ActivityThread$AppBindData;");
if (fid_mBoundApp == nullptr) {
LOGE("Failed to find mBoundApplication field");
env->DeleteLocalRef(at);
return nullptr;
}
jobject mBoundApp = env->GetObjectField(at, fid_mBoundApp);
if (mBoundApp == nullptr) {
LOGE("Failed to get mBoundApplication");
env->DeleteLocalRef(at);
return nullptr;
}
jclass clsAppBindData = env->FindClass("android/app/ActivityThread$AppBindData");
if (clsAppBindData == nullptr) {
LOGE("Failed to find AppBindData class");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
return nullptr;
}
jfieldID fid_info = env->GetFieldID(clsAppBindData, "info", "Landroid/app/LoadedApk;");
if (fid_info == nullptr) {
LOGE("Failed to find info field");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
return nullptr;
}
jobject loadedApk = env->GetObjectField(mBoundApp, fid_info);
if (loadedApk == nullptr) {
LOGE("Failed to get LoadedApk");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
return nullptr;
}
jclass clsLoadedApk = env->FindClass("android/app/LoadedApk");
if (clsLoadedApk == nullptr) {
LOGE("Failed to find LoadedApk class");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
return nullptr;
}
jmethodID m_makeApp = env->GetMethodID(clsLoadedApk, "makeApplication", "(ZLandroid/app/Instrumentation;)Landroid/app/Application;");
if (m_makeApp == nullptr) {
LOGE("Failed to find makeApplication method");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
return nullptr;
}
jobject app = env->CallObjectMethod(loadedApk, m_makeApp, JNI_FALSE, nullptr);
if (app == nullptr) {
LOGE("Failed to create Application instance");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
return nullptr;
}
jclass clsApp = env->GetObjectClass(app);
if (clsApp == nullptr) {
LOGE("Failed to get Application class");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
env->DeleteLocalRef(app);
return nullptr;
}
jmethodID m_getBaseContext = env->GetMethodID(clsApp, "getBaseContext", "()Landroid/content/Context;");
if (m_getBaseContext == nullptr) {
LOGE("Failed to find getBaseContext method");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
env->DeleteLocalRef(app);
return nullptr;
}
jobject context = env->CallObjectMethod(app, m_getBaseContext);
if (context == nullptr) {
LOGE("Failed to get base context");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
env->DeleteLocalRef(app);
return nullptr;
}
return context;
}
jobject createNewContext(JNIEnv* env) {
LOGD("createNewContext called");
if (env == nullptr) {
LOGD("createNewContext: env is null");
return nullptr;
}
jclass clsActivityThread = env->FindClass("android/app/ActivityThread");
if (clsActivityThread == nullptr) {
LOGE("Failed to find ActivityThread class");
return nullptr;
}
jmethodID m_currentAT = env->GetStaticMethodID(clsActivityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
if (m_currentAT == nullptr) {
LOGE("Failed to find currentActivityThread method");
return nullptr;
}
jobject at = env->CallStaticObjectMethod(clsActivityThread, m_currentAT);
if (at == nullptr) {
LOGE("Failed to get current ActivityThread");
return nullptr;
}
jfieldID fid_mBoundApp = env->GetFieldID(clsActivityThread, "mBoundApplication", "Landroid/app/ActivityThread$AppBindData;");
if (fid_mBoundApp == nullptr) {
LOGE("Failed to find mBoundApplication field");
env->DeleteLocalRef(at);
return nullptr;
}
jobject mBoundApp = env->GetObjectField(at, fid_mBoundApp);
if (mBoundApp == nullptr) {
LOGE("Failed to get mBoundApplication");
env->DeleteLocalRef(at);
return nullptr;
}
jclass clsAppBindData = env->FindClass("android/app/ActivityThread$AppBindData");
if (clsAppBindData == nullptr) {
LOGE("Failed to find AppBindData class");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
return nullptr;
}
jfieldID fid_info = env->GetFieldID(clsAppBindData, "info", "Landroid/app/LoadedApk;");
if (fid_info == nullptr) {
LOGE("Failed to find info field");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
return nullptr;
}
jobject loadedApk = env->GetObjectField(mBoundApp, fid_info);
if (loadedApk == nullptr) {
LOGE("Failed to get LoadedApk");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
return nullptr;
}
jclass clsLoadedApk = env->FindClass("android/app/LoadedApk");
if (clsLoadedApk == nullptr) {
LOGE("Failed to find LoadedApk class");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
return nullptr;
}
jmethodID m_makeApp = env->GetMethodID(clsLoadedApk, "makeApplication", "(ZLandroid/app/Instrumentation;)Landroid/app/Application;");
if (m_makeApp == nullptr) {
LOGE("Failed to find makeApplication method");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
return nullptr;
}
jobject app = env->CallObjectMethod(loadedApk, m_makeApp, JNI_FALSE, nullptr);
if (app == nullptr) {
LOGE("Failed to create Application instance");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
return nullptr;
}
jclass clsApp = env->GetObjectClass(app);
if (clsApp == nullptr) {
LOGE("Failed to get Application class");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
env->DeleteLocalRef(app);
return nullptr;
}
jmethodID m_getBaseContext = env->GetMethodID(clsApp, "getBaseContext", "()Landroid/content/Context;");
if (m_getBaseContext == nullptr) {
LOGE("Failed to find getBaseContext method");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
env->DeleteLocalRef(app);
return nullptr;
}
jobject context = env->CallObjectMethod(app, m_getBaseContext);
if (context == nullptr) {
LOGE("Failed to get base context");
env->DeleteLocalRef(at);
env->DeleteLocalRef(mBoundApp);
env->DeleteLocalRef(loadedApk);
env->DeleteLocalRef(app);
return nullptr;
}
return context;
}
vector<string> getClassNameList(JNIEnv *env, jobject classloader) {
LOGD("getClassNameList called");
vector<string> classNameList = {};
jclass classloaderClass = env->GetObjectClass(classloader);
jfieldID pathListFieldID = env->GetFieldID(classloaderClass, "pathList","Ldalvik/system/DexPathList;");
if (pathListFieldID == nullptr) {
LOGE("Failed to find field 'pathList' in classloader");
return classNameList;
}
jobject pathList = env->GetObjectField(classloader, pathListFieldID);
if (pathList == nullptr) {
LOGE("pathList is null");
return classNameList;
}
jclass dexPathListClass = env->GetObjectClass(pathList);
jfieldID dexElementsFieldID = env->GetFieldID(dexPathListClass, "dexElements",
"[Ldalvik/system/DexPathList$Element;");
if (dexElementsFieldID == nullptr) {
LOGE("Failed to find field 'dexElements' in DexPathList");
return classNameList;
}
jobjectArray dexElements = (jobjectArray) env->GetObjectField(pathList, dexElementsFieldID);
if (dexElements == nullptr) {
LOGE("dexElements is null");
return classNameList;
}
jint dexElementsLength = env->GetArrayLength(dexElements);
for (jint i = 0; i < dexElementsLength; i++) {
jobject dexElement = env->GetObjectArrayElement(dexElements, i);
jclass dexElementClass = env->GetObjectClass(dexElement);
jfieldID dexFileFieldID = env->GetFieldID(dexElementClass, "dexFile",
"Ldalvik/system/DexFile;");
if (dexFileFieldID == nullptr) {
LOGE("Failed to find field 'dexFile' in DexPathList$Element");
continue;
}
jobject dexFile = env->GetObjectField(dexElement, dexFileFieldID);
if (dexFile == nullptr) {
LOGE("dexFile is null");
continue;
}
jclass dexFileClass = env->GetObjectClass(dexFile);
jmethodID entriesMethodID = env->GetMethodID(dexFileClass, "entries", "()Ljava/util/Enumeration;");
if (entriesMethodID == nullptr) {
LOGE("Failed to find method 'entries' in DexFile");
continue;
}
jobject entries = env->CallObjectMethod(dexFile, entriesMethodID);
if (entries == nullptr) {
LOGE("entries is null");
continue;
}
jclass enumerationClass = env->FindClass("java/util/Enumeration");
jmethodID hasMoreElementsMethodID = env->GetMethodID(enumerationClass, "hasMoreElements", "()Z");
jmethodID nextElementMethodID = env->GetMethodID(enumerationClass, "nextElement","()Ljava/lang/Object;");
while (env->CallBooleanMethod(entries, hasMoreElementsMethodID)) {
jstring className = (jstring) env->CallObjectMethod(entries, nextElementMethodID);
const char *classNameStr = env->GetStringUTFChars(className, nullptr);
classNameList.push_back(classNameStr);
env->ReleaseStringUTFChars(className, classNameStr);
env->DeleteLocalRef(className);
}
env->DeleteLocalRef(entries);
env->DeleteLocalRef(dexFile);
env->DeleteLocalRef(dexElement);
env->DeleteLocalRef(dexElementClass);
env->DeleteLocalRef(dexFileClass);
env->DeleteLocalRef(enumerationClass);
}
env->DeleteLocalRef(dexElements);
env->DeleteLocalRef(pathList);
env->DeleteLocalRef(classloaderClass);
return classNameList;
}
vector<string> getClassNameList(JNIEnv *env, jobject classloader) {
LOGD("getClassNameList called");
vector<string> classNameList = {};
jclass classloaderClass = env->GetObjectClass(classloader);
jfieldID pathListFieldID = env->GetFieldID(classloaderClass, "pathList","Ldalvik/system/DexPathList;");
if (pathListFieldID == nullptr) {
LOGE("Failed to find field 'pathList' in classloader");
return classNameList;
}
jobject pathList = env->GetObjectField(classloader, pathListFieldID);
if (pathList == nullptr) {
LOGE("pathList is null");
return classNameList;
}
jclass dexPathListClass = env->GetObjectClass(pathList);
jfieldID dexElementsFieldID = env->GetFieldID(dexPathListClass, "dexElements",
"[Ldalvik/system/DexPathList$Element;");
if (dexElementsFieldID == nullptr) {
LOGE("Failed to find field 'dexElements' in DexPathList");
return classNameList;
}
jobjectArray dexElements = (jobjectArray) env->GetObjectField(pathList, dexElementsFieldID);
if (dexElements == nullptr) {
LOGE("dexElements is null");
return classNameList;
}
jint dexElementsLength = env->GetArrayLength(dexElements);
for (jint i = 0; i < dexElementsLength; i++) {
jobject dexElement = env->GetObjectArrayElement(dexElements, i);
jclass dexElementClass = env->GetObjectClass(dexElement);
jfieldID dexFileFieldID = env->GetFieldID(dexElementClass, "dexFile",
"Ldalvik/system/DexFile;");
if (dexFileFieldID == nullptr) {
LOGE("Failed to find field 'dexFile' in DexPathList$Element");
continue;
}
jobject dexFile = env->GetObjectField(dexElement, dexFileFieldID);
if (dexFile == nullptr) {
LOGE("dexFile is null");
continue;
}
jclass dexFileClass = env->GetObjectClass(dexFile);
jmethodID entriesMethodID = env->GetMethodID(dexFileClass, "entries", "()Ljava/util/Enumeration;");
if (entriesMethodID == nullptr) {
LOGE("Failed to find method 'entries' in DexFile");
continue;
}
jobject entries = env->CallObjectMethod(dexFile, entriesMethodID);
if (entries == nullptr) {
LOGE("entries is null");
continue;
}
jclass enumerationClass = env->FindClass("java/util/Enumeration");
jmethodID hasMoreElementsMethodID = env->GetMethodID(enumerationClass, "hasMoreElements", "()Z");
jmethodID nextElementMethodID = env->GetMethodID(enumerationClass, "nextElement","()Ljava/lang/Object;");
while (env->CallBooleanMethod(entries, hasMoreElementsMethodID)) {
jstring className = (jstring) env->CallObjectMethod(entries, nextElementMethodID);
const char *classNameStr = env->GetStringUTFChars(className, nullptr);
classNameList.push_back(classNameStr);
env->ReleaseStringUTFChars(className, classNameStr);
env->DeleteLocalRef(className);
}
env->DeleteLocalRef(entries);
env->DeleteLocalRef(dexFile);
env->DeleteLocalRef(dexElement);
env->DeleteLocalRef(dexElementClass);
env->DeleteLocalRef(dexFileClass);
env->DeleteLocalRef(enumerationClass);
}
env->DeleteLocalRef(dexElements);
env->DeleteLocalRef(pathList);
env->DeleteLocalRef(classloaderClass);
return classNameList;
}
class ClassLoaderVisitor : public art::SingleRootVisitor {
public:
vector<string> classLoaderStringList;
vector<string> classNameList;
ClassLoaderVisitor(JNIEnv *env, jclass classLoader) : env_(env), classLoader_(classLoader) {
classLoaderStringList = vector<string>();
classNameList = vector<string>();
}
void VisitRoot(art::mirror::Object *root, const art::RootInfo &info ATTRIBUTE_UNUSED) final {
jobject object = newLocalRef(env_, (jobject) root);
if (object != nullptr) {
if (env_->IsInstanceOf(object, classLoader_)) {
string classLoaderName = getClassName((JNIEnv *) env_, object);
LOGD("ClassLoaderVisitor classLoaderName %s", classLoaderName.c_str());
string classLoaderString = get_class_loader_string(env_, object);
LOGD("ClassLoaderVisitor %s", classLoaderString.c_str());
vector<string> classLoaderClassNameList = getClassNameList((JNIEnv *) env_, object);
classLoaderStringList.push_back(classLoaderString);
classNameList.insert(classNameList.end(), classLoaderClassNameList.begin(), classLoaderClassNameList.end());
}else{
deleteLocalRef(env_, object);
}
}
}
private:
JNIEnv *env_;
jclass classLoader_;
};
void zClassLoader::checkGlobalRef(JNIEnv *env, jclass clazz) {
LOGD("checkGlobalRef called");
auto VisitRoots = (void (*)(void *, void *)) zLinker::getInstance()->find_lib("libart.so").find_symbol("_ZN3art9JavaVMExt10VisitRootsEPNS_11RootVisitorE");
if (VisitRoots == nullptr) {
LOGE("Failed to find method 'VisitRoots' in JavaVMExt");
return;
}
JavaVM *jvm;
env->GetJavaVM(&jvm);
ClassLoaderVisitor visitor(env, clazz);
VisitRoots(jvm, &visitor);
classLoaderStringList.insert(classLoaderStringList.end(), visitor.classLoaderStringList.begin(), visitor.classLoaderStringList.end());
classNameList.insert(classNameList.end(), visitor.classNameList.begin(), visitor.classNameList.end());
LOGI("ClassLoaderVisitor classLoaderStringList size %zu", visitor.classLoaderStringList.size());
}
class ClassLoaderVisitor : public art::SingleRootVisitor {
public:
vector<string> classLoaderStringList;
vector<string> classNameList;
ClassLoaderVisitor(JNIEnv *env, jclass classLoader) : env_(env), classLoader_(classLoader) {
classLoaderStringList = vector<string>();
classNameList = vector<string>();
}
void VisitRoot(art::mirror::Object *root, const art::RootInfo &info ATTRIBUTE_UNUSED) final {
jobject object = newLocalRef(env_, (jobject) root);
if (object != nullptr) {
if (env_->IsInstanceOf(object, classLoader_)) {
string classLoaderName = getClassName((JNIEnv *) env_, object);
LOGD("ClassLoaderVisitor classLoaderName %s", classLoaderName.c_str());
string classLoaderString = get_class_loader_string(env_, object);
LOGD("ClassLoaderVisitor %s", classLoaderString.c_str());
vector<string> classLoaderClassNameList = getClassNameList((JNIEnv *) env_, object);
classLoaderStringList.push_back(classLoaderString);
classNameList.insert(classNameList.end(), classLoaderClassNameList.begin(), classLoaderClassNameList.end());
}else{
deleteLocalRef(env_, object);
}
}
}
private:
JNIEnv *env_;
jclass classLoader_;
};
void zClassLoader::checkGlobalRef(JNIEnv *env, jclass clazz) {
LOGD("checkGlobalRef called");
auto VisitRoots = (void (*)(void *, void *)) zLinker::getInstance()->find_lib("libart.so").find_symbol("_ZN3art9JavaVMExt10VisitRootsEPNS_11RootVisitorE");
if (VisitRoots == nullptr) {
LOGE("Failed to find method 'VisitRoots' in JavaVMExt");
return;
}
JavaVM *jvm;
env->GetJavaVM(&jvm);
ClassLoaderVisitor visitor(env, clazz);
VisitRoots(jvm, &visitor);
classLoaderStringList.insert(classLoaderStringList.end(), visitor.classLoaderStringList.begin(), visitor.classLoaderStringList.end());
classNameList.insert(classNameList.end(), visitor.classNameList.begin(), visitor.classNameList.end());
LOGI("ClassLoaderVisitor classLoaderStringList size %zu", visitor.classLoaderStringList.size());
}
zLinker::zLinker() {
LOGD("Constructor called");
this->elf_file_ptr = parse_elf_file("/system/bin/linker64");
LOGI("linker64 elf_file_ptr %p", this->elf_file_ptr);
parse_elf_head();
parse_program_header_table();
parse_section_table();
this->elf_mem_ptr = get_maps_base("linker64");
LOGI("linker64 elf_mem_ptr %p", this->elf_mem_ptr);
soinfo*(*solist_get_head)() = (soinfo*(*)())(this->find_symbol("__dl__Z15solist_get_headv"));
LOGI("linker64 solist_get_head %p", solist_get_head);
soinfo_head = solist_get_head();
LOGI("soinfo_head %p", soinfo_head);
soinfo_get_realpath = (char*(*)(void*))(this->find_symbol("__dl__ZNK6soinfo12get_realpathEv"));
LOGI("soinfo_get_realpath %p", soinfo_get_realpath);
}
zLinker::zLinker() {
LOGD("Constructor called");
this->elf_file_ptr = parse_elf_file("/system/bin/linker64");
LOGI("linker64 elf_file_ptr %p", this->elf_file_ptr);
parse_elf_head();
parse_program_header_table();
parse_section_table();
this->elf_mem_ptr = get_maps_base("linker64");
LOGI("linker64 elf_mem_ptr %p", this->elf_mem_ptr);
soinfo*(*solist_get_head)() = (soinfo*(*)())(this->find_symbol("__dl__Z15solist_get_headv"));
LOGI("linker64 solist_get_head %p", solist_get_head);
soinfo_head = solist_get_head();
LOGI("soinfo_head %p", soinfo_head);
soinfo_get_realpath = (char*(*)(void*))(this->find_symbol("__dl__ZNK6soinfo12get_realpathEv"));
LOGI("soinfo_get_realpath %p", soinfo_get_realpath);
}
vector<string> zLinker::get_libpath_list(){
LOGD("get_libpath_list called");
vector<string> libpath_list = vector<string>();
soinfo* soinfo = soinfo_head;
while(soinfo->next != nullptr){
char* real_path = soinfo_get_realpath(soinfo);
libpath_list.push_back(real_path);
soinfo = soinfo->next;
}
LOGI("get_libpath_list: found %zu libraries", libpath_list.size());
return libpath_list;
}
vector<string> zLinker::get_libpath_list(){
LOGD("get_libpath_list called");
vector<string> libpath_list = vector<string>();
soinfo* soinfo = soinfo_head;
while(soinfo->next != nullptr){
char* real_path = soinfo_get_realpath(soinfo);
libpath_list.push_back(real_path);
soinfo = soinfo->next;
}
LOGI("get_libpath_list: found %zu libraries", libpath_list.size());
return libpath_list;
}
bool zLinker::check_lib_crc(const char* so_name){
LOGD("check_lib_crc called with so_name: %s", so_name);
LOGI("check_lib_hash so_name: %s", so_name);
zElf elf_lib_file = zLinker::getInstance()->find_lib(so_name);
LOGI("zElf elf_lib_file = zLinker::getInstance()->find_lib(so_name);");
uint64_t elf_lib_file_crc = elf_lib_file.get_elf_header_crc() +
elf_lib_file.get_program_header_crc() +
elf_lib_file.get_text_segment_crc();
LOGI("check_lib_hash elf_lib_file: %p crc: %lu", elf_lib_file.elf_file_ptr, elf_lib_file_crc);
zElf elf_lib_mem = zElf((void*)zLinker::get_maps_base(so_name));
uint64_t elf_lib_mem_crc = elf_lib_mem.get_elf_header_crc() +
elf_lib_mem.get_program_header_crc() +
elf_lib_mem.get_text_segment_crc();
LOGI("check_lib_hash elf_lib_mem: %p crc: %lu", elf_lib_mem.elf_mem_ptr, elf_lib_mem_crc);
bool crc_mismatch = elf_lib_file_crc != elf_lib_mem_crc;
if (crc_mismatch) {
LOGW("check_lib_crc: CRC mismatch detected for %s", so_name);
} else {
LOGI("check_lib_crc: CRC match for %s", so_name);
}
return crc_mismatch;
}
bool zLinker::check_lib_crc(const char* so_name){
LOGD("check_lib_crc called with so_name: %s", so_name);
LOGI("check_lib_hash so_name: %s", so_name);
zElf elf_lib_file = zLinker::getInstance()->find_lib(so_name);
LOGI("zElf elf_lib_file = zLinker::getInstance()->find_lib(so_name);");
uint64_t elf_lib_file_crc = elf_lib_file.get_elf_header_crc() +
elf_lib_file.get_program_header_crc() +
elf_lib_file.get_text_segment_crc();
LOGI("check_lib_hash elf_lib_file: %p crc: %lu", elf_lib_file.elf_file_ptr, elf_lib_file_crc);
zElf elf_lib_mem = zElf((void*)zLinker::get_maps_base(so_name));
uint64_t elf_lib_mem_crc = elf_lib_mem.get_elf_header_crc() +
elf_lib_mem.get_program_header_crc() +
elf_lib_mem.get_text_segment_crc();
LOGI("check_lib_hash elf_lib_mem: %p crc: %lu", elf_lib_mem.elf_mem_ptr, elf_lib_mem_crc);
bool crc_mismatch = elf_lib_file_crc != elf_lib_mem_crc;
if (crc_mismatch) {
LOGW("check_lib_crc: CRC mismatch detected for %s", so_name);
} else {
LOGI("check_lib_crc: CRC match for %s", so_name);
}
return crc_mismatch;
}
HttpsResponse zHttps::performRequest(const HttpsRequest& request) {
HttpsResponse response;
int timeout_seconds = request.timeout_seconds > 0 ? request.timeout_seconds : default_timeout_seconds;
RequestTimer timer(timeout_seconds);
RequestResources resources;
LOGI("Starting HTTPS request with timeout: %d seconds", timer.timeout_seconds);
if (request.url.substr(0, 8) != "https://") {
response.error_message = "Only HTTPS URLs are supported";
LOGE("Security Error: Only HTTPS protocol is allowed");
return response;
}
if (!initialized) {
if (!initialize()) {
response.error_message = "Failed to initialize mbedtls";
LOGE("Failed to initialize mbedtls");
return response;
}
}
LOGI("Starting new HTTPS request to %s", request.host.c_str());
if (initialized) {
cleanup();
if (!initialize()) {
response.error_message = "Failed to reinitialize mbedtls";
LOGE("Failed to reinitialize mbedtls");
return response;
}
}
auto it = pinned_certificates.find(request.host);
if (it != pinned_certificates.end()) {
response.pinned_certificate = it->second;
}
char error_buf[0x1000];
int ret;
LOGI("Connecting to %s:%d with custom socket...", request.host.c_str(), request.port);
if (timer.isTimeout()) {
response.error_message = "Connection timeout before establishing connection";
LOGE("Connection timeout before establishing connection");
return response;
}
time_t connect_start = time(nullptr);
LOGI("Connection attempt started at: %ld", connect_start);
resources.sockfd = connectWithTimeout(request.host, request.port, timeout_seconds);
time_t connect_end = time(nullptr);
int connect_duration = connect_end - connect_start;
LOGI("Connection attempt completed in %d seconds", connect_duration);
if (resources.sockfd < 0) {
response.error_message = "Connection failed with custom socket";
LOGE("Connection failed after %d seconds with custom socket", connect_duration);
return response;
}
resources.server_fd.fd = resources.sockfd;
timer.markConnection();
mbedtls_ssl_config_init(&resources.conf);
ret = mbedtls_ssl_config_defaults(&resources.conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
mbedtls_ssl_conf_authmode(&resources.conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&resources.conf, &cacert, nullptr);
mbedtls_ssl_conf_min_version(&resources.conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
ret = mbedtls_ssl_handshake(&resources.ssl);
if (ret != 0) {
mbedtls_strerror(ret, error_buf, sizeof(error_buf));
response.error_message = "TLS handshake failed: " + string(error_buf);
return response;
}
uint32_t flags = mbedtls_ssl_get_verify_result(&resources.ssl);
if (flags != 0) {
response.ssl_verification_passed = false;
} else {
response.ssl_verification_passed = true;
}
const mbedtls_x509_crt* cert = mbedtls_ssl_get_peer_cert(&resources.ssl);
if (cert) {
response.certificate = extractCertificateInfo(cert);
response.certificate_pinning_passed = verifyCertificatePinning(cert, request.host);
}
return response;
}
HttpsResponse zHttps::performRequest(const HttpsRequest& request) {
HttpsResponse response;
int timeout_seconds = request.timeout_seconds > 0 ? request.timeout_seconds : default_timeout_seconds;
RequestTimer timer(timeout_seconds);
RequestResources resources;
LOGI("Starting HTTPS request with timeout: %d seconds", timer.timeout_seconds);
if (request.url.substr(0, 8) != "https://") {
response.error_message = "Only HTTPS URLs are supported";
LOGE("Security Error: Only HTTPS protocol is allowed");
return response;
}
if (!initialized) {
if (!initialize()) {
response.error_message = "Failed to initialize mbedtls";
LOGE("Failed to initialize mbedtls");
return response;
}
}
LOGI("Starting new HTTPS request to %s", request.host.c_str());
if (initialized) {
cleanup();
if (!initialize()) {
response.error_message = "Failed to reinitialize mbedtls";
LOGE("Failed to reinitialize mbedtls");
return response;
}
}
auto it = pinned_certificates.find(request.host);
if (it != pinned_certificates.end()) {
response.pinned_certificate = it->second;
}
char error_buf[0x1000];
int ret;
LOGI("Connecting to %s:%d with custom socket...", request.host.c_str(), request.port);
if (timer.isTimeout()) {
response.error_message = "Connection timeout before establishing connection";
LOGE("Connection timeout before establishing connection");
return response;
}
time_t connect_start = time(nullptr);
LOGI("Connection attempt started at: %ld", connect_start);
resources.sockfd = connectWithTimeout(request.host, request.port, timeout_seconds);
time_t connect_end = time(nullptr);
int connect_duration = connect_end - connect_start;
LOGI("Connection attempt completed in %d seconds", connect_duration);
if (resources.sockfd < 0) {
response.error_message = "Connection failed with custom socket";
LOGE("Connection failed after %d seconds with custom socket", connect_duration);
return response;
}
resources.server_fd.fd = resources.sockfd;
timer.markConnection();
mbedtls_ssl_config_init(&resources.conf);
ret = mbedtls_ssl_config_defaults(&resources.conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
mbedtls_ssl_conf_authmode(&resources.conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&resources.conf, &cacert, nullptr);
mbedtls_ssl_conf_min_version(&resources.conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
ret = mbedtls_ssl_handshake(&resources.ssl);
if (ret != 0) {
mbedtls_strerror(ret, error_buf, sizeof(error_buf));
response.error_message = "TLS handshake failed: " + string(error_buf);
return response;
}
uint32_t flags = mbedtls_ssl_get_verify_result(&resources.ssl);
if (flags != 0) {
response.ssl_verification_passed = false;
} else {
response.ssl_verification_passed = true;
}
const mbedtls_x509_crt* cert = mbedtls_ssl_get_peer_cert(&resources.ssl);
if (cert) {
response.certificate = extractCertificateInfo(cert);
response.certificate_pinning_passed = verifyCertificatePinning(cert, request.host);
}
return response;
}
bool zHttps::verifyCertificatePinning(const mbedtls_x509_crt* cert, const string& hostname) {
LOGD("verifyCertificatePinning called for hostname: %s", hostname.c_str());
auto it = pinned_certificates.find(hostname);
if (it == pinned_certificates.end()) {
LOGI("No pinned certificate for hostname: %s, skipping pinning check.", hostname.c_str());
return true;
}
const CertificateInfo& pinned = it->second;
bool serial_match = (pinned.serial_number == extractCertificateInfo(cert).serial_number);
bool fingerprint_match = (pinned.fingerprint_sha256 == extractCertificateInfo(cert).fingerprint_sha256);
bool subject_match = true;
if (!pinned.subject.empty()) {
subject_match = (pinned.subject == extractCertificateInfo(cert).subject);
}
if (!serial_match || !fingerprint_match || !subject_match) {
LOGW("Certificate pinning verification failed for %s", hostname.c_str());
LOGD("serial_match %d fingerprint_match %d subject_match %d ", serial_match, fingerprint_match, subject_match);
if (!serial_match) {
LOGD("Expected serial: %s, Got: %s",
pinned.serial_number.c_str(),
extractCertificateInfo(cert).serial_number.c_str());
}
if (!fingerprint_match) {
LOGD("Expected fingerprint: %s, Got: %s",
pinned.fingerprint_sha256.c_str(),
extractCertificateInfo(cert).fingerprint_sha256.c_str());
}
if (!subject_match && !pinned.subject.empty()) {
LOGD("Expected subject: %s, Got: %s",
pinned.subject.c_str(),
extractCertificateInfo(cert).subject.c_str());
}
return false;
}
LOGI("Certificate pinning verification passed for %s", hostname.c_str());
return true;
}
bool zHttps::verifyCertificatePinning(const mbedtls_x509_crt* cert, const string& hostname) {
LOGD("verifyCertificatePinning called for hostname: %s", hostname.c_str());
auto it = pinned_certificates.find(hostname);
if (it == pinned_certificates.end()) {
LOGI("No pinned certificate for hostname: %s, skipping pinning check.", hostname.c_str());
return true;
}
const CertificateInfo& pinned = it->second;
bool serial_match = (pinned.serial_number == extractCertificateInfo(cert).serial_number);
bool fingerprint_match = (pinned.fingerprint_sha256 == extractCertificateInfo(cert).fingerprint_sha256);
bool subject_match = true;
if (!pinned.subject.empty()) {
subject_match = (pinned.subject == extractCertificateInfo(cert).subject);
}
if (!serial_match || !fingerprint_match || !subject_match) {
LOGW("Certificate pinning verification failed for %s", hostname.c_str());
LOGD("serial_match %d fingerprint_match %d subject_match %d ", serial_match, fingerprint_match, subject_match);
if (!serial_match) {
LOGD("Expected serial: %s, Got: %s",
pinned.serial_number.c_str(),
extractCertificateInfo(cert).serial_number.c_str());
}
if (!fingerprint_match) {
LOGD("Expected fingerprint: %s, Got: %s",
pinned.fingerprint_sha256.c_str(),
extractCertificateInfo(cert).fingerprint_sha256.c_str());
}
if (!subject_match && !pinned.subject.empty()) {
LOGD("Expected subject: %s, Got: %s",
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2025-11-8 01:13
被简单的简单编辑
,原因: 更新部分内容