• 编写Android可执行二进制文件调用so


    前文

    为了方便讲解这里简单提一下自己编写so

    使用ndk-build工具编写so文件,不懂的可以先看看我的“使用gdb调试Android(aarch 64)可执行二进制文件”文章

    Android.mk文件

    $ cat Android.mk
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := png_to_yuv
    LOCAL_SRC_FILES := png_to_yuv.c
    include $(BUILD_SHARED_LIBRARY)
    #会在obj/local/arm64-v8a/生成libdemo.so库
    #include $(BUILD_EXECUTABLE) #生成可执行二进制文件。后面会用到!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Application.mk文件

    $ cat Application.mk
    APP_ABI := armeabi-v7a arm64-v8a
    APP_OPTIM := debug
    
    • 1
    • 2
    • 3

    编译脚本

    $ cat load.sh
    #!/bin/zsh
    
    ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk  NDK_DEBUG=1 NDK_HOST_32BIT=1  #32bit
    
    #adb push ./obj/local/arm64-v8a/$1 /data/local/tmp/
    adb push ./obj/local/armeabi-v7a/$1 /data/local/tmp/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这样经过脚本移动端有了一个so文件

    ----------------------------------------------------ok------------------------------------------------------------------------------

    背景

    APK文件中需要分析一个so加密算法(不适用于jni层相关的函数!!)那么一般情况下加密so会是一个导出函数,因为要提供给外界调用嘛,是吧。

    首先将目标so放入移动端工作目录,编写程序时可以通过dlopen()函数进行调用so库,如下:

        void* handle2 = dlopen("./libByteVC1_dec.so",RTLD_LAZY);
        void* handle = dlopen("./libttheif_dec.so",RTLD_LAZY);
    
    • 1
    • 2

    这种情况适用于so之间的嵌套调用,只需要通过dlopen加载该so程序会识别到该库,因为源码是通过Systen.load()实现的。当然一个so也可以这样玩

    然后动态声明到本程序

     int*(*heif_decode_to_yuv420p_inner)(int **a1, int *byteArray, int byteArray_length,int**width,int**height, int* rect);
    
        heif_decode_to_yuv420p_inner = dlsym(handle,"heif_decode_to_yuv420p_inner");
    
    • 1
    • 2
    • 3

    这里的参数,可以很简单的通过IDA por静态分析出来参数类别变量

    最后实现调用

    int *result = heif_decode_to_yuv420p_inner(&a1,byteArr,size,&wid,&h
    ei,rect);
    
    • 1
    • 2

    完整代码:

    #include
    #include
    #include
    #include 
    
    int main(){
        FILE * fp = fopen("./prepare_to_yuv.log","r");
        fseek(fp,0,SEEK_END);
        int size = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        printf("size: %#x\n",size);
    
        int *byteArr = malloc(size);
        fread(byteArr,size,1,fp);
        fclose(fp);
    
    	void* handle2 = dlopen("./libByteVC1_dec.so",RTLD_LAZY);
    	void* handle = dlopen("./libttheif_dec.so",RTLD_LAZY);
    	if (!handle || !handle2) {
    		 fprintf(stderr, "%s\n", dlerror());
    		 exit(EXIT_FAILURE);
    		 }
    //		 printf("%p\n", handle);
    //		 printf("%p\n", handle2);
    	int*(*heif_decode_to_yuv420p_inner)(int **a1, int *byteArray, int byteArray_length,int**width,int**height, int* rect);
    
    	heif_decode_to_yuv420p_inner = dlsym(handle,"heif_decode_to_yuv420p_inner");
    	if (heif_decode_to_yuv420p_inner== NULL) { 
            fprintf(stderr, "Couldn't find function: \n"); 
            exit(1);
        }
    
    	int* rect = malloc(0x18);
    	for(int i=0; i<0x18/4 ;i++){
    		rect[i] = 0;
    	}
    		rect[0] = 1;
    		rect[1] = 1;
    	int buf[15];
    	int *a1 =  buf;
    	int wid = 0;
    	int hei = 0;
    	int *result = heif_decode_to_yuv420p_inner(&a1,byteArr,size,&wid,&hei,rect);
    	printf("size -> %#x\n",result[0]);
    	printf("buff -> %#x\n",result[1]);
    	printf("data -> %#x\n",*(int*)(result[1]));
    	dlclose(handle);
    
    	int fd = open("./local_out_data.yuv",2);
    	char*buffer = result[1];
    	int len = result[0];
    	int err = write(fd,buffer,len);
    	printf("err->%d\n",err);
    	close(fd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    这样就可以正常获取so层的输入输出、内存数据。

    当然你会觉得Frida会更方便~
    但是Frida并不是持久的,通过这种方式你可以毫不费力的将它工具化。
    博主最近开发了一款基于Frida的工具,现在主要用来调试SO文件,也是非常适合该篇文章的背景下使用,详细了解的话在这里: https://github.com/github546229768/YJ
    工具使用截图如下:
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    结语

    上述脚本灵感来源于一次实战积累

  • 相关阅读:
    图文详细解决IDEA使用Debug模式启动项目一直转圈圈跑起不来(亲测可以)
    【JVM篇】有哪些垃圾回收算法
    华裔科学家Ashe教授对涉嫌造假的Nature论文的正面回应
    嵌入式 ADC使用手册完整版 (188977万字)(附源码详细篇)
    无门槛访问ChatGPT升级版-数据指北AI
    html实现个人空间主页(附源码)
    C++设计模式之装饰者模式(结构型模式)
    基于增强蛇优化算法求解单目标优化问题附matlab代码
    正则表达式(JAVA)
    Pinia的使用
  • 原文地址:https://blog.csdn.net/csdn546229768/article/details/125902239