这步就简单了,修改一下这个字段:e_shnum
/** * 修改elf头部总的section的总数信息 */ public static byte[] changeElfHeaderSectionCount(byte[] src){ byte[] count = Utils.copyBytes(src, elfHeaderSectionCountIndex, 2); short counts = Utils.byte2Short(count); counts++; count = Utils.short2Byte(counts); src = Utils.replaceByteAry(src, elfHeaderSectionCountIndex, count); return src; }第五步:修改Program Header中第一个类型为LOAD的段的文件大小和内存映像的大小
这里需要修改的是第一个Type为LOAD的file_size和mem_size字段的大小为文件的总大小即可。下面会说道type为PT_LOAD的都会被加载到内存中。那么我们如果想把我们新加的段的内容加载内存中,这里需要修改一下file_size和mem_size为文件的总大小。
代码如下:
/** * 修改Program Header中的信息 * 把新增的段内容加入到LOAD Segement中 * 就是修改第一个LOAD类型的Segement的filesize和memsize为文件的总长度 */ public static byte[] changeProgramHeaderLoadInfo(byte[] src){ //寻找到LOAD类型的Segement位置 int offset = elfHeaderSize + programHeaderSize * firstLoadInPHIndex + programFileSizeIndex; //file size字段 byte[] fileSize = Utils.int2Byte(src.length); src = Utils.replaceByteAry(src, offset, fileSize); //mem size字段 offset = offset + 4; byte[] memSize = Utils.int2Byte(src.length); src = Utils.replaceByteAry(src, offset, memSize); //flag字段 offset = offset + 4; byte[] flag = Utils.int2Byte(7); src = Utils.replaceByteAry(src, offset, flag); return src; }上面我们完成了工作,下面还需要来讲解一下一个重要的知识点,就是我们上面一直说到的文件末尾这个词?
我在开始的时候就说了,这里的文件末尾不是真正意义上的文件末尾,比如这里的文件长度是:0x3478
这里的文件末尾指的是映像到内存中的字节大小?因为我们知道文件的大小和这个文件映像到内存中的大小是不一定相等的,因为映像到内存中是需要做很多操作的,比如最简单的就是页面对其工作,这个就会增加字节了,所以映像到内存的大小肯定是大于等于文件大小的。
那么我们该如何获取到这个文件映像到内存之后的大小呢?这里就需要先来了解一个知识点:
可执行文件和共享目标文件(动态链接库)是程序在磁盘中的静态存储形式;要执行一个程序,系统就要先把相应的可执行文件和共享目标文件装载到进程的地址空间中,这样就形成一个可运行进程的内存空间布局,称为进程镜像;一个已经装载完成的进程空间中会包含多个不同的段(Segment),比如,代码段、数据段、堆栈段,等等;
准备一个程序的内存镜像,大体上可以分为两个步骤:装载和链接;前者是把目标文件装载到内存中,后者是解析目标文件中的符号引用;
一个可执行文件及其依赖的共享目标文件被完全成功地装载到进程的内存地址空间中之后,这个可执行文件或共享目标文件中的程序头部表(Program Header Table)就是必须存在的、不可缺少的必需品,程序头部表是一个数组,数组中的每一个元素就称为一个程序头(Program Header),每一个程序头描述一个内存段(Segment)或者一块用于准备执行程序的信息;内存中的一个目标文件中的段包含一个或多个节;也就是ELF文件在磁盘中的一个或多个节可能会被映射到内存中的同一个段中;程序头只对可执行文件或共享目标文件有意义,对于其它类型的目标文件,该信息可以忽略;
p_type=PT_LOAD时,段的内容会被从文件中拷贝到内存中,如果p_memsz>p_filesz,则在内存中多出的存储空间中填0补充,即,段在内存中可以比在文件中占用更大空间;相反,p_filesz永远都不应该比p_memsz大,因为这样的话,在内存中就将无法完整地映射段的内容;在程序头部表中,所有PT_LOAD类型的程序头都按照p_vaddr的值做升序排列;
从上面的两段话可以看出我们看到两个知识点:
1、程序头中的p_type=PT_LOAD的时候,段的内容会被从文件中拷贝到内存中
2、所有PT_LOAD类型的程序头都按照p_vaddr的值做升序排列的