passself 2024-12-27 19:09 采纳率: 42.9%
浏览 42
已结题

android native carsh

android native中,获取到java方法的jmethod id ,然后做一个偏移,想判断偏移后的一些数据。
结果转化一下crash了,也判断了当前指针是否可用,可是还是报错了

auto *p = (long long *)(((char *)self_art_method)+entry_point_offset);

//验证这个指针的方法也通过了:
bool checkSelfp(long long* selfp) {
    // 检查地址有效性
    if (selfp == nullptr) {
        LOGD("selfp is null");
        return false;
    }

    // 检查地址对齐
    if ((uintptr_t)selfp % alignof(long long) != 0) {
        LOGD("selfp is not aligned: %p", (void*)selfp);
        return false;
    }

    // 检查内存权限
    size_t page_size = getpagesize();
    void* page_start = (void*)((uintptr_t)selfp & ~(page_size - 1));
    if (mprotect(page_start, page_size, PROT_READ) == -1) {
        LOGD("Failed to set memory permissions: %s", strerror(errno));
        return false;
    }

    // 验证内存内容
    long long value;
    try {
            value = *selfp;  // 尝试读取内存
    } catch (...) {
        LOGD("Failed to read memory at selfp: %p", (void*)selfp);
        return false;
    }
    LOGD("Value at selfp: %lld", value);

    return true;
}

//但是,这行代码报错了:
unsigned int ins1 = *(unsigned int *) *p;

crash内容是:Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x55 in tid 14287
有知道的巨佬吗?

  • 写回答

33条回答 默认 最新

  • caozhenyu650 2024-12-29 12:33
    关注

    问题 android native carsh 解答如下,
    借助GPT4-pro和本人整理后的结果,如有帮助,还请采纳!
    在你提供的代码中,出现了段错误(SIGSEGV),通常是由于访问了无效的内存地址。根据你提供的代码和错误信息,我们可以逐步分析问题所在,并提供一些解决思路。

    错误原因分析

    错误信息中提到:

    Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x55 in tid 14287
    

    这意味着程序尝试访问地址 0x55,但是该地址并不映射到有效的内存区域,导致了段错误(SEGV_MAPERR)。这种情况通常有以下几种原因:

    1. 无效指针:指针 p 可能指向一个无效的内存地址。
    2. 指针类型错误:偏移量计算或指针解引用时,存在类型错误,导致错误的内存地址被访问。
    3. 内存未初始化:在你尝试访问的内存地址之前,可能未正确初始化或分配内存。

    问题代码分析

    在你提供的代码中,报错发生在这一行:

    unsigned int ins1 = *(unsigned int *) *p;
    

    这里的代码可以分解为两部分:

    1. *p:表示指针 p 所指向的值。由于你在 p 的计算中做了偏移,它应当是一个指向 long long 类型的指针,所以 *p 取出的值应该是 long long 类型。
    2. (unsigned int *) *p:将 *p 的值转换为指向 unsigned int 类型的指针。
    3. *(unsigned int *) *p:解引用转换后的指针,尝试读取 unsigned int 类型的数据。

    从这里可以推测出问题所在:你将 *p 解引用并转换为 unsigned int *,但是 *p 的值可能并不是一个有效的指针,或者它指向的内存不符合 unsigned int 类型的数据布局。这可能导致访问无效地址或类型不匹配,从而触发段错误。

    解决思路

    1. 检查指针的有效性

    虽然你已经在 checkSelfp 函数中检查了指针有效性、对齐性和内存权限,但有几个地方需要改进:

    • 内存保护(mprotect):你使用 mprotect 设置内存权限时,应该确认当前内存区域的权限是否足够。你当前的代码只设置了 PROT_READ,如果你需要写入该内存区域,可能需要设置为 PROT_WRITE
    • 内存对齐:你已经检查了指针的对齐性,但如果 long long 类型在某些架构上不是对齐的(如某些32位架构),你可能需要调整内存对齐方式。

    2. 调整指针解引用

    你应该确保指针 *p 指向的数据类型和你想要访问的数据类型一致。假设偏移后的地址应该指向一个有效的内存区域,并且该区域的数据类型是你需要的类型。你可以尝试先打印 *p 的值,确保它是一个有效的指针:

    long long addr = *p;
    LOGD("Pointer p points to: %p", (void*)addr);
    

    这样可以帮助你确认 *p 是否指向了一个有效的地址。

    3. 使用 uintptr_t 进行指针运算

    为了避免类型转换出错,建议使用 uintptr_t 来进行指针偏移运算。uintptr_t 是一个整型,它能够存储指针的值,并且避免了指针类型转换可能出现的错误。例如:

    uintptr_t addr = (uintptr_t)p + entry_point_offset;
    unsigned int ins1 = *(unsigned int *)addr;
    

    这样可以确保你的偏移量运算是基于原始地址进行的,而不是类型不匹配。

    4. 确保指针地址是有效的

    除了你在 checkSelfp 中的检查,建议在解引用指针之前再做一次检查,确认 *p 指向的地址是有效的。例如:

    if (p == nullptr || *p == nullptr) {
        LOGD("Pointer p or dereferenced pointer is nullptr");
        return false;
    }
    

    这将帮助你捕获一些不明显的空指针问题。

    5. 捕获异常

    尽管你已经用 try-catch 块包裹了内存读取操作,但在 C++ 中,访问非法内存不会抛出异常,反而会导致程序崩溃。因此,try-catch 可能无法捕捉到错误。你可以通过一些其他方法来避免崩溃,如使用 sigaction 捕获信号,或通过 setjmplongjmp 实现异常处理。

    最终改进方案

    综合以上分析,下面是一个改进后的代码示例:

    bool checkSelfp(long long* selfp) {
        // 检查地址有效性
        if (selfp == nullptr) {
            LOGD("selfp is null");
            return false;
        }
        // 检查地址对齐
        if ((uintptr_t)selfp % alignof(long long) != 0) {
            LOGD("selfp is not aligned: %p", (void*)selfp);
            return false;
        }
        // 检查内存权限
        size_t page_size = getpagesize();
        void* page_start = (void*)((uintptr_t)selfp & ~(page_size - 1));
        if (mprotect(page_start, page_size, PROT_READ | PROT_WRITE) == -1) {
            LOGD("Failed to set memory permissions: %s", strerror(errno));
            return false;
        }
        // 验证内存内容
        try {
            long long value = *selfp;  // 尝试读取内存
            LOGD("Value at selfp: %lld", value);
        } catch (...) {
            LOGD("Failed to read memory at selfp: %p", (void*)selfp);
            return false;
        }
        return true;
    }
    
    unsigned int readFromPointer(long long *p, size_t entry_point_offset) {
        uintptr_t addr = (uintptr_t)p + entry_point_offset;
        unsigned int *ins1_ptr = (unsigned int *)addr;
        
        // 添加检查,确认地址有效
        if (ins1_ptr == nullptr) {
            LOGD("Pointer is null after offset calculation");
            return 0;
        }
    
        unsigned int ins1 = *ins1_ptr;
        return ins1;
    }
    

    总结

    1. 指针类型和偏移:确保偏移后指针的类型与实际数据类型一致,避免非法访问。
    2. 内存访问权限:通过 mprotect 设置合适的内存权限,确保访问操作不被操作系统阻止。
    3. 指针有效性检查:在解引用指针前,进行更严格的检查,确保地址有效且数据对齐。
    4. 错误处理:建议使用更为合适的错误处理机制,避免因为非法内存访问导致程序崩溃。

    通过这些改进,你应该能够避免当前的段错误,并提高程序的稳定性和健壮性。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(32条)

报告相同问题?

问题事件

  • 系统已结题 1月10日
  • 已采纳回答 1月2日
  • 修改了问题 12月27日
  • 创建了问题 12月27日