补库函数(五)

  1. 补库函数(五)
    1. 一、引言
    2. 二、Free
    3. 三、munmap
  • 参考
  • 补库函数(五)

    一、引言

    本篇记录内存相关的库函数、系统调用处理。

    二、Free

    free函数是重要的内存释放函数,原型如下。

    void free(void *ptr)
    

    Unidbg在内存管理方面存在一些问题,在内存的释放上尤为明显,munmap以及free都有不低的出错概率。先讨论free,常见报错如下。

    Invalid address 0x40175000 passed to free: value not allocated
    [crash]A/libc: Invalid address 0x40175000 passed to free: value not allocated
    

    遇到这个报错,最简单的处理方法就是hook free函数,替换它的实现,让它什么都不做就直接返回。这里我用Whale来实现,让它直接返回,且返回值是0,即释放成功之意。

    public void patchFree(){
        IWhale whale = Whale.getInstance(emulator);
        Symbol free = emulator.getMemory().findModule("libc.so").findSymbolByName("free");
        whale.inlineHookFunction(free, new ReplaceCallback() {
            @Override
            public HookStatus onCall(Emulator<?> emulator, long originFunction) {
                System.out.println("WInlineHookFunction free=" + emulator.getContext().getPointerArg(0));
                return HookStatus.LR(emulator, 0);
            }
        });
    }
    

    你可以使用xHook、HookZz或者其他感兴趣的Unidbg内置Hook方案。你可能会觉得这么做有些浪费,很多应该释放的内存没有得到释放,那么也可以根据报错,对报错的待释放内存做处理,比如只有指针地址是0x40175000、0x40176000 的两处释放失败时,代码如下。

    public void patchFree(){
        IWhale whale = Whale.getInstance(emulator);
        Symbol free = emulator.getMemory().findModule("libc.so").findSymbolByName("free");
        whale.inlineHookFunction(free, new ReplaceCallback() {
            @Override
            public HookStatus onCall(Emulator<?> emulator, long originFunction) {
                System.out.println("WInlineHookFunction free=" + emulator.getContext().getPointerArg(0));
                long addr = emulator.getContext().getPointerArg(0).peer;
                if(addr == 0x40175000 | addr == 0x40176000){
                    return HookStatus.LR(emulator, 0);
                }else {
                    return HookStatus.RET(emulator, originFunction);
                }
            }
        });
    }
    

    三、munmap

    简单谈谈munmap,它的实现位于src/main/java/com/github/unidbg/spi/AbstractLoader.java。它出现的频率太高了,这里有个样本进行展示。

    样本:wanggetong.apk(附件详见参考)。Unidbg示例代码如下

    package com.munmap;
    
    import com.github.unidbg.AndroidEmulator;
    import com.github.unidbg.arm.backend.Unicorn2Factory;
    import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
    import com.github.unidbg.linux.android.AndroidResolver;
    import com.github.unidbg.linux.android.dvm.AbstractJni;
    import com.github.unidbg.linux.android.dvm.DalvikModule;
    import com.github.unidbg.linux.android.dvm.DvmObject;
    import com.github.unidbg.linux.android.dvm.VM;
    import com.github.unidbg.memory.Memory;
    
    import java.io.File;
    
    public class MUNMAP extends AbstractJni {
        private final AndroidEmulator emulator;
        private final DvmObject BlackBox;
        private final VM vm;
    
        public MUNMAP() {
            emulator = AndroidEmulatorBuilder
                    .for32Bit()
                    .addBackendFactory(new Unicorn2Factory(true))
                    .build();
    
            Memory memory = emulator.getMemory();
            memory.setLibraryResolver(new AndroidResolver(23));
            vm = emulator.createDalvikVM(new File("D:\\Compilation_Enviroment\\unidbg-master\\unidbg-android\\src\\test\\java\\com\\munmap\\wanggetong.apk"));
            vm.setJni(this);
            vm.setVerbose(true);
            DalvikModule dm = vm.loadLibrary("msec", true);
            BlackBox = vm.resolveClass("com.autohome.mainlib.common.util.BlackBox").newObject(null);
            dm.callJNI_OnLoad(emulator);
        }
    
        public static void main(String[] args) {
            MUNMAP wgt = new MUNMAP();
        }
    }
    

    运行报错
    image-20250604185128267

    报错中的0xffffffff是什么,是Unidbg不支持样本所采用的JNI版本?不是。

    让我们回忆一下JNI_OnLoad的开发习惯,比如下面这样。

    //注册函数映射
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
        JNIEnv *pEnv = NULL;
        //获取环境
        jint ret = vm->GetEnv((void**) &pEnv, JNI_VERSION_1_6);
        if (ret != JNI_OK) {
            LOGE("jni_replace JVM ERROR:GetEnv");
            return -1;
        }
        //在{}里面进行方法映射编写,第一个是java端方法名,第二个是方法签名,第三个是c语言形式签名(括号内表示方法返回值)
        JNINativeMethod g_Methods[] = {{"jniOnLoadTest", "()V", (void*) onLoadTest},
                                       {"jniOnload1", "(Lzqc/com/example/Person;)Ljava/lang/String;", (jstring*)onloadTest1}
        };
        jclass cls = pEnv->FindClass("zqc/com/example/NativeTest");
        if (cls == NULL) {
            LOGE("FindClass Error");
            return -1;
        }
        //动态注册本地方法
        ret = pEnv->RegisterNatives(cls, g_Methods,sizeof(g_Methods) / sizeof(g_Methods[0]));
        if (ret != JNI_OK) {
            LOGE("Register Error");
            return -1;
        }
        //返回版本
        return JNI_VERSION_1_6;
    }
    

    JNI_OnLoad的返回值必须是JNI的版本信息,目前支持1.4、1.6、1.8三个版本,当返回三者之一时表示一切正常,如果像表示JNI_OnLoad处理出错的意思,只需要返回其他值即可,按照惯例一般返回-1,也就是十六进制的0xffffffff,比如上面贴的demo代码就是这样。

    因为这里Unidbg的报错,并不是说样本真采用了什么其他的JNI版本,而只是表明,样本在早先的JNI_OnLoad处理里遇到了问题,直接走到了return -1

    所以解决它的问题就是日志往上翻,找到最早报错在哪里
    image-20250604185707554

    可以看到是munmap报错,点进去看看,发现是下图这样一个位置
    image-20250604185856008

    if (segment == null || segment.size < aligned) {
        throw new IllegalStateException("munmap aligned=0x" + Long.toHexString(aligned) + ", start=0x" + Long.toHexString(start));
    }
    

    表面是munmap的处理有问题,本质上依然是Unidbg在内存管理、模块加载相关的处理逻辑上不健全,所以这里主动抛出了一个异常。

    如果SO加固,比如加个UPX壳,那么往往就会遇到这个报错,如果你只想绕过这个问题,那么只需要注释掉这个throw即可,如果你想彻底解决Unidbg在这个逻辑下的问题,那么需要对比Unidbg ElfLoader逻辑和Linker的差异,找到这个问题的根源。

    我这里简单注释一下
    image-20250604190255452

    然后运行就进入正常的补JNI逻辑。

    image-20250604190428290

    感兴趣的读者可以自行尝试。

    mmap和munmap相关的问题,在Unidbg中很容易出现,而且往往都是发生在加固SO的解密和内存释放阶段,这里要阐述两点。

    一是这说明Unidbg在内存加载方面的处理逻辑做不到位,有漏洞。

    二是不能因此认为**“Unidbg不能处理加固的SO”**,只不过是SO加固和解密时会更多的使用到系统层的各种功能和特性,而Unidbg对它们的支持度不那么高,相对来说更容易出问题罢了。

    参考

    [补库函数(五)](https://www.yuque.com/lilac-2hqvv/xdwlsg/bzoykwvuim3hkz2o?# 《补库函数(五)》)


    转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1621925986@qq.com

    💰

    ×

    Help us with donation