正文
将我们的逻辑用c++写好之后编译成so,通过lief静态注入到zpp_process64
def inject_so_tail(src: str | Path,
new_so: str,
dst: str | Path | None = None) -> Path:
bin_ = lief.parse(str(src))
# 1. 记录旧依赖顺序并全部移除
old_needed = [lib for lib in bin_.libraries if lib != new_so]
for lib in list(bin_.libraries):
bin_.remove_library(lib)
# 2. 先插入新库,再按「旧依赖逆序」重建
bin_.add_library(new_so) # 现在它在最前
for lib in reversed(old_needed): # 逐个插到前面
bin_.add_library(lib)
out = Path(dst) if dst else Path(str(src) + ".patched")
bin_.write(str(out))
return out
修改好之后,需要借助apatch来实现替换,对于apatch,做了一些了解,它主要是通过向内核注入一段程序,来实现所有功能的,包括kpm,apm,root等。并且他会给用户态暴露系统调用,由这个系统调用统筹管理所有功能,以下这段程序即可使线程获得至高权限,并且pid也不是0,注意,这个线程去执行命令,是没有高权限的,只是他自己有。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define __NR_supercall 45
#define SUPERCALL_THREAD_SU 0x1011
static inline int ver_and_cmd(const char *key, int cmd) {
int v = 0;
for (int i = 0; key[i]; ++i) {
v += key[i];
}
return ((v & 0xffff) <16) | (cmd & 0xffff);
}
int main(int argc, char *argv[]) {
const char *key = "xxxxx";
int tid = gettid();
long result = syscall(__NR_supercall, key, ver_and_cmd(key, SUPERCALL_THREAD_SU), tid, "u:r:magisk:s0");
printf("提权调用返回值: %ld\n", result);
printf("\n--- 权限验证信息 ---\n");
system("cat /proc/self/status | grep -E 'Uid:|Gid:|CapEff:'");
return 0;
}
这个系统调用日后应当有大作为,目前暂时不用,目前只是用一下apm模块,apm模块主要做的事情就是完成app_process64的替换,同时往/system/lib64里挂载上我们的主要功能so,功能so主要实现如下。
读取配置文件,hook setthreadname,判断是不是目标进程。
static void hook_thread() {
LOGD( "[*] hook_thread() called");
sleep(2);
gum_init_embedded();
GumAddress base_adress=get_module_base("libandroid_runtime.so");
GumAddress SpecializeCommon=base_adress+(GumAddress)0x20AB70;
GumAddress SetThreadName=base_adress+(GumAddress)0x2118A0;
ExtractJString=(ExtractJStringFunc)(base_adress+(GumAddress)0x2115E0);
auto on_enter = [](GumInvocationContext* ic, gpointer) {
gpointer raw = gum_invocation_context_get_nth_argument(ic, 0);
auto str = reinterpret_cast(raw);
entry(str->c_str());
};
auto on_leave = [](GumInvocationContext* ic, gpointer) {
gpointer raw = gum_invocation_context_get_nth_argument(ic, 0);
auto str = reinterpret_cast(raw);
LOGD("[on_leave] SetThreadName %d %s",getpid(),str->c_str());
};
gum_hook((GumAddress)SetThreadName,
on_enter,
nullptr,
nullptr);
LOGD( "[*] hook_thread() finished");
}
__attribute__((constructor))
void test_entry(){
std::thread t(hook_thread);
t.detach();
}
如果是目标进程,启动两个线程,其中一个线程负责接收外部指令,也就是load脚本哪些;另一个线程负责具体处理脚本相关的操作,使用frida-gumjs的c api,两个线程内部使用zmq的inproc功能通信,server线程使用zmq的socket通信,zmq是一个轻量级的强大的消息中间件,使用它可以避免原始socket,功能太低级,要处理很多繁琐的细节。