吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2224|回复: 9
收起左侧

[Android 原创] 安卓虚表hook的简单实现

  [复制链接]
jbczzz 发表于 2024-5-10 15:29
本帖最后由 jbczzz 于 2024-5-10 17:11 编辑

0x0 虚表的简单介绍
虚表(Virtual Table)是一种在面向对象编程中常见的概念,特别是在使用C++等支持多态性(Polymorphism)的编程语言中。它是一种数据结构,用于实现类的动态绑定(Dynamic Binding)和多态性。在C++中,虚表通常与虚函数(Virtual Function)一起使用。在具有继承关系的类中,当一个基类指针或引用指向一个派生类对象时,如果基类声明了虚函数,那么通过这个指针或引用调用虚函数时,将会根据实际对象的类型来确定调用的是哪个函数。这种确定发生在运行时,被称为动态绑定。虚表的作用就是为实现动态绑定提供支持。每个带有虚函数的类都有一个虚表,其中存储了指向各个虚函数的指针。当通过基类指针或引用调用虚函数时,实际上是通过虚表来确定应该调用哪个函数。在编译时,编译器会根据类的虚函数情况生成虚表,并在每个对象的内存布局中添加一个指向该虚表的指针。这个指针被称为虚表指针(vptr)。当调用虚函数时,实际上是通过对象的虚表指针找到对应的虚表,然后再根据函数在虚表中的位置来调用正确的函数。总的来说,虚表是一种实现多态性和动态绑定的机制,它使得在面向对象编程中能够以更灵活的方式使用继承和多态性。
0x1 虚表的正向逆向示例
虚表的一个简单示例源码:
[C++] 纯文本查看 复制代码
class A
{
public:
    void func1() { cout << "A func1" << endl; }
    virtual void vfunc1() { cout << "A vfunc1" << endl; }
private:
    int m_a;
    int m_b;
};

虚表在内存中的图示:
image.png
多继承中源码:
[Asm] 纯文本查看 复制代码
class A
{
public:
    void func1() { cout << "A func1" << endl; }
    virtual void vfunc1() { cout << "A vfunc1" << endl; }
private:
    int m_a;
    int m_b;
};

class B : public A
{
public:
    void func1() { cout << "B func1" << endl; }
    virtual void vfunc2() { cout << "B vfunc2" << endl; }
private:
    int m_a;
};

内存图示:
image.png

https://zhuanlan.zhihu.com/p/353260837

IDA中逆向分析时里的虚表例子:
image.png
image.png
*(_QWORD *)v21为读取虚表,+0x28LL为虚表偏移(i*0x8)
0x2 虚表hook的实现
综上所示,我们想要实现虚表hook的方式就很明了了,内存加载后虚表的位置是不会改变的,只需要用mprotect把想要hook的虚表所在内存页的属性修改为rw,然后把hook函数的地址写入指定虚表地址,写入后还原页属性即可
[Asm] 纯文本查看 复制代码
void VFuncHook(uintptr_t VFunc, uintptr_t hookAddr)
{
    // 假设 PAGE_SIZE 是系统的页面大小
    size_t page_size = sysconf(_SC_PAGESIZE);
    // 计算 addr 所在页的起始地址
    void *page_start = (void *)((uintptr_t)VFunc & ~(page_size - 1));
    // 计算要修改的地址的偏移量
    size_t offset = (uintptr_t)VFunc - (uintptr_t)page_start;
    // 新的权限:不可读写可执行
    int prot = PROT_READ | PROT_WRITE;
    // 使用 mprotect 修改内存权限
    if (mprotect(page_start, page_size, prot) == -1)
    {
        LOGD("[-] mprotect Erro");
        exit(EXIT_FAILURE);
    }
    Write<uintptr_t>(VFunc, hookAddr);
    if (mprotect(page_start, page_size, PROT_READ) == -1)
    {
        LOGD("[-] mprotect Erro");
        exit(EXIT_FAILURE);
    }
    // LOGD("[+] Hook");
}

[C++] 纯文本查看 复制代码
void hookFunc(){
 LOGD("FuncHooked");
}

[C] 纯文本查看 复制代码
void *main_thread(){
VFuncHook(VFuncPtr,(uintptr_t)hookFunc);
}

0x3 小结
虚表hook相较于inlinehook来说没有修改代码段,无法通过对代码段crc校验来检测,一般检测的话需要分析类虚表中有哪些引用,通过so地址+偏移计算出虚表函数中的地址并通过算法生成一个校验值,然后需要检测的时候可以选择比对整个虚表的校验值或者校验某些函数地址是否被修改。还可以通过hook mprotect,判断当前修改的地址是否合法,回溯调用栈,看调用的地址是否有异常。
说起mprotect,还有一种可以通过mprotect实现基于页面异常的hook(其实就是软件断点)这种hook可以实现不会触发crc校验。虽然linux mprotect只能修改整个页面,但是发生异常的时候handler接收的siginfo里会有发生异常的地址信息,能准确的对某个地方进行hook。这个东西后面有空的时候可以作为学习记录一下在自己实现的过程中发现的一些小技巧和坑。

免费评分

参与人数 8威望 +1 吾爱币 +27 热心值 +8 收起 理由
allspark + 1 + 1 用心讨论,共获提升!
lingyun011 + 1 + 1 热心回复!
qwertyuiop1822 + 1 + 1 热心回复!
debug_cat + 1 + 1 谢谢@Thanks!
wang917 + 1 + 1 我很赞同!
HUAJIEN + 1 + 1 谢谢@Thanks!
正义天下 + 1 + 1 谢谢@Thanks!
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

正义天下 发表于 2024-5-10 22:36
谢谢分享~
xixicoco 发表于 2024-5-10 23:08
实际过程中遇到过的,不知道frida hook如何实现呢?
chuanxing1701 发表于 2024-5-11 14:38
 楼主| jbczzz 发表于 2024-5-11 14:56
如果要调用原函数的话 return reinterpret_cast<void *(*)(paramType,paramType...)>(originalFuncAddr)(a1,a2....);
zjh889 发表于 2024-5-12 00:27
很好的东西,谢谢大师分享!
wuweiwuwei 发表于 2024-5-12 08:29
感谢分享
starryskyhello 发表于 2024-5-12 18:03
学习到了,无敌
z9644 发表于 2024-5-12 22:11
谢谢谢分享,学习一下
WuDiZhanShen 发表于 2024-11-16 13:19
个人觉得应该在写入新函数前记录原虚函数地址,这样新旧函数就都能使用。和inlineHook效果一样。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-14 08:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表