返回
如何在 ELF 文件中计算 PLT 的虚拟地址并检测篡改
Linux
2024-03-28 17:09:54
利用 sh_addr 计算 PLT 虚拟地址
计算 PLT 虚拟地址
当 ELF 文件加载到内存时,它的可执行代码段(即 PLT)会被分配一个虚拟地址。要计算 PLT 的虚拟地址,我们需要将 PLT 的 sh_addr 与动态链接器用于加载 ELF 文件的加载基址相加。
确定加载基址
加载基址存储在 ELF 文件头中的 e_phoff
字段中。它通常是一个随机地址,位于 0x00400000
和 0x04000000
之间。
读取程序头表
程序头表包含有关 ELF 文件各部分的信息。其中,PT_LOAD
程序头定义了可执行代码段的加载地址。
查找 PT_LOAD
程序头
通过遍历程序头表,我们可以找到类型为 PT_LOAD
的程序头,它包含 PLT 的加载地址。
计算 PLT 虚拟地址
最后,我们将 PLT 的 sh_addr
与加载基址相加,得到 PLT 的虚拟地址。
PLT 虚拟地址 = PLT 的 sh_addr + 加载基址
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <elf.h>
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <ELF file>\n", argv[0]);
return EXIT_FAILURE;
}
// 读取 ELF 文件头
FILE *fp = fopen(argv[1], "rb");
if (fp == NULL) {
perror("fopen");
return EXIT_FAILURE;
}
Elf64_Ehdr elf_hdr;
if (fread(&elf_hdr, sizeof(elf_hdr), 1, fp) != 1) {
perror("fread");
fclose(fp);
return EXIT_FAILURE;
}
// 校验 ELF 文件类型
if (elf_hdr.e_ident[EI_MAG0] != ELFMAG0 ||
elf_hdr.e_ident[EI_MAG1] != ELFMAG1 ||
elf_hdr.e_ident[EI_MAG2] != ELFMAG2 ||
elf_hdr.e_ident[EI_MAG3] != ELFMAG3) {
fprintf(stderr, "Invalid ELF file\n");
fclose(fp);
return EXIT_FAILURE;
}
// 获取程序头表偏移量
off_t ph_offset = elf_hdr.e_phoff;
// 读取程序头表
Elf64_Phdr *phdr_table = malloc(elf_hdr.e_phnum * sizeof(Elf64_Phdr));
if (phdr_table == NULL) {
perror("malloc");
fclose(fp);
return EXIT_FAILURE;
}
if (fseek(fp, ph_offset, SEEK_SET) != 0) {
perror("fseek");
fclose(fp);
free(phdr_table);
return EXIT_FAILURE;
}
if (fread(phdr_table, sizeof(Elf64_Phdr), elf_hdr.e_phnum, fp) != elf_hdr.e_phnum) {
perror("fread");
fclose(fp);
free(phdr_table);
return EXIT_FAILURE;
}
// 查找 PT_LOAD 程序头
Elf64_Phdr *pt_load_phdr = NULL;
for (int i = 0; i < elf_hdr.e_phnum; i++) {
if (phdr_table[i].p_type == PT_LOAD) {
pt_load_phdr = &phdr_table[i];
break;
}
}
// 未找到 PT_LOAD 程序头
if (pt_load_phdr == NULL) {
fprintf(stderr, "PT_LOAD program header not found\n");
fclose(fp);
free(phdr_table);
return EXIT_FAILURE;
}
// 计算 PLT 的虚拟地址
uint64_t plt_virtual_address = pt_load_phdr->p_vaddr + elf_hdr.e_shoff;
// 打印 PLT 的虚拟地址
printf("PLT virtual address: 0x%lx\n", plt_virtual_address);
// 释放内存
fclose(fp);
free(phdr_table);
return EXIT_SUCCESS;
}
检测 PLT 或 GOT 的篡改
共享库攻击
攻击者可以篡改共享库中的 PLT 或 GOT(执行挂钩),以劫持代码执行。要检测这种类型的攻击,我们可以比较内存中的 PLT 或 GOT 与 ELF 文件中存储的预期值。
检测方法
- 解析进程内存中的 ELF 文件。
- 获取 PLT 或 GOT 节的
sh_addr
。 - 计算 PLT 或 GOT 的虚拟地址。
- 访问内存中的 PLT 或 GOT。
- 将内存中的 PLT 或 GOT 与 ELF 文件中存储的 PLT 或 GOT 进行比较。
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <elf.h>
#include <sys/mman.h>
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <ELF file>\n", argv[0]);
return EXIT_FAILURE;
}
// 打开 ELF 文件
FILE *elf_fp = fopen(argv[1], "rb");
if (elf_fp == NULL) {
perror("fopen");
return EXIT_FAILURE;
}
// 获取 ELF 文件大小
fseek(elf_fp, 0, SEEK_END);
size_t elf_size = ftell(elf_fp);
rewind(elf_fp);
// 将 ELF 文件映射到内存
void *elf_mem = mmap(NULL, elf_size, PROT_READ, MAP_PRIVATE, fileno(elf_fp), 0);
if (elf_mem == MAP_FAILED) {
perror("mmap");
fclose(elf_fp);
return EXIT_FAILURE;
}
// 解析 ELF 文件头
Elf64_Ehdr *elf_hdr = (Elf64_Ehdr *)elf_mem;
// 校验 ELF 文件类型
if (elf_hdr->e_ident[EI_MAG0] != ELFMAG0 ||
elf_hdr->e_ident[EI_MAG1] != ELFMAG1 ||
elf_hdr->e_ident[EI_MAG2] != ELFMAG2 ||
elf_hdr->e_ident[EI_MAG3] != ELFMAG3) {
fprintf(stderr, "Invalid ELF file\n");
fclose(elf_fp);
munmap(elf_mem, elf_size);
return EXIT_FAILURE;
}
// 获取程序头表偏移量
off_t ph_offset = elf_hdr->e_phoff;
// 读取程序头表
Elf64_Phdr *phdr_table = (Elf64_Phdr *)(elf_mem + ph_offset);
// 查找 PT_LOAD 程序头
Elf64_Phdr *pt_load_phdr = NULL;
for (int i = 0; i < elf_hdr->e_phnum; i++) {
if (phdr_table[i].p_type == PT_LOAD) {
pt_load_phdr = &phdr_table[i];
break;
}
}
// 未找到 PT_LOAD 程序头
if (pt_load_phdr == NULL) {
fprintf(stderr, "PT_LOAD program header not found\n");
fclose(elf_fp);
munmap(elf_mem, elf_size);
return EXIT_FAILURE;
}
// 计算 PLT 的虚拟地址
uint64_t plt_virtual_address = pt_load_phdr->p_vaddr + elf_hdr->e_shoff;
// 获取 PLT 节的 sh_addr
Elf64_Shdr *plt_shdr = (Elf64_Shdr *)(elf_mem + elf_hdr->e_shstrndx);
uint64_t plt_sh_addr = plt_shdr->sh_addr;
// 访问内存中的 PLT
uint64_t *plt_mem = (uint64_