知识点总结"/>
ret2dl_resolve 知识点总结
ret2dl-resole 是通过伪造到 .rel.plt , .dynsym, .dynstr中表项中的偏移以及伪造这三个段中的数据结构来达到调用system 函数的目的
这三个段的信息都可以在IDA中 DYNAMIC 段中找到
DT_STRTAB -> .dynstr
DT_SYMTAB -> .dynsym
DT_JMPREL -> .rel.plt
箭头处压入的是 printf 函数 在 .rel.plt 中的偏移
jump 8048420 <.plt>
跳转到 .rel.plt 中的表头, 并根据上面压入的偏移量找到 printf函数在 .rel.plt 中的表项,
该表项结构为:
typedef struct
{Elf32_Addr r_offset; //对应函数got表项的地址Elf32_Word r_info; //前24位为 在 .dynsum 段中的偏移量, 后八位 固定为 0x07
} Elf32_Rel; //共占八个字节
.rel.plt 的地址在 DYNAMIC段中可以看到的是:0x080483b4
在peda 中用 x/20xw 0x080483b4
printf 压入的偏移量为0,故为 .rel.plt 中为第一项, 图中的 0x00000107 就是 r_info 的值, (r->info)>>8(即高24位) 是 printf 字符串在 .dynsym 中的偏移,根据这个偏移加上 .dynsym 的起始地址就可以找到printf 在 .dynsym 中的表项
根据 readelf -S warrior_tales 查看各个段的信息,可以看到.dynsym 段的地址为 0x080481d8
.dynsym 中表项的结构为:
typedef struct
{Elf32_Word st_name; //符号名,是相对.dynstr起始的偏移Elf32_Addr st_value;Elf32_Word st_size;unsigned char st_info; //对于导入函数符号而言,它是0x12unsigned char st_other;Elf32_Section st_shndx;
}Elf32_Sym; //对于导入函数符号而言,其他字段都是0
//共占16个字节
在peda 中 用 x/48b 0x080481d8 查看表项中各种数据的值
根据r_info>>8 等于 1 可以发现printf 的表项 在 .dynsym 中的地址为0x80481e8,那么st_name 的值为 0x3c
peda 中用 x/20s 0x80482b8(.dynstr) 查看.dynstr 的值
根据0x80482b8 + 0x3c = 0x80482f4 找到printf 字符串的地址
之后再根据找到的这个字符串 动态链接器 去glibc库中 找到printf函数,然后将printf函数的地址填入到got表项中
最后调用函数
整个流程大致是:
假设 某函数字符串 在 .rel.plt 中的偏移 为 n ,
那么重定位过程就是
根据这个n 找到在 .rel.plt 中的表项 ,再由 r_info 找到在 .dynsym 的表项, 再由 st_name 跳转到 .dynstr , 然后找到相应的字符串, 最后调用函数
retdl_resolve的思路就是伪造n, r_info , st_name,, 由于
**addr(.rel.plt) + n, ** 不会检查是否越界,所以n 可以由我们自己设置,跳转到一片可写的区域,如bss 段, 在这片区域上伪造 .rel.plt , .dynsym , .dynstr的表项, 最终实现调用 system函数
还有就是在 伪造这些 数据中用到了切栈的技术, 通过两次的:
gadget
leave -> mov esp, ebp pop ebp
ret
可以让bss段 作为新的栈段
第一次gadget 使得ebp 的值为bss 段的地址
第二次gadget通过 mov esp, ebp 使esp指向bss段,这样bss段地址就成了栈顶,至于ebp为什么值不怎么关心
参考的博客:
.htm
更多推荐
ret2dl_resolve 知识点总结
发布评论