ELF解析06 - 入口参数与傀儡进程

发布时间:2024年01月18日

04 里面的重定位表没讲完,这里继续,我们说过重定位的类型有很多,04 里面基本只介绍了一种,就是 plt 表。重定位不仅仅发生在代码里面,还会发生在数据里面,比如so程序里面对一些全局变量的引用,它们的重定位信息是放在另外一个表里面:

对应 section 的:

里面的结构体与 rela.plt 的结构体是一样的。以第一项为例:

0x05D690 表示符号地址。

0x0403 表示符号重定位类型。由于它不属于符号表,所以索引为0。0x403 表示的重定位类型是:

#define?R_GENERIC_RELATIVE??R_AARCH64_RELATIVE

对应的逻辑:

case?R_GENERIC_RELATIVE:
????*reinterpret_cast<ElfW(Addr)*>(reloc)?=?(load_bias?+?addend);

load_bias,就是第一个可加载段所在的位置,需要是页对齐的,否则:

load_bias?=?phdr0_load_address?-?page_start(phdr0->p_vaddr)

所以,我们只需要在这个位置按照上面的方式计算好地址填进去就行。

打印一下 ls 这个 elf 有哪些类型:

rela.dyn?type?=?403?
rela.dyn?type?=?401

发现只有两种类型,非常nice。

0x401的逻辑如下(8.0 逻辑):

case?R_GENERIC_GLOB_DAT:
????????count_relocation(kRelocAbsolute);
????????MARK(rel->r_offset);
????????TRACE_TYPE(RELO,?"RELO?GLOB_DAT?%16p?<-?%16p?%s\n",
???????????????????reinterpret_cast<void*>(reloc),
???????????????????reinterpret_cast<void*>(sym_addr?+?addend),?sym_name);
????????*reinterpret_cast<ElfW(Addr)*>(reloc)?=?(sym_addr?+?addend);

8.0 相比 2.0 没啥变化,就是多了一个 addend。

傀儡进程

实验失败了,还没分析出问题,可以不用往下看

前面,我们分析了重定位表

  • rela.plt

  • rela.dyn

ls elf 文件里面的重定位类型一共有3种:

  • 0x401

  • 0x402

  • 0x403

我们可以手动将其重定位:

if?(rela_type?==?0x402)
{
????uint64_t?*sym_save_addr?=?BASE?+?rela_ptr->r_offset;
????uint64_t?relocation_type?=?rela_ptr->r_info?&?0xFFFFFFFF;
????uint64_t?symbol_index?=?rela_ptr->r_info?>>?32;
????uint32_t?table_index?=?symtab_addr[symbol_index].st_name;
????char?*symbol_name?=?strtab_addr?+?table_index;

????//?find?symbol?addr
????for?(size_t?i?=?0;?i?<?needed_so_count;?i++)
????{
????????void?*symbol_addr?=?dlsym(needed_so_handles[5],?symbol_name);
????????if?(symbol_addr?!=?NULL)
????????{
????????????*(uint64_t?*)sym_save_addr?=?(uint64_t)symbol_addr?+?rela_ptr->r_addend;
????????????printf("so?name?=?%s,?symbol?name?=?%s,?symbol_addr?=?%10lx\n",?needed_so_names[i],?symbol_name,?*sym_save_addr);
????????????break;
????????}
????}
}

if?(rela_type?==?0x0403)
{
????uint64_t?*target_addr?=?BASE?+?p_rela->r_offset;

????*target_addr?=?p_rela->r_addend?+?BASE;
????p_rela++;
}
else?if?(rela_type?==?0x0401)
{
????//?现找到符号表地址,再加上?r_addend
????//?find?symbol?addr
????uint64_t?symbol_index?=?p_rela->r_info?>>?32;
????uint32_t?table_index?=?symtab_addr[symbol_index].st_name;
????char?*symbol_name?=?strtab_addr?+?table_index;
????for?(size_t?i?=?0;?i?<?needed_so_count;?i++)
????{

????????void?*symbol_addr?=?dlsym(needed_so_handles[5],?symbol_name);
????????if?(symbol_addr?!=?NULL)
????????{
????????????uint64_t?*target_addr?=?BASE?+?p_rela->r_offset;
????????????*target_addr?=?p_rela->r_addend?+?symbol_addr;

????????????printf("so?name?=?%s,?symbol?name?=?%s,?symbol_addr?=?%10lx\n",?needed_so_names[i],?symbol_name,?*target_addr);
????????????break;
????????}
????}

????/*?code?*/
}

主要是利用 dlopen 与 dlsym 这两个函数,从依赖的 so 文件里面定位到符号地址。拿到地址之后,就根据重定位类型回填符号地址就行。

做完之后,我们就可以主动调用入口地址,然后执行这个 elf 文件:

typedef?int?(*START)(int?x0,?int?x1,?int?x2,?int?x3,?int?x4,?int?x5,?int?x6,?int?x7,?int?argc,?char?*argv,?...);

START?fun?=?(START)(ehdr->e_entry?+?BASE);

fun(0,?0,?0,?0,?0,?0,?0,?0,
???????1,?cmd,
???????0,
???????0);

由于ls在执行的时候,是使用的栈来传递参数,所以我们使用额外的8个参数来占用寄存器,后面真正的参数 argc,argv 放在第9 与 10 位置,这样这两个参数就分配在了栈中。

我们将 ls 入口地址改成死循环,看一下栈中的数据,好模仿参数传递:

?

栈中,第一个参数是 1,第二个参数是一个地址,去地址看看:

第二个参数是字符串:/data/local/tmp/ls

后面是一些环境变量。也是放在栈中的。

参数都模仿好之后,调用入口地址,发现报了Segmentation fault?,用 ?IDA 调试发现是一个空指针错误,寄存器访问了一个 0 地址,搞到晚上1点了都没找到原因先放着了。

如果能成功运行,我们就可以将 ls 这个程序运行到我们的进程中,并执行里面的功能,非常的神奇。

文章来源:https://blog.csdn.net/a5right/article/details/135669647
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。