安卓编译: 2.native层定制修改(上)

2.1 添加libc库日志打印方法

目的 提供打印函数,方便日志打印以及参数分析

修改步骤 bionic\libc\async_safe\include\async_safe\log.h 新增

scss 复制代码
// 新增libc打印函数
#define SAMUEL_TAG "SamuelHookTagLogC"
#define CLOGI(...) async_safe_format_log(ANDROID_LOG_INFO, SAMUEL_TAG, __VA_ARGS__);
#define CLOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, SAMUEL_TAG, __VA_ARGS__);


创建文件 bionic\libc\bionic\clog.cpp

#include <unistd.h>
#include <async_safe/log.h>
#include <string.h>
#include <stdio.h>
  
  
void clogi(const char* method, const char* info) {
    int pid = getpid();
    if (pid < 2000) {
        return;
    }
 
    char sPid[32] = {0};
    snprintf(sPid, sizeof(sPid), "\"PID\":\"%d\",\"UID\":\"%d\"", pid, getuid());
 
    char sinfo[1024] = {0};
    size_t lenInfo = strlen(info);
    if (lenInfo > 1023) {
        strncpy(sinfo, info, 1023);
        sinfo[1023] = '\0';  // Ensure null-termination
    } else {
        strncpy(sinfo, info, lenInfo);
        sinfo[lenInfo] = '\0';  // Ensure null-termination
    }
 
    CLOGI("{\"Type\":\"NativeLog\",%s,\"Info\":\"<%s>:%s\"}", sPid, method, sinfo);
}
  
void cloge(const char* method, const char* info){
  int pid = getpid();
  if (pid < 2000){
    return;
  }
   
  char sPid[32] = {0};
    snprintf(sPid, sizeof(sPid), "\"PID\":\"%d\",\"UID\":\"%d\"", pid, getuid());
 
    char sinfo[1024] = {0};
    size_t lenInfo = strlen(info);
    if (lenInfo > 1023) {
        strncpy(sinfo, info, 1023);
        sinfo[1023] = '\0';  // Ensure null-termination
    } else {
        strncpy(sinfo, info, lenInfo);
        sinfo[lenInfo] = '\0';  // Ensure null-termination
    }
 
    CLOGE("{\"Type\":\"NativeLog\",%s,\"Error\":\"<%s>:%s\"}", sPid, method, sinfo);
  
}
 
void clogif(const char* method, const char* format, ...){
     // 构建可变参数的格式化信息
    char sinfo[1024] = {0};
    va_list args;
    va_start(args, format);
    vsnprintf(sinfo, sizeof(sinfo), format, args);
    va_end(args);
    clogi(method, sinfo);
}
 
void clogef(const char* method, const char* format, ...) {
    char sinfo[1024] = {0};
    va_list args;
    va_start(args, format);
    vsnprintf(sinfo, sizeof(sinfo), format, args);
    va_end(args);
    cloge(method, sinfo);
}
 
int isuseruid(void) {
    if ( getuid() > 10000) {
        return 1;
    }  
    return 0;
}

新增 bionic\libc\include\unistd.h

arduino 复制代码
// 新增日志libc打印函数
void clogi(const char* method, const char* info);
void cloge(const char* method, const char* error);
void clogif(const char* method, const char* format, ...);
void clogef(const char* method, const char* format, ...);
int isuseruid(void);

修改 bionic\libc\Android.bp , 添加clog.cpp到编译链

arduino 复制代码
// ========================================================
// libc_tzcode.a - upstream 'tzcode' code
// ========================================================
 
cc_library_static {
 
    defaults: ["libc_defaults"],
    srcs: [
        "tzcode/**/*.c",
        "tzcode/bionic.cpp",
        "upstream-openbsd/lib/libc/time/wcsftime.c", // tzcode doesn't include wcsftime, so we use the OpenBSD one.
        "bionic/clog.cpp", // 增加链接库编译, 用于libc日志打印
    ],

调用方式

引入 unistd.h 头文件 Demo

perl 复制代码
#include<unistd.h>
int main() { 
     clogi("函数名称", "日志内容");
     cloge("函数名称", "日志内容");
     clogif("函数名称", "日志内容格式: value=%s, number=%d", "value", 12);
     clogef("函数名称", "日志内容格式: value=%s, number=%d", "value", 12);
}

2.2 修改strstr实现字符串特征绕过

背景

当前市场上针对 Frida、Xposed 和 Root 的检测方法种类繁多,但它们的核心逻辑大多基于特定特征字符串的检测。例如,通过查询目标进程内存、文件路径或动态链接库中的关键字来判断系统状态。这些方法通常依赖以下函数:

strstr:用于查找字符串中是否包含目标特征。 strcmp:用于比较两个字符串是否相等。 为绕过这些检测机制,可以通过修改 strstr 和 strcmp 函数的行为来隐藏特征信息。有以下两种修改思路

直接修改系统函数行为

修改 AOSP 系统源码,将 strstr 修改为始终返回 NULL,或对关键字的判断进行特殊处理(如跳过检测特定特征)。 将 strcmp 修改为对特定字符串返回假值(即认为字符串不相等)。 Hook 系统函数 使用 Hook 技术自定义 strstr 和 strcmp 的实现逻辑,例如对检测关键字返回自定义值。 可灵活调整返回值,动态适应不同的检测逻辑。

相比 Hook 技术,直接修改系统源码的方法具有以下优势:

规避二次检测 Hook 技术本身可能会引入新的特征(如 Frida 或 Xposed 的加载路径、动态库等),从而被检测机制发现。通过修改系统源码,避免额外工具的引入,降低被检测的风险。

性能开销低 修改源码后,系统函数的行为在底层实现中直接改变,不需要额外的 Hook 调用,运行效率更高,适合长期使用。

持久性强 系统源码修改后生成的固件可以直接刷入设备,具备持久性,即使设备重启或更新应用也能保持生效。

隐蔽性好 系统源码的修改不依赖第三方框架或工具,特征难以被检测,尤其适用于对抗高级检测手段。

修改方式

修改以及新增函数 bionic\libc\upstream-openbsd\lib\libc\string\strstr.c 新增函数myStrstr,contains_keyword

kotlin 复制代码
// 新增如下代码,实现自己strstr函数
#define MAX_KEYWORDS 8
 
/* 特定关键词 */
const char* keywords[MAX_KEYWORDS] = {"libfrida", "frida-","/sbin/.magisk", "/sbin/.core/mirror", "/magisk.db", " su", "bin/su", "lsposed"};
 
char *
myStrstr(const char *h, const char *n)
{
    /* Return immediately on empty needle */
    if (!n[0]) return (char *)h;
 
    /* Use faster algorithms for short needles */
    h = strchr(h, *n);
    if (!h || !n[1]) return (char *)h;
    if (!h[1]) return 0;
    if (!n[2]) return twobyte_strstr((void *)h, (void *)n);
    if (!h[2]) return 0;
    if (!n[3]) return threebyte_strstr((void *)h, (void *)n);
    if (!h[3]) return 0;
    if (!n[4]) return fourbyte_strstr((void *)h, (void *)n);
 
    return twoway_strstr((void *)h, (void *)n);
}
 
/* 检查是否包含特定关键词 */
int contains_keyword(const char *str) {
    // ALOGI("full-keyword %s", str);
    clogi("contains_keyword", str);
    if (isuseruid() == 0){
        return 0;
    }
    for (int i = 0; i < MAX_KEYWORDS; i++) {
        if (myStrstr(str, keywords[i])) {
            char buffer[256];
            snprintf(buffer, sizeof(buffer), "%s detect keyword: %s return null", str, keywords[i]);
            clogi("contains_keyword", buffer);
            return 1; // 找到关键词
        }
    }
    return 0; // 未找到关键词
}
 
// 修改函数行为,当遇到对应关键词则返回0 以此脱离敏感词查询
char *
strstr(const char *h, const char *n)
{
    /* Return immediately on empty needle */
    if (!n[0]) return (char *)h;
 
    // 这里添加上字符串判断
    if (contains_keyword(n)) return 0;
 
    /* Use faster algorithms for short needles */
    h = strchr(h, *n);
    if (!h || !n[1]) return (char *)h;
    if (!h[1]) return 0;
    if (!n[2]) return twobyte_strstr((void *)h, (void *)n);
    if (!h[2]) return 0;
    if (!n[3]) return threebyte_strstr((void *)h, (void *)n);
    if (!h[3]) return 0;
    if (!n[4]) return fourbyte_strstr((void *)h, (void *)n);
 
    return twoway_strstr((void *)h, (void *)n);
}
DEF_STRONG(strstr);

修改string.h头文件声明, bionic\libc\include\string.h 增加

arduino 复制代码
char* strdup(const char* __s);
char* myStrstr(const char* __haystack, const char* __needle); //新增 
int contains_keyword(const char *str); // 新增
char* strstr(const char* __haystack, const char* __needle) __attribute_pure__;

执行编译 make -j4

最终实现效果 libc.so文件新增符号myStrstr,contains_keyword

日志打印

2.3 添加exec有关函数进行参数打印

目的

打印命令行执行函数 ,用于方便定位调试

修改步骤 路径 <aosp>\bionic\libc\bionic\exec.cpp

arduino 复制代码
#include <sys/types.h>
#include <sys/uio.h>
 
#include <errno.h>
#include <limits.h>
#include <paths.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 新增
#include <unistd.h> // 新增
 
#include "private/__bionic_get_shell_path.h"
#include "private/FdPath.h"
 
extern "C" char** environ;
 
enum ExecVariant { kIsExecL, kIsExecLE, kIsExecLP };
 
// 单纯打印命令执行函数,用于调试定位分析
static void log_argv(const char* func_name, char* const* argv) {
  if (argv == nullptr) return;
 
  clogi(func_name, "called with argv:");
  for (size_t i = 0; argv[i] != nullptr; ++i) {
    char buffer[256];
    snprintf(buffer, sizeof(buffer), "  argv[%zu]: %s", i, argv[i]);
    clogi(func_name, buffer);
  }
}
 
static int __execl(const char* name, const char* argv0, ExecVariant variant, va_list ap) {
  // Count the arguments.
  va_list count_ap;
  va_copy(count_ap, ap);
  size_t n = 1;
  while (va_arg(count_ap, char*) != nullptr) {
    ++n;
  }
  va_end(count_ap);
 
  // Construct the new argv.
  char* argv[n + 1];
  argv[0] = const_cast<char*>(argv0);
  n = 1;
  while ((argv[n] = va_arg(ap, char*)) != nullptr) {
    ++n;
  }
 
  // Collect the argp too.
  char** argp = (variant == kIsExecLE) ? va_arg(ap, char**) : environ;
 
  va_end(ap);
  // 新增
  log_argv("__execl", argv);
  return (variant == kIsExecLP) ? execvp(name, argv) : execve(name, argv, argp);
}
 
 
int execv(const char* name, char* const* argv) {
  log_argv("execv", argv); // 新增
  return execve(name, argv, environ);
}
 
int execvp(const char* name, char* const* argv) {
  log_argv("execvp", argv); // 新增
  return execvpe(name, argv, environ);
}

实现效果

相关推荐
网安Ruler9 小时前
mips简单栈溢出
逆向
介一安全4 天前
【Frida Android】实战篇1:环境准备
android·网络安全·逆向·frida
SamsongSSS6 天前
JavaScript逆向Vue处理事件和捕获错误的核心逻辑
前端·javascript·vue.js·逆向
Glommer8 天前
验证码滑动轨迹浅谈
javascript·逆向
介一安全10 天前
【Frida Android】基础篇15(完):Frida-Trace 基础应用——JNI 函数 Hook
android·网络安全·ida·逆向·frida
huidu0116 天前
昨天线下赛的复盘
逆向
Glommer16 天前
某易易盾验证码处理思路(下)
javascript·逆向
介一安全17 天前
【Frida Android】基础篇12:Native层hook基础——调用原生函数
android·网络安全·逆向·安全性测试·frida·1024程序员节
介一安全20 天前
【Frida Android】基础篇9:Java层Hook基础——Hook构造函数
android·网络安全·逆向·安全性测试·frida