On 2015-10-20, jack wrote:
>非常感谢,注入的字节码是直接用汇编写的,直接调用了系统调用接口,没有用任何的第
>三方库,那这样的话就需要新增一个section,将它追加到elf文件的尾部,那么对应的就
>需要调整program header中.data, .bss对应的segment(需要添加一个section header吗
>?),但是program header中有三个属性之间的关系我不是特别的清楚,一个是offset,
>filesz, memsz,能否讲解一些,多谢了。
>
拿 Arch Linux 最新版本的喵喵开刀……
Name : coreutils
Version : 8.24-1
Description : The basic file, shell and text manipulation utilities of the GNU operating system
Architecture : x86_64
下面给一个示例,效果是改变入口点,多执行一条 jmp 到原来的入口点。
#include <elf.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define ROF(i, a, b) for (int i = (b); --i >= (a); )
int main(int argc, char *argv[])
{
int fd = open(argv[1], O_RDWR);
struct stat st;
fstat(fd, &st);
size_t size = st.st_size + 5; // 比原始文件多5字节,编码一条 jmp 指令到原始入口地址
ftruncate(fd, size);
char *c = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
auto *elf = (Elf64_Ehdr*)c; // 文件头即ELF头
auto *phdr = (Elf64_Phdr*)(c+elf->e_phoff); // 文件中program header偏移量
ROF(i, 0, elf->e_phnum)
if (phdr[i].p_type == PT_LOAD) { // 最后一个 PT_LOAD `readelf -l`
phdr[i].p_flags |= PF_X; // 原来用来存 .data .bss 等,为之添加可执行标志
phdr[i].p_filesz = size - phdr[i].p_offset; // 延长至文件尾,这样.bss后面的文件内容也会被载入进该segment,我们的代码在文件末尾
// 注意 p_memsz 按页向上取整会是该 segment 大小(/proc/$pid/maps 查看),原来 segment 末尾部分是 .bss,其内容不在ELF中(特征是p_memsz > p_filesz),加载时会被内核初始化为0,cat 的 p_memsz 较大,覆盖了文件末尾后很大一片区域的虚拟地址,我们的指令与 .bss 一部分重叠。妥善的处理方式是在执行完任务跳转回原始入口地址时把自身清零(重新开辟一段空间,拷贝自身指令到那里,跳转到那里,把原始文件对应 .bss 除清零)
c[size-5] = 0xe9; // JMP 的 opcode
Elf64_Addr new_entry = phdr[i].p_vaddr + size-5 - phdr[i].p_offset;
*(uint64_t*)&c[size-4] = elf->e_entry - (new_entry + 5);
elf->e_entry = new_entry;
break;
}
}
% cp =cat /tmp/a
% ./main /tmp/a # 修改
% gdb -ex 'b *0x60cb20' -ex r /tmp/a
Reading symbols from /tmp/a...(no debugging symbols found)...done.
Breakpoint 1 at 0x60cb20
Starting program: /tmp/a
Breakpoint 1, 0x000000000060cb20 in ?? ()
(gdb) x/i $rip
=> 0x60cb20: jmp 0x402590 # 入口点发生变化且执行了我们的指令
(gdb) ni
0x0000000000402590 in ?? ()
(gdb) x/5i $rip # 原始入口点
=> 0x402590: xor ebp,ebp
0x402592: mov r9,rdx
0x402595: pop rsi
0x402596: mov rdx,rsp
0x402599: and rsp,0xfffffffffffffff0
(gdb)
--
Ray
http://maskray.me