文章目录
写在前面
本篇是老表带你学linux kernel pwn系列的第四篇,这里用到的题目是内核态uaf漏洞。
第一章 基础知识
1.1 ida中诡异变量的意义
https://www.dazhuanlan.com/2019/12/30/5e094e9beea31/
第二章 目标分析与准备
2.1 逆向分析baby.ko
分析目标驱动文件,首先从init_module()
入手,然后分析主功能函数的逻辑。
2.1.1init_moudle()
注册了一个misc设备``,
__int64 init_module()
{
_fentry__();
return misc_register(&off_200);
}
2.1.2 ioctl
驱动文件分析
ioctl
函数特别长我们慢慢来梳理一下
-
ioctl的初始化部分
__int64 __fastcall ioctl(__int64 fd, __int64 command) { __int64 args1; // rdx __int64 info; // r13 __int64 chunks; // rbx __int64 i; // rax __int64 can_use_id; // r12 __int64 chunk_addr; // rax __int64 v9; // rdx __int64 edit_idx; // rax __int64 edit_chunk_ptr; // rsi __int64 v12; // rax __int64 idx; // rax __int64 free_chunk_ptr; // rdi _fentry__(fd, command); info = args1; chunks = kmem_cache_alloc_trace(kmalloc_caches[4], 0x6000C0LL, 16LL); copy_from_user(chunks, info, 16LL);
看的时候顺手函数名、便令名做修改,方便进行后面的分析
上来
ioctl
的给参数设备描述符*fd
,运行命令0x6008
之类的第18行,申请内核chunk
第19行,把
info
也就是ioctl
中传进来的第三个变量进行copy_from_user
做个拷贝。传进来的大小16个字节。
-
内核堆块释放部分
case 0x6008: // free 函数 idx = *(signed int *)(chunks + 8); if ( (unsigned int)idx <= 31 ) { free_chunk_ptr = buf[idx]; if ( free_chunk_ptr ) kfree(free_chunk_ptr); } break;
第2行,说明传进来的结构体offset8处表示对块的索引,变量类型为
__int64
.buf是一个全局的指针数组,用来存申请的内核指针。
第3-9行,当前索引不超过31时,寻找buf中idx处的指针,如果不为空,就释放掉。
-
函数调用功能部分
case 0x6009: edit_idx = *(signed int *)(chunks + 8); if ( (unsigned int)edit_idx <= 31 ) { edit_chunk_ptr = buf[edit_idx]; if ( edit_chunk_ptr ) { rax_value = *(_QWORD *)(edit_chunk_ptr + 64); _x86_indirect_thunk_rax(*(_QWORD *)(edit_chunk_ptr + 0x38), edit_chunk_ptr, 0x48LL); } } break;
__x86_indirect_thunk_rax
直接jmp
到rax
相当于系统调用直接call。1-12功能为:根据chunks的id定位buf数组中需要使用的内核堆块地址
edit_chunk_ptr
然后把offset为0x40
出的值传给eax,然后执行call eax
-
内核堆块申请部分
case