【Android车载系列】第12章 车载渲染-SurfaceFlinger单独绘制到界面

news2024/9/27 9:24:45

1 SurfaceFlinger渲染方案

车载的倒车影像同学们知道是怎么实现的吗?等待Android设备开机再打开倒车影像App?车子挂到R档倒车如果等待这个流程实在太久,是否还有其它办法可以让车子挂R档直接展示倒车影像呢?大家思考一下。
我们从Android渲染的基础上思考,Android图像渲染到屏幕上通过SurfaceFlinger完成,如果我们不通过App的Surface来完成对SurfaceFlinger的调用,是否可以直接通过某些方式拿到SurfaceFlinger把图像绘制在屏幕上?
实际上,我们有如下两种方式:
1.通过SurfaceComposeClient拿到SurfaceFlinger,使用OpenGL直接渲染到屏幕上
2.通过Hook拿到SurfaceFlinger,截获SurfaceFlinger的eglSwapBuffers方法调用替换为new_eglSwapBuffers方法。我们在new_eglSwapBuffers方法内调用原来的eglSwapBuffers并且绘制自定义的图像。

下面我们来通过方式2实现自定义渲染:

2 Hook的方式实现渲染

2.1 实现思路

1. 实现一个Linux下的ptrace注入器: inject
2. 通过Hook注入的方式,将SurfaceFlinger渲染的方法拦截
3. 在拦截的方法内自定义渲染的逻辑

2.2 Hook注入

Hook其实被应用的场景也比较多,比如平时我们使用AndroidStudio进行debug时,就是通过的这种Hook方式实现。需要对某个App的关键函数就行Hook,对于android而言,Smali层我们使用Xposed Hook框架,So层我们可以使用Cydia Substrate框架, Smali 层的Hook由于Xposed做的很好了, 但是关于Cydia Substrate对So的Hook使用起来很不好用,Cydia Substrate对So的Hook,会有以下问题:

  1. 会所有android 应用程序的进行Hook
  2. 很难实现对某个App 私有的so进行Hook
  3. Hook的事机 都是被动的,用户不能选择。
    基于此,我们直接通过注入的方式实现Hook。

类似Windows 下的DLL 注入, 远程进程调用使用关键函数ptrace函数实现, 具体实现步骤如下:
4. 远程进程调用mmap 函数 在对方进程申请一块内存
5. 在这块内存中写上 so 的路径
6. 调用远程调用 dlopen 函数, dlopen就会调用so的所有void attribute((constructor)) 属性的函数
7. void attribute((constructor)) 属性的函数中实现我们的hook函数
8. hook 函数我们可以利用Cydia Substrat框架的MSFunctionHook函数量实现对某个函数进行Hook。

2.2.1 ptrace注入器

之所以我们的注入程序叫做ptrace注入器,这是因为我们整个注入过程都在使用Linux的API -ptrace,ptrace是linux系统的一个编写调试器的api,此api必须在root用户才能调用,所以此种注入方式只能在root用户下使用,这里需要利用ptrace读取和修改寄存器的值和读写内存,以完成类似windows的dll注入功能。 下面的关键函数,已有大神帮我们封装的好了。
Ptrace 注入器基本原理:

  1. attach目标进程,保存寄存器环境, 关键函数:ptrace_attach , ptrace
    if (ptrace_attach(target_pid) == -1)
        goto exit;

    if (ptrace_getregs(target_pid, &regs) == -1)
        goto exit;
  1. 远程进程调用 mmap在目标进程内申请一块内存, 关键函数:ptrace_call_wrapper
    mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap);
    DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr);

    /* call mmap */
    parameters[0] = 0;                                  // addr
    parameters[1] = 0x8000;                             // size
    parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
    parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE;        // flags
    parameters[4] = 0;                                  //fd
    parameters[5] = 0;                                  //offset

    if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, &regs) == -1)
        goto exit;

    map_base = ptrace_retval(&regs);
  1. 把注入SO的路径写入上一步申请的地址空间, 关键函数:ptrace_writedata
    ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);
  1. 远程进程调用函数dlopen,注意获取dlopen函数地址在linker模块中获取
	// linker_path:/system/bin/linker
    dlopen_addr = get_remote_addr(target_pid, linker_path, (void *)dlopen);
    dlsym_addr = get_remote_addr(target_pid, linker_path, (void *)dlsym);
    dlclose_addr = get_remote_addr(target_pid, linker_path, (void *)dlclose);
    dlerror_addr = get_remote_addr(target_pid, linker_path, (void *)dlerror);

    DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n",
                dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);

    printf("library path = %s\n", library_path);
    ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);

    parameters[0] = map_base;
    parameters[1] = RTLD_NOW | RTLD_GLOBAL;

    if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, &regs) == -1)
        goto exit;

	// ptrace_retval 获取远程进程调用的ret value
    void *sohandle = ptrace_retval(&regs);
  1. 恢复环境,detach 目标进程: 关键函数:ptrace_detach
    ptrace_setregs(target_pid, &original_regs);
    ptrace_detach(target_pid);

就此一个Linux 下的ptrace注入器就完成了。注: 由于笔者的代码是在android模拟上运行的, 代码是交叉编译,不便于调试,所以基本靠printf 完成代码的调试,但是这样效率比较低, 笔者建议看官朋友,可以在Linux系统完成这个注入程序的开发,这样不会笔者遇到一些问题,难以调试。 另外上面代码很多函数已经进行过封装了,具体项目地址看文章最后。
另外:ptrace:还可以用于反调试ptrace (TRACEME)

2.3 SurfaceFlinger渲染

对于Android for arm上的so注入(inject)挂钩(hook),网上已有牛人给出了代码inject。由于实现中的ptrace函数是依赖于平台的,所以不经改动只能用于arm平台。本文将之扩展用于Android的x86平台。Arm平台部分基本重用了inject中的代码,其中因为汇编不好移植且容易出错,所以把shellcode.s用ptrace_call替换掉了,另外保留了mmap,用来传字符串参数,当然也可以通过栈来传,但栈里和其它东西混一起,一弄不好就会隔儿了,所以还是保险点好。最后注意设备要root。还有就是要在Linux中配置一下NDK编译环境呀。
首先创建目录及文件:
在这里插入图片描述

(说明:由于NDK编译的条件限制,所以我们需要创建的jni文件夹,当然这个文件的名字必须是jni)
注入的核心源代码:

  1. inject.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <asm/ptrace.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <elf.h>
#include <android/log.h>

#if defined(__i386__)
#define pt_regs user_regs_struct
#endif

#define ENABLE_DEBUG 1

#if ENABLE_DEBUG
#define LOG_TAG "INJECT"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, fmt, ##args)
#define DEBUG_PRINT(format, args...) \  
    LOGD(format, ##args)
#else
#define DEBUG_PRINT(format, args...)
#endif

#define CPSR_T_MASK (1u << 5)

const char *libc_path = "/system/lib/libc.so";
const char *linker_path = "/system/bin/linker";

int ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size)
{
    uint32_t i, j, remain;
    uint8_t *laddr;

    union u
    {
        long val;
        char chars[sizeof(long)];
    } d;

    j = size / 4;
    remain = size % 4;

    laddr = buf;

    for (i = 0; i < j; i++)
    {
        d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
        memcpy(laddr, d.chars, 4);
        src += 4;
        laddr += 4;
    }

    if (remain > 0)
    {
        d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
        memcpy(laddr, d.chars, remain);
    }

    return 0;
}

int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size)
{
    uint32_t i, j, remain;
    uint8_t *laddr;

    union u
    {
        long val;
        char chars[sizeof(long)];
    } d;

    j = size / 4;
    remain = size % 4;

    laddr = data;

    for (i = 0; i < j; i++)
    {
        memcpy(d.chars, laddr, 4);
        ptrace(PTRACE_POKETEXT, pid, dest, d.val);

        dest += 4;
        laddr += 4;
    }

    if (remain > 0)
    {
        d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0);
        for (i = 0; i < remain; i++)
        {
            d.chars[i] = *laddr++;
        }

        ptrace(PTRACE_POKETEXT, pid, dest, d.val);
    }

    return 0;
}

#if defined(__arm__)
int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs *regs)
{
    uint32_t i;
    for (i = 0; i < num_params && i < 4; i++)
    {
        regs->uregs[i] = params[i];
    }

    //
    // push remained params onto stack
    //
    if (i < num_params)
    {
        regs->ARM_sp -= (num_params - i) * sizeof(long);
        ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)&params[i], (num_params - i) * sizeof(long));
    }

    regs->ARM_pc = addr;
    if (regs->ARM_pc & 1)
    {
        /* thumb */
        regs->ARM_pc &= (~1u);
        regs->ARM_cpsr |= CPSR_T_MASK;
    }
    else
    {
        /* arm */
        regs->ARM_cpsr &= ~CPSR_T_MASK;
    }

    regs->ARM_lr = 0;

    if (ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1)
    {
        printf("error\n");
        return -1;
    }

    int stat = 0;
    waitpid(pid, &stat, WUNTRACED);
    while (stat != 0xb7f)
    {
        if (ptrace_continue(pid) == -1)
        {
            printf("error\n");
            return -1;
        }
        waitpid(pid, &stat, WUNTRACED);
    }

    return 0;
}

#elif defined(__i386__)
long ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct user_regs_struct *regs)
{
    regs->esp -= (num_params) * sizeof(long);
    ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long));

    long tmp_addr = 0x00;
    regs->esp -= sizeof(long);
    ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr));

    regs->eip = addr;

    if (ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1)
    {
        printf("error\n");
        return -1;
    }

    int stat = 0;
    waitpid(pid, &stat, WUNTRACED);
    while (stat != 0xb7f)
    {
        if (ptrace_continue(pid) == -1)
        {
            printf("error\n");
            return -1;
        }
        waitpid(pid, &stat, WUNTRACED);
    }

    return 0;
}
#else
#error "Not supported"
#endif

int ptrace_getregs(pid_t pid, struct pt_regs *regs)
{
    if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0)
    {
        perror("ptrace_getregs: Can not get register values");
        return -1;
    }

    return 0;
}

int ptrace_setregs(pid_t pid, struct pt_regs *regs)
{
    if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0)
    {
        perror("ptrace_setregs: Can not set register values");
        return -1;
    }

    return 0;
}

int ptrace_continue(pid_t pid)
{
    if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0)
    {
        perror("ptrace_cont");
        return -1;
    }

    return 0;
}

int ptrace_attach(pid_t pid)
{
    if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0)
    {
        perror("ptrace_attach");
        return -1;
    }

    int status = 0;
    waitpid(pid, &status, WUNTRACED);

    return 0;
}

int ptrace_detach(pid_t pid)
{
    if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0)
    {
        perror("ptrace_detach");
        return -1;
    }

    return 0;
}

void *get_module_base(pid_t pid, const char *module_name)
{
    FILE *fp;
    long addr = 0;
    char *pch;
    char filename[32];
    char line[1024];

    if (pid < 0)
    {
        /* self process */
        snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
    }
    else
    {
        snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
    }

    fp = fopen(filename, "r");

    if (fp != NULL)
    {
        while (fgets(line, sizeof(line), fp))
        {
            if (strstr(line, module_name))
            {
                pch = strtok(line, "-");
                addr = strtoul(pch, NULL, 16);

                if (addr == 0x8000)
                    addr = 0;

                break;
            }
        }
        fclose(fp);
    }
    else
    {
        printf("get_module_base error!\n");
    }
    return (void *)addr;
}

void *get_remote_addr(pid_t target_pid, const char *module_name, void *local_addr)
{
    void *local_handle, *remote_handle;
    DEBUG_PRINT("[+] get_remote_addr:%s\n", module_name);

    local_handle = get_module_base(-1, module_name);
    remote_handle = get_module_base(target_pid, module_name);

    DEBUG_PRINT("[+] get_remote_addr: local[%x], remote[%x]\n", local_handle, remote_handle);

    void *ret_addr = (void *)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);

    return ret_addr;
}

int find_pid_of(const char *process_name)
{
    int id;
    pid_t pid = -1;
    DIR *dir;
    FILE *fp;
    char filename[32];
    char cmdline[256];

    struct dirent *entry;

    if (process_name == NULL)
        return -1;

    dir = opendir("/proc");
    if (dir == NULL)
        return -1;

    while ((entry = readdir(dir)) != NULL)
    {
        id = atoi(entry->d_name);
        if (id != 0)
        {
            sprintf(filename, "/proc/%d/cmdline", id);
            fp = fopen(filename, "r");
            if (fp)
            {
                fgets(cmdline, sizeof(cmdline), fp);
                fclose(fp);

                if (strcmp(process_name, cmdline) == 0)
                {
                    /* process found */
                    pid = id;
                    break;
                }
            }
        }
    }

    closedir(dir);
    return pid;
}

long ptrace_retval(struct pt_regs *regs)
{
#if defined(__arm__)
    return regs->ARM_r0;
#elif defined(__i386__)
    return regs->eax;
#else
#error "Not supported"
#endif
}

long ptrace_ip(struct pt_regs *regs)
{
#if defined(__arm__)
    return regs->ARM_pc;
#elif defined(__i386__)
    return regs->eip;
#else
#error "Not supported"
#endif
}

int ptrace_call_wrapper(pid_t target_pid, const char *func_name, void *func_addr, long *parameters, int param_num, struct pt_regs *regs)
{
    DEBUG_PRINT("[+] Calling %s in target process.\n", func_name);
    if (ptrace_call(target_pid, (uint32_t)func_addr, parameters, param_num, regs) == -1)
        return -1;

    if (ptrace_getregs(target_pid, regs) == -1)
        return -1;
    DEBUG_PRINT("[+] Target process returned from %s, return value=%x, pc=%x \n",
                func_name, ptrace_retval(regs), ptrace_ip(regs));
    return 0;
}

int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, const char *param, size_t param_size)
{
    int ret = -1;
    void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr;
    void *local_handle, *remote_handle, *dlhandle;
    uint8_t *map_base = 0;
    uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr;

    struct pt_regs regs, original_regs;
    extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \  
        _dlsym_param2_s,
        _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \  
        _saved_cpsr_s,
        _saved_r0_pc_s;

    uint32_t code_length;
    long parameters[10];

    DEBUG_PRINT("[+] Injecting process: %d\n", target_pid);

    if (ptrace_attach(target_pid) == -1)
        goto exit;

    if (ptrace_getregs(target_pid, &regs) == -1)
        goto exit;

    /* save original registers */
    memcpy(&original_regs, &regs, sizeof(regs));

    mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap);
    DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr);

    /* call mmap */
    parameters[0] = 0;                                  // addr
    parameters[1] = 0x8000;                             // size
    parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
    parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE;        // flags
    parameters[4] = 0;                                  //fd
    parameters[5] = 0;                                  //offset

    if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, &regs) == -1)
        goto exit;

    map_base = ptrace_retval(&regs);

    dlopen_addr = get_remote_addr(target_pid, linker_path, (void *)dlopen);
    dlsym_addr = get_remote_addr(target_pid, linker_path, (void *)dlsym);
    dlclose_addr = get_remote_addr(target_pid, linker_path, (void *)dlclose);
    dlerror_addr = get_remote_addr(target_pid, linker_path, (void *)dlerror);

    DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n",
                dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);

    printf("library path = %s\n", library_path);
    ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);

    parameters[0] = map_base;
    parameters[1] = RTLD_NOW | RTLD_GLOBAL;

    if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, &regs) == -1)
        goto exit;

    void *sohandle = ptrace_retval(&regs);

#define FUNCTION_NAME_ADDR_OFFSET 0x100
    ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1);
    parameters[0] = sohandle;
    parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET;

    if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, &regs) == -1)
        goto exit;

    void *hook_entry_addr = ptrace_retval(&regs);
    DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr);

#define FUNCTION_PARAM_ADDR_OFFSET 0x200
    ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1);
    parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET;

    if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, &regs) == -1)
        goto exit;

    printf("hook success\n");
    // getchar();
    // parameters[0] = sohandle;

    // if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, &regs) == -1)
    //     goto exit;

    /* restore */
    ptrace_setregs(target_pid, &original_regs);
    ptrace_detach(target_pid);
    ret = 0;

exit:
    return ret;
}

int main(int argc, char **argv)
{
    pid_t target_pid;
    target_pid = find_pid_of("/system/bin/surfaceflinger");
    if (-1 == target_pid)
    {
        printf("Can't find the process\n");
        return -1;
    }
    printf("target_pid:%d\n", target_pid);
    DEBUG_PRINT("[+] Start\n");
    inject_remote_process(target_pid, "/data/local/tmp/libdraw.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!"));
    return 0;
}

注意上面的/system/bin/surfaceflinger进程我随手写的,你的设备上不一定有。没有的话挑其它的也
行,前提是ps命令里能找到。

  1. inject文件夹的Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := inject 
LOCAL_SRC_FILES := inject.c 
#shellcode.s
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
#LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
  1. draw.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <elf.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdbool.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <string.h>

#define LOG_TAG "INJECT"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

#define LIBSF_PATH "/system/lib/libsurfaceflinger.so"
GLuint gProgram;
EGLBoolean (*old_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surf) = -1;
EGLint w, h;
char *gVertexShader =
        "attribute vec4 vPosition;\n"
        "void main() {\n"
        "  gl_Position = vPosition;\n"
        "}\n";
//  字符串
char *gFragmentShader =
        "precision mediump float;\n"
        "void main() {\n"
        "  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
        "}\n";
// 这种代码运行 CPU  1   GPU 2  GPU 的代码       glsl 语法    字符语言  gpu  编译
void init_gl(){
//创建顶点程序
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);

    if (vertexShader) {
//         内存中  字符串   关联到CPU   开好的程序
        glShaderSource(vertexShader, 1, &gVertexShader, NULL);
//        GPU     编译
        glCompileShader(vertexShader);
//        检测编译成功
        GLint compiled = 0;
//        过程  java       对象.方法    面向过程= 面向状态
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compiled);
//
        if (!compiled) {
            LOGD("创建片元程序失败");
            glDeleteShader(vertexShader);
            vertexShader = 0;
            return;
        }

        LOGD("创建顶点程序成功");

    }

//创建片元程序
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    if (fragmentShader) {
        glShaderSource(fragmentShader, 1, &gFragmentShader, NULL);
        glCompileShader(fragmentShader);

        GLint compiled = 0;
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            LOGD("创建片元程序失败");
            glDeleteShader(fragmentShader);
            fragmentShader = 0;
            return;
        }
        LOGD("创建片元程序成功");

    }
    gProgram = glCreateProgram();
    if (gProgram)
    {
        glAttachShader(gProgram, vertexShader);
        glAttachShader(gProgram, fragmentShader);
        glLinkProgram(gProgram);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(gProgram, GL_LINK_STATUS, &linkStatus);
        LOGD("开始创建总程序");
        if (linkStatus != GL_TRUE)
        {
            LOGD("总程序创建失败");
            glDeleteProgram(gProgram);
            gProgram = 0;
            return;
        }

    }

    LOGD("总程序创建成功");
//     程序  公司 连个部门

}

void drawRect(int x,int y,int width,int height){
    float  draw_x =0.1;
    float draw_y=0.1;
    float draw_width=0.3;
    float draw_height=0.3;
    const GLfloat Vertices[] = {
            draw_x,draw_y,0.0f,
            draw_x+draw_width,draw_y,0.0f,
            draw_x+draw_width,draw_y-draw_height,0.0f,
            draw_x,draw_y-draw_height,0.0f,
    };

//    绘制图像
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, Vertices);
    glDrawArrays(GL_LINE_LOOP, 0, 4);
}

//执行     我们从这里开始
//定义好了新函数  没事 1 s    简单操作  这一个
EGLBoolean new_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
{
    LOGD("New eglSwapBuffers\n");
    eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
    eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
    glUseProgram(gProgram);
    glLineWidth(2);
    glEnableVertexAttribArray(0);
    drawRect(0,0,200,200);

    return eglSwapBuffers(dpy, surface);
}


void *get_module_base(pid_t pid, const char *module_name)
{
    FILE *fp;
    long addr = 0;
    char *pch;
    char filename[32];
    char line[1024];

    if (pid < 0)
    {
        snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
    }
    else
    {
        snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
    }

    fp = fopen(filename, "r");

    if (fp != NULL)
    {
        while (fgets(line, sizeof(line), fp))
        {
            if (strstr(line, module_name))
            {
                pch = strtok(line, "-");
                addr = strtoul(pch, NULL, 16);

                if (addr == 0x8000)
                    addr = 0;

                break;
            }
        }

        fclose(fp);
    }

    return (void *)addr;
}

#define LIBSF_PATH "/system/lib/libsurfaceflinger.so"
int hook_eglSwapBuffers()
{
//    系统函数
    old_eglSwapBuffers = eglSwapBuffers;
    LOGD("Orig eglSwapBuffers = %p\n", old_eglSwapBuffers);
//    找到这个地址eglSwapBuffers  的地址  map 工具 查找  surfaceflinger  找到这个地址eglSwapBuffers 的地址
    void *base_addr = get_module_base(getpid(), LIBSF_PATH);
    LOGD("libsurfaceflinger.so address = %p\n", base_addr);

    int fd;
    fd = open(LIBSF_PATH, O_RDONLY);
    if (-1 == fd)
    {
        LOGD("error\n");
        return -1;
    }

    Elf32_Ehdr ehdr;
    read(fd, &ehdr, sizeof(Elf32_Ehdr));

    unsigned long shdr_addr = ehdr.e_shoff;
    int shnum = ehdr.e_shnum;
    int shent_size = ehdr.e_shentsize;
    unsigned long stridx = ehdr.e_shstrndx;

    Elf32_Shdr shdr;
    lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET);
    read(fd, &shdr, shent_size);

    char *string_table = (char *)malloc(shdr.sh_size);
    lseek(fd, shdr.sh_offset, SEEK_SET);
    read(fd, string_table, shdr.sh_size);
    lseek(fd, shdr_addr, SEEK_SET);

    int i;
    uint32_t out_addr = 0;
    uint32_t out_size = 0;
    uint32_t got_item = 0;
    int32_t got_found = 0;

    // LOGD("shnum=%d\n", shnum);
    for (int num = 0; num < shnum; num++)
    {
        read(fd, &shdr, shent_size);
        int name_idx = shdr.sh_name;
        if (strcmp(&(string_table[name_idx]), ".got.plt") == 0 || strcmp(&(string_table[name_idx]), ".got") == 0)
        {
            LOGD("%s\n", &(string_table[name_idx]));
            out_addr = base_addr + shdr.sh_addr;
            out_size = shdr.sh_size;
            LOGD("out_addr = %lx, out_size = %lx\n", out_addr, out_size);
            for (i = 0; i < out_size; i += 4)
            {
                got_item = *(uint32_t *)(out_addr + i);
                // LOGD("got_item = %lx\n", got_item);
                if (got_item == old_eglSwapBuffers)
                {
                    LOGD("Found eglSwapBuffers in got\n");
                    got_found = 1;

                    uint32_t page_size = getpagesize();
                    uint32_t entry_page_start = (out_addr + i) & (~(page_size - 1));
                    mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE);
//                     新函数 注入这个地址
                    *(uint32_t *)(out_addr + i) = new_eglSwapBuffers;
//                    系统surfaceflinger 调用  eglSwapBuffers-----》new_eglSwapBuffers
                    break;
                }
                else if (got_item == new_eglSwapBuffers)
                {
                    LOGD("Already hooked\n");
                    break;
                }
            }
            if (got_found)
            {
                break;
            }
        }
    }
    free(string_table);
    close(fd);
}
int hook_entry(char *a)
{
//      了解 用    hook  设备其他设备,设备机型有关 小米 note 1 1
//正式开始  绘制  Opengl  C进程 渲染

    init_gl();
//    替换函数
//    函数 运行   目标进程  1     注入进程 2
    hook_eglSwapBuffers();
    return 0;
}
  1. yvan文件夹的Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_CPPFLAGS += -std=c++11

LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog -lEGL -lGLESv1_CM -lGLESv2
#LOCAL_ARM_MODE := arm
LOCAL_MODULE    := draw
MY_CPP_LIST := $(wildcard $(LOCAL_PATH)/*.c)
LOCAL_SRC_FILES := $(MY_CPP_LIST:$(LOCAL_PATH)/%=%)

LOCAL_C_INCLUDES := \
	$(LOCAL_PATH)/ \

include $(BUILD_SHARED_LIBRARY)
  1. 项目的Android.mk
include $(call all-subdir-makefiles)
  1. 项目的Application.mk:
APP_ABI := x86 armeabi-v7a
  1. CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.18.1)

# Declares and names the project.

project("nativehook")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        nativehook

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp
        jni/inject/inject.c
        jni/yvan/draw.c)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        nativehook

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})
  1. native-lib.cpp
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_nativehook_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string draw = "Draw from C++";
    return env->NewStringUTF(draw.c_str());
}

2.4 编译可执行文件和注入的so

运行nkd-build编译成生x86平台下的可执行文件和注入的so:
在这里插入图片描述

然后就可以跑起来试试了,连接root过的Android设备或者打开模拟器。将inject和libdraw.so拷入设备,设执行权限,执行:先看看被注入进程(surfaceflinger)的mmap,可以看到我们的so已经被加载了,紧接着的那一块就是我们mmap出来的:
从logcat中也可以看到so注入成功,并且以被注入进程的身份执行了so中的代码。将编译好的文件拷贝到设备的data/local/tmp目录(不一定是该目录)。

cd libs/x86
adb push inject /data/local/tmp
adb push libdraw.so /data/local/tmp

在这里插入图片描述
然后修改文件权限,并运行inject,将libdraw.so注入到了此进程中。

adb shell
cd /data/local/tmp
chmod 777 inject
./inject

在这里插入图片描述
简单的注入成功,现在我们再来做一个实验,就是应用这套机制来截获surfaceflinger中的
eglSwapBuffers调用,用替换为new_eglSwapBuffers方法。我们在new_eglSwapBuffers方法内调用原来的eglSwapBuffers并且绘制自定义的图像。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/497027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

opencv_c++学习(四)

图像在opencv中的存储方式 在上图中可以看出&#xff0c;在opencv中采用的是像素值来代表每一个像素三通道颜色的深浅。 Mat对象 Mat对象是在OpenCV2.0之后引进的图像数据结构、自动分配内存、不存在内存泄漏的问题&#xff0c;是面向对象的数据结构。分了两个部分&#xff0…

Graph—随机游走

Random Walk Approaches for Node Embeddings 一、随机游走基本概念 想象一个醉汉在图中随机的行走&#xff0c;其中走过的节点路径就是一个随机游走序列。 随机行走可以采取不同的策略&#xff0c;如行走的方向、每次行走的长度等。 二、图机器学习与NLP的关系 从图与NLP的…

《计算机网络—自顶向下方法》 第二章Wireshark实验:HTTP协议分析

HTTP 协议工作于客户端-服务端架构上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。 WEB 服务器有&#xff1a;Apache服务器&#xff0c;IIS服务器&#xff08;Internet Information Services&#xff09;等。URL&#xff1a;即统一资源定位符(…

手写自己的Springboot-1-整合tomcat

文章目录 创建自己的Springboot创建项目注册、启动Spring容器启动tomcat 引用测试 创建自己的Springboot 首先明确本文章想要达到的目的,就是引入我们自己写的Springboot项目的依赖,无需配置,就可以达到处理普通请求的效果. 这里有一个非常简单的Springboot项目,项目里面就只…

将 Segment Anything 扩展到医学图像领域

文章目录 前言技术交流SAM 拆解分析从医学角度理解 SAM 的效用MedSAM实验总结 前言 SAM 是一种在自然图像分割方面取得成功的模型&#xff0c;但在医学图像分割方面表现不佳。MedSAM 首次尝试将 SAM 的成功扩展到医学图像&#xff0c;并成为用于分割各种医学图像的通用工具。为…

22.网络爬虫—APP数据抓取详讲

网络爬虫—APP数据抓取详讲 Fiddler工作原理安装完成Fiddler后的配置前提条件工具配置 手机数据抓取Fiddler手机端配置手机端操作 实战演示后记 前言&#xff1a; &#x1f3d8;️&#x1f3d8;️个人简介&#xff1a;以山河作礼。 &#x1f396;️&#x1f396;️:Python领域新…

springboot+mybatis搭建maven多模块工程

最近看了一篇博客&#xff0c;选定springbootmybatis作为框架&#xff0c;在idea中搭建maven的多模块工程&#xff0c;下面也再温习一下&#xff0c;并将搭建过程分享出来&#xff0c;供小伙伴们参考。 1、开发工具及系统环境 Idea 2020.3系统环境为win10mysql5.7springboot2.…

GNN学习/GCN学习/GNN环境配置cuda安装/GCN代码展示

参考: A Gentle Introduction to Graph Neural Networks https://distill.pub/2021/gnn-intro/Understanding Convolutions on Graphs https://distill.pub/2021/understanding-gnns/Graph neural networks: A review of methods and applications https://arxiv.org/pdf/1812…

【Java】认识异常

目录 1.异常概念和分类 2.异常的抛出 3.异常的捕获 3.1异常声明throws 3.2异常捕获try-catch 3.3finally 3.4.异常的处理流程 4.自定义异常类 1.异常概念和分类 public class Test {public static void test() {test();}public static void main(String[] args) {Sy…

详解数据集safety-pilot-model-deployment-data

safety-pilot-model-deployment-data这个数据集是由美国交通部的联邦航空管理局&#xff08;FAA&#xff09;和交通运输部&#xff08;DOT&#xff09;主导的“安全试点”&#xff08;Safety Pilot Model Deployment&#xff09;项目所提供的。该项目旨在研究汽车与飞机之间的通…

Vue电商项目--开发Search模块与mockjs模拟数据

Search模块中商品分类与过度动画 现在完成了在/home路由下实现三级导航组件的显示隐藏 通过this.$route.path!/home在搜索页面显示&#xff0c;通过方法鼠标移入移出从而又控制在search路由下的显示隐藏 过渡动画&#xff1a;前提组件|元素必要又v-if| v-show指令才可以进行…

Linux进程状态及优先级

本文已收录至《Linux知识与编程》专栏&#xff01; 作者&#xff1a;ARMCSKGT 演示环境&#xff1a;CentOS 7 进程状态及优先级 前言正文进程状态就绪运行状态R阻塞睡眠状态 S休眠状态D挂起 暂停状态T前台与后台进程待追踪暂停状态t 死亡状态 X僵尸状态 Z 孤儿进程进程优先级查…

nginx(七十三)nginx与Location响应头细节探讨

一 nginx与Location响应头细节探讨 ① 重定向和Location回顾 多种重定向跳转方式的差异 nginx之absolute_redirect、server_name_in_redirect、port_in_redirect 共同控制Location响应头 ② STS响应头导致307重定向 "第一次访问 http://www.baidu.com" 观察…

基于Qt、C++的毕业设计课设数学绘图工具(平面图、图表、立体图绘制-附下载链接)

基于Qt、C的毕业设计课设数学绘图工具&#xff08;平面图、图表、立体图绘制&#xff09; 介绍 这是我的毕业设计&#xff0c;基于Qt Creator 4.11.1&#xff0c;c语言。 效果图如下 点我下载项目源码&#xff08;含打包软件&#xff09; 使用说明 1. 二维函数绘制 开始界面…

python 系列 07 - 基于easyocr的ocr识别

OCR,光学文字识别&#xff0c;对文本资料进行扫描&#xff0c;然后对图像文件进行分析处理&#xff0c;获取文字及版面信息的过程。本示例通过easyocr库来演示。easyocr是一个比较流行的库&#xff0c;支持超过80种语言。安装的时候注意会附带安装torch库&#xff08;一个深度学…

将ROS1和ROS2安装到同一个ubuntu系统中,ROS2安装??????????????

1. 本文测试环境: ubuntu:20.04,虚拟机 ROS1:noetic ROS2:foxy 2. 先说结论 ROS1 与 ROS2 共存,需要考虑三个问题: 1) 不同Ubuntu版本,有不同版本的ROS1和ROS2推荐,尽量不要任性地乱装; 2)ROS1和ROS2安装过程中,是否会出现文件“删改”的问题?目前使用下来,并…

拓扑排序详解(包含算法原理图解、算法实现过程详解、算法例题变式全面讲解等)

前置知识 有向无环图 在图论中&#xff0c;如果一个有向图无法从某个顶点出发经过若干条边回到该点&#xff0c;则这个图是一个有向无环图&#xff08;DAG图&#xff09;。 如图所示。 入度 对于一个有向图&#xff0c;若x点指向y点&#xff0c;则称x点为y点的入度。 出度…

Redis --- 多级缓存

一、什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后&#xff0c;先查询Redis&#xff0c;如果未命中则查询数据库&#xff0c;如图&#xff1a; 存在下面的问题&#xff1a; 请求要经过Tomcat处理&#xff0c;Tomcat的性能成为整个系统的瓶颈Redis缓存失效时&#xff…

python每日一练:硬币划分(多方法详解)

文章目录 前言0、题目一、暴力总是不能解决问题的二、还能更暴力一点三、减少暴力思想四、引入先进思想总结 前言 这题挺有意思的&#xff0c;典型的背包组合问题&#xff0c;虽然没有要求各种组合方式&#xff0c;不过我们可以试试给出组合方式。当然这题不太可能用一行代码解…

STM32 从入门到精通系列讲解 - 总目录

&#x1f466; 作者介绍&#xff1a;Bazinga bingo&#xff0c;专注C语言应用硬核干货分享&#xff0c;潜心修炼&#xff0c;虚心学习&#xff0c;立志做嵌入式相关赛道的Top。 &#x1f4d5; 本文收录于《STM32开发》专栏&#xff0c;包含STM32内部模块介绍、片内资源开发、不…