ELF - 更改入口点时出现 SEGFAULT
ELF - Getting a SEGFAULT when changing the entry point
我正在尝试通过 e_entry
字段直接修补 ELF 文件的入口点:
Elf64_Ehdr *ehdr = NULL;
Elf64_Phdr *phdr = NULL;
Elf64_Shdr *shdr = NULL;
if (argc < 2)
{
printf("Usage: %s <executable>\n", argv[0]);
exit(EXIT_SUCCESS);
}
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
perror("open");
exit(EXIT_FAILURE);
}
if (fstat(fd, &st) < 0)
{
perror("fstat");
exit(EXIT_FAILURE);
}
/* map whole executable into memory */
mapped_file = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_file < 0)
{
perror("mmap");
exit(EXIT_FAILURE);
}
// check for an ELF file
check_elf(mapped_file, argv);
ehdr = (Elf64_Ehdr *) mapped_file;
phdr = (Elf64_Phdr *) &mapped_file[ehdr->e_phoff];
shdr = (Elf64_Shdr *) &mapped_file[ehdr->e_shoff];
mprotect((void *)((uintptr_t)&ehdr->e_entry & ~(uintptr_t)4095), 4096, PROT_READ | PROT_WRITE);
if (ehdr->e_type != ET_EXEC)
{
fprintf(stderr, "%s is not an ELF executable.\n", argv[1]);
exit(EXIT_FAILURE);
}
printf("Program entry point: %08x\n", ehdr->e_entry);
int text_found = 0;
uint64_t test_addr;
uint64_t text_end;
size_t test_len = strlen(shellcode);
int text_idx;
for (i = 0; i < ehdr->e_phnum; ++i)
{
if (text_found)
{
phdr[i].p_offset += PAGE_SIZE;
continue;
}
if (phdr[i].p_type == PT_LOAD && phdr[i].p_flags == ( PF_R | PF_X))
{
test_addr = phdr[i].p_vaddr + phdr[i].p_filesz;
text_end = phdr[i].p_vaddr + phdr[i].p_filesz;
printf("TEXT SEGMENT ends at 0x%x\n", text_end);
puts("Changing entry point...");
ehdr->e_entry = (Elf64_Addr *) test_addr;
memmove(test_addr, shellcode, test_len);
phdr[i].p_filesz += test_len;
phdr[i].p_memsz += test_len;
text_found++;
}
}
//patch sections
for (i = 0; i < ehdr->e_shnum; ++i)
{
if (shdr->sh_offset >= test_addr)
shdr->sh_offset += PAGE_SIZE;
else
if (shdr->sh_size + shdr->sh_addr == test_addr)
shdr->sh_size += test_len;
}
ehdr->e_shoff += PAGE_SIZE;
close(fd);
}
本例中的 shellcode 只是一堆 NOP,最后有一条 int3 指令。
我确保调整了这个新代码之后的段和部分,但问题是,一旦我修补入口点,程序就会崩溃,这是为什么?
我怀疑您没有启用对程序头的写访问权限。您可以通过
之类的方式执行此操作
const uintptr_t page_size = 4096;
mprotect((void *)((uintptr_t)&ehdr->e_entry & ~(uintptr_t)4095), 4096, PROT_READ | PROT_WRITE);
ehdr->e_entry = test_addr;
变化:
memmove(test_addr, shellcode, test_len);
至:
memmove(mapped_file + phdr[i].p_offset + phdr[i].p_filesz, shellcode, test_len);
似乎可以解决您的问题。 test_addr是属于你映射的文件的虚拟地址;您不能直接将其用作指针。您要处理的位是文件映射地址 p_offset 和 p_filesz.
我正在尝试通过 e_entry
字段直接修补 ELF 文件的入口点:
Elf64_Ehdr *ehdr = NULL;
Elf64_Phdr *phdr = NULL;
Elf64_Shdr *shdr = NULL;
if (argc < 2)
{
printf("Usage: %s <executable>\n", argv[0]);
exit(EXIT_SUCCESS);
}
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
perror("open");
exit(EXIT_FAILURE);
}
if (fstat(fd, &st) < 0)
{
perror("fstat");
exit(EXIT_FAILURE);
}
/* map whole executable into memory */
mapped_file = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_file < 0)
{
perror("mmap");
exit(EXIT_FAILURE);
}
// check for an ELF file
check_elf(mapped_file, argv);
ehdr = (Elf64_Ehdr *) mapped_file;
phdr = (Elf64_Phdr *) &mapped_file[ehdr->e_phoff];
shdr = (Elf64_Shdr *) &mapped_file[ehdr->e_shoff];
mprotect((void *)((uintptr_t)&ehdr->e_entry & ~(uintptr_t)4095), 4096, PROT_READ | PROT_WRITE);
if (ehdr->e_type != ET_EXEC)
{
fprintf(stderr, "%s is not an ELF executable.\n", argv[1]);
exit(EXIT_FAILURE);
}
printf("Program entry point: %08x\n", ehdr->e_entry);
int text_found = 0;
uint64_t test_addr;
uint64_t text_end;
size_t test_len = strlen(shellcode);
int text_idx;
for (i = 0; i < ehdr->e_phnum; ++i)
{
if (text_found)
{
phdr[i].p_offset += PAGE_SIZE;
continue;
}
if (phdr[i].p_type == PT_LOAD && phdr[i].p_flags == ( PF_R | PF_X))
{
test_addr = phdr[i].p_vaddr + phdr[i].p_filesz;
text_end = phdr[i].p_vaddr + phdr[i].p_filesz;
printf("TEXT SEGMENT ends at 0x%x\n", text_end);
puts("Changing entry point...");
ehdr->e_entry = (Elf64_Addr *) test_addr;
memmove(test_addr, shellcode, test_len);
phdr[i].p_filesz += test_len;
phdr[i].p_memsz += test_len;
text_found++;
}
}
//patch sections
for (i = 0; i < ehdr->e_shnum; ++i)
{
if (shdr->sh_offset >= test_addr)
shdr->sh_offset += PAGE_SIZE;
else
if (shdr->sh_size + shdr->sh_addr == test_addr)
shdr->sh_size += test_len;
}
ehdr->e_shoff += PAGE_SIZE;
close(fd);
}
本例中的 shellcode 只是一堆 NOP,最后有一条 int3 指令。
我确保调整了这个新代码之后的段和部分,但问题是,一旦我修补入口点,程序就会崩溃,这是为什么?
我怀疑您没有启用对程序头的写访问权限。您可以通过
之类的方式执行此操作const uintptr_t page_size = 4096;
mprotect((void *)((uintptr_t)&ehdr->e_entry & ~(uintptr_t)4095), 4096, PROT_READ | PROT_WRITE);
ehdr->e_entry = test_addr;
变化:
memmove(test_addr, shellcode, test_len);
至:
memmove(mapped_file + phdr[i].p_offset + phdr[i].p_filesz, shellcode, test_len);
似乎可以解决您的问题。 test_addr是属于你映射的文件的虚拟地址;您不能直接将其用作指针。您要处理的位是文件映射地址 p_offset 和 p_filesz.