logo资料库

linux api hook.pdf

第1页 / 共53页
第2页 / 共53页
第3页 / 共53页
第4页 / 共53页
第5页 / 共53页
第6页 / 共53页
第7页 / 共53页
第8页 / 共53页
资料共53页,剩余部分请下载后查看
一步一步走进 Linux HOOK API(一) 最近我查阅很多参考资料.发现对于讲述 Linux HOOK API 的资料是很少,让我们这些新人难以去 走进 Linux HOOK 大门.在这里我将全面的讲述 Linux HOOK API 的全部实现过程,这个过程中我 也遇到很多坎坷,所以在这么写下这份教程.让大家都来进入 HOOK 的神秘世界. 不要认为 HOOK API 是 windows 的专利(PS.其实我以前就是这么认为的.哈哈....),其实在 Linux 中 也有 HOOK API 这样的技术,只是实现起来相对比较麻烦,首先今天主要带大家认识的是 ELF 文件, 在 Linux 中,ELF 文件主要是应用在可执行文件,重定位文件,可执行文件动态连接库。首先来看一 下 ELF Head 的定义: PS.我们这里主要针对的是 32 位平台.有关 64 位平台相关定义请参阅/usr/include/elf.h #define EI_NIDENT (16) typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf32_Half e_type; /* Object file type */ Elf32_Half e_machine; /* Architecture */ Elf32_Word e_version; /* Object file version */ Elf32_Addr e_entry; /* Entry point virtual address */ Elf32_Off e_phoff; /* Program header table file offset */ Elf32_Off e_shoff; /* Section header table file offset */ Elf32_Word e_flags; /* Processor-specific flags */ Elf32_Half e_ehsize; /* ELF header size in bytes */ Elf32_Half e_phentsize; /* Program header table entry size */ Elf32_Half e_phnum; /* Program header table entry count */ Elf32_Half e_shentsize; /* Section header table entry size */ Elf32_Half e_shnum; /* Section header table entry count */ Elf32_Half e_shstrndx; /* Section header string table index */ } Elf32_Ehdr; e_ident: 这个成员,是 ELF 文件的第一个成员,该成员是个数字,根据上面的宏可以看出,这个程序 是个 16 字节的数据.该成员的前 4 个字节依次是 0x7F,0x45,0x4c,0x46,也 就是"\177ELF"。这是 ELF 文件的标志,任何一个 ELF 文件这四个字节都完全相同。 为了让我们更方便的使用 ELF 数据在 elf.h 中对上述数据进行了宏定义.如下: #define EI_MAG0 0 /* File identification byte 0 index */ #define ELFMAG0 0x7f /* Magic number byte 0 *
#define EI_MAG1 1 /* File identification byte 1 index */ #define ELFMAG1 'E' /* Magic number byte 1 */ #define EI_MAG2 2 /* File identification byte 2 index */ #define ELFMAG2 'L' /* Magic number byte 2 */ #define EI_MAG3 3 /* File identification byte 3 index */ #define ELFMAG3 'F' /* Magic number byte 3 */ /* Conglomeration of the identification bytes, for easy testing as a word. */ #define ELFMAG "\177ELF" #define SELFMAG 4 第四个字节表示 ELF 格式,1:32 位 2:64 位 #define EI_CLASS 4 /* File class byte index */ #define ELFCLASSNONE 0 /* Invalid class */ #define ELFCLASS32 1 /* 32-bit objects */ #define ELFCLASS64 2 /* 64-bit objects */ #define ELFCLASSNUM 3 第五个字节表示数据编码格式,1:小端模式 2:大端模式 #define EI_DATA 5 /* Data encoding byte index */ #define ELFDATANONE 0 /* Invalid data encoding */ #define ELFDATA2LSB 1 /* 2's complement, little endian */ #define ELFDATA2MSB 2 /* 2's complement, big endian */ #define ELFDATANUM 3 第六个字节表示文件版本,该值目前必须为 1 #define EV_CURRENT 1 /* Current version */ 第七个字节表示操作系统标识: #define EI_OSABI 7 /* OS ABI identification */ #define ELFOSABI_NONE 0 /* UNIX System V ABI */
#define ELFOSABI_SYSV 0 /* Alias. */ #define ELFOSABI_HPUX 1 /* HP-UX */ #define ELFOSABI_NETBSD 2 /* NetBSD. */ #define ELFOSABI_LINUX 3 /* Linux. */ #define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ #define ELFOSABI_AIX 7 /* IBM AIX. */ #define ELFOSABI_IRIX 8 /* SGI Irix. */ #define ELFOSABI_FREEBSD 9 /* FreeBSD. */ #define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ #define ELFOSABI_MODESTO 11 /* Novell Modesto. */ #define ELFOSABI_OPENBSD 12 /* OpenBSD. */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /*Standalone(embedded) application */ 第八个字节表示 ABI 版本 #define EI_ABIVERSION 8 /* ABI version */ 第九个字节表示 e_ident 中从哪开始之后未使用. #define EI_PAD 9 /* Byte index of padding bytes */ e_type: 这个成员是 ELF 文件的类型: 1:表示此文件是重定位文件. 2:表示可执行文件. 3:表示此文件是一个动态连接库。
e_machine: 这个成员表示机器版本.具体定义参与 elf.h (篇幅问题,太长了) e_version: 这个成员表示 ELF 文件版本,为 1 e_entry: 这个成员表示可执行文件的入口虚拟地址。此字段指出了该文件中第一条可执 行 机器指令在进程被正确加载后的内存地址!ELF 可执行文件只能被加载到固定位 置. e_phoff: 这个成员表示程序头(Program Headers)在 ELF 文件中的偏移量。如果程序头 不 存在此值为 0。 e_shoff: 这个成员表示节头(Section Headers:)在 ELF 文件中的偏移量。如果节头不存 在 此值为 0。 e_flags: 这个成员表示处理器标志. e_ehsize: 这个成员描述了“ELF 头”自身占用的字节数。 e_phentsize: 该成员表示程序头中的每一个结构占用的字节数。程序头也叫程序头表,可 以 被看做一个在文件中连续存储的结构数组,数组中每一项是一个结构,此字段 给出了这 个结构占用的字节大小。 e_phoff: 指出程序头在 ELF 文件中的起始偏移。 e_phnum: 此字段给出了程序头中保存了多少个结构。如果程序头中有 3 个结构则程序头 在文件中占用了 3×e_phentsize 个字节的大小。 e_shentsize: 节头中每个结构占用的字节大小。节头与程序头类似也是一个结构数组,关 于 这两个结构的定义将分别在讲述程序头和节头的时候给出。 e_shnum: 节头中保存了多少个结构。 e_shstrndx: 这是一个整数索引值。节头可以看作是一个结构数组,用这个索引值做为此 数 组的下标,它在节头中指定的一个结构进一步给出了一个“字符串表”的信息,而这 个字 符串表保存着节头中描述的每一个节的名称,包括字符串表自己也是其中的一 个节。 示例代码: #include #include #include
#include #include #include #include #include #include int g_File = 0; void *g_pData = NULL; void * Map(char* szFileName) { g_File = open(szFileName, O_RDWR); if (g_File < 0) { g_File = 0; return NULL; } struct stat status; fstat(g_File, &status); g_pData = mmap(0, status.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, g_File, 0); if (MAP_FAILED != g_pData) { return g_pData; } close(g_File); g_pData = NULL; g_File = 0; return NULL; } void displayEhdr(Elf32_Ehdr *ehdr) { printf("Magic:"); int i = 0; for(i = 0; i < EI_NIDENT;i++){ printf(" %02x",ehdr->e_ident[i]); } printf("\n"); printf("Version: 0x%x\n",ehdr->e_version); printf("Entry point address: 0x%x\n",ehdr->e_entry); printf("Start of program headers: %d (bytes into file)\n",ehdr->e_phoff); printf("Start of section headers: %d (bytes into file)\n",ehdr->e_shoff);
printf("Flags: %d\n",ehdr->e_flags); printf("Size of this header: %d (bytes)\n",ehdr->e_ehsize); printf("Size of program headers: %d (bytes)\n",ehdr->e_phentsize); printf("Number of program headers: %d\n",ehdr->e_phnum); printf("Size of section headers: %d (bytes)\n",ehdr->e_shentsize); printf("Number of section headers: %d\n",ehdr->e_shnum); printf("Section header string table index: %d\n",ehdr->e_shstrndx); } int main(int argc,char *argv[]) { } if(argc != 2){ printf("parameter error\n"); exit(0); } Elf32_Ehdr *ehdr = (Elf32_Ehdr *)Map(argv[1]); if(ehdr == NULL){ perror("Map Error\n"); exit(0); } displayEhdr(ehdr); 一步一步走进 Linux HOOK API(二) 从上一篇的 ELF Head 之后,想必很多读者已经对 ELF 文件开始感觉不是遥不可及了,今天这一节, 主要是讲程序头(Program Headers),程序头主要是从加载执行的角度来看的,很多人想那里面到底 是什么东西呢,其实程序头就是一个结构数组,每一个头保存着对应的不同的数据,有的数据是告 诉系统把我放入内存,有的数据时告诉系统我是变量.等等...在系统加载程序的时候就要通过该程 序头来加载不同的段. 下面就来看一下程序头的结构体: typedef struct { Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */ Elf32_Word p_filesz; /* Segment size in file */ Elf32_Word p_memsz; /* Segment size in memory */ Elf32_Word p_flags; /* Segment flags */ Elf32_Word p_align; /* Segment alignment */ } Elf32_Phdr; p_type: 段的类型 在 elf.h 头文件中,有很详细的说明段的类型,比如 PT_LOAD 表示加载的程序段 p_offset: 文件偏移 该段在文件中的偏移。这个偏移是相对于整个文件的。 p_vaddr: 加载后的虚拟地址 该段加载后在进程空间中占用的内存起始地址。 p_paddr: 该段的物理地址 这个字段被忽略,因为在多数现代操作系统下物理地址是进程无法触及的。 p_filesz: 该段在文件中占用的字节大小 有些段可能在文件中不存在但却占用一定的内存空间,此时这个字段为 0。 p_memsz: 该段在内存中占用的字节大小 有些段可能仅存在于文件中而不被加载到内存,此时这个字段为 0。 p_flags: 段的属性 表示该段的读写执行等属性.elf.h 文件中的定义是 #define PF_X (1 << 0) /* Segment is executable */
#define PF_W (1 << 1) /* Segment is writable */ #define PF_R (1 << 2) /* Segment is readable */ #define PF_MASKOS 0x0ff00000 /* OS-specific */ #define PF_MASKPROC 0xf0000000 /* Processor-specific */ p_align: 对齐 现代操作系统都使用虚拟内存为进程序提供更大的空间,分页技术功不可没,页就成了最小 的内存分配单位,不足一页的按一页算。所以加载程序数据一般也从一页的起始地址开始, 这就属于对齐。 示例代码: typedef struct _SegmentType_ { unsigned int type; char *typeName; }SegmentType; SegmentType segTyoe[] = { {0,"NULL"},{1,"LOAD"}, {2,"DYNAMIC"},{3,"INTERP"}, {4,"NOTE"},{5,"SHLIB"}, {6,"PHDR"},{7,"TLS"}, {8,"NUM"},{0x60000000,"LOOS"}, {0x6474e550,"GNU_EH_FRAME"},{0x6474e551,"PT_GNU_STACK"}, {0x6474e552,"PT_GNU_RELRO"},{0x6ffffffa,"PT_SUNWBSS"}, {0x6ffffffb,"PT_SUNWSTACK"},{0x6fffffff,"PT_HISUNW"}, {0x70000000,"PT_HIOS"},{0x7fffffff,"PT_HIPROC"},
分享到:
收藏