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);
}
实现效果
