So文件添加Section段詳細(xì)說(shuō)明

簡(jiǎn)介

在逆向Android底層時(shí),一般都或多或少的接觸so文件,需要逆向so文件,一般的方法是往so文件植入我們的調(diào)試的代碼;而通常都是通過(guò)添加section段來(lái)植入代碼;查看本篇文章之前你需要先了解elf文件格式,so文件就是采用這種格式的

簡(jiǎn)單介紹elf文件格式

elf文件一般是由elf文件頭、program header頭(多個(gè))、section段(多個(gè))和section header頭(多個(gè)組成)。

program header

程序運(yùn)行時(shí),定位文件中各個(gè)段的位置,提供創(chuàng)建程序映像的具體信息;一個(gè)program header指向一個(gè)segment(運(yùn)行時(shí)的稱呼),而一個(gè)segement可能包含多個(gè)section 段

1.png

section header

執(zhí)行之前描述文件結(jié)構(gòu)的,理論上來(lái)說(shuō),Linux運(yùn)行期間不需要section header頭的,可以刪掉;一個(gè)section header頭對(duì)應(yīng)一個(gè)section 段,所以添加一個(gè)section段時(shí)還要添加一個(gè)section header頭

section段

section段是elf文件的主題,很多內(nèi)容都保存在里面,包括我們熟悉的.text、.rodata、.data等等,還有我們不熟悉的.got(全局偏移表)、.plt(過(guò)程鏈接表)等等,這兩個(gè)表主要用于函數(shù)和全局變量的調(diào)用,可參考這個(gè)鏈接理解這兩個(gè)表,下面是展示部分的section header頭:

1.png

進(jìn)入主題 -- 添加section字段

當(dāng)ELF文件被加載到內(nèi)存中后,系統(tǒng)會(huì)將多個(gè)具有相同權(quán)限(flg值)section合并一個(gè)segment。操作系統(tǒng)往往以頁(yè)為基本單位來(lái)管理內(nèi)存分配,一般頁(yè)的大小為4096B,即4KB的大小。同時(shí),內(nèi)存的權(quán)限管理的粒度也是以頁(yè)為單位,頁(yè)內(nèi)的內(nèi)存是具有同樣的權(quán)限等屬性,并且操作系統(tǒng)對(duì)內(nèi)存的管理往往追求高效和高利用率這樣的目標(biāo)。ELF文件在被映射時(shí),是以系統(tǒng)的頁(yè)長(zhǎng)度為單位的,那么每個(gè)section在映射時(shí)的長(zhǎng)度都是系統(tǒng)頁(yè)長(zhǎng)度的整數(shù)倍,如果section的長(zhǎng)度不是其整數(shù)倍,則導(dǎo)致多余部分也將占用一個(gè)頁(yè)。所以section段的位置和長(zhǎng)度不是隨便添加的,要根據(jù)program header頭的align對(duì)齊方式來(lái)添加

確定添加位置

新加入的section一般是在文件末尾,但是這里不是真正的文件末尾,而是將文件加載到內(nèi)存映像中的末尾,一般等于虛擬地址vaddr+占用空間大小memsiz,但是這樣還不行,還要做字節(jié)對(duì)其,我們知道文件末尾不等于映像末尾,一般這個(gè)映像末尾比文件末尾大,因?yàn)椴僮飨到y(tǒng)對(duì)內(nèi)存都是以頁(yè)的粒度來(lái)操作的,所以我們添加section段要找到真正的映像末尾,操作系統(tǒng)會(huì)把program header的type為L(zhǎng)OAD的段加入內(nèi)存映像中,而LOAD類型的program header一般都是做升序排列,我們只需要取最后一個(gè)LOAD就可以,讓vaddr+memsiz在和align做對(duì)齊操作就能夠得到映像末尾,算法如下:

/**
     * 對(duì)其算法 --- 保證addr能被align整除,系統(tǒng)按align字節(jié)對(duì)齊的訪問(wèn)方式
     * 返回一個(gè)addr是align的整數(shù)倍的值
     * @param addr:vaddr + memsi
     * @param align:對(duì)其的字節(jié)數(shù)
     * @return
     */
    public static int align(int addr, int align){
        if(align > addr){    //這種情況不好弄,我也不知道怎么辦
            return addr;
        }
        int offset = addr % align;
        return addr + (align-offset);
    }

1.png

添加section header頭

添加section header頭的目的是把5000H的表名和5010H的內(nèi)容聯(lián)系起來(lái);先看section header的結(jié)構(gòu):

typedef struct{
    Elf32_Word sh_name;
    Elf32_Word sh_type;
    Elf32_Word sh_flags;
    Elf32_Addr sh_addr;
    Elf32_Off  sh_offset;
    Elf32_Word sh_size;
    Elf32_Word sh_link;
    Elf32_Word sh_info;
    Elf32_Word sh_addralign;
    Elf32_Word sh_entsize;
}Elf32_Shdr;

sh_name : 是一個(gè)字符串表的索引偏移(5000H - 第一個(gè)字符串表的位置,第一個(gè)字符串表位置等于)

sh_addr和 sh_offset填上一個(gè)步驟得到的地址即可

sh_size: 就是我們填入的section長(zhǎng)度

sh_type:SHT_PROGBITS 1 此節(jié)區(qū)包含程序定義的信息,其格式和含義都由程序來(lái)解釋。

sh_flags:

名稱 取值 意義
SHF_WRITE 0x1 節(jié)區(qū)包含進(jìn)程執(zhí)行過(guò)程中將可寫(xiě)的數(shù)據(jù)
SHF_ALLOC 0x2 節(jié)區(qū)在進(jìn)程執(zhí)行過(guò)程中占用內(nèi)存。某些控制節(jié)區(qū)并不出現(xiàn)于目標(biāo)文件的內(nèi)存映像中,對(duì)于那些節(jié)區(qū),此位應(yīng)設(shè)置為 0
SHF_EXECINSTR 0x4 節(jié)區(qū)包含可執(zhí)行的機(jī)器指令
SHF_MASKPROC 0xF0000000 所有包含于此掩碼中的四位都用于處理器專用的語(yǔ)義

其他的幾個(gè)可以不用處理,以下是示例代碼:

public static byte[] addSectionHeader(byte[] src){
                /**
                 *  public byte[] sh_name = new byte[4];
                 public byte[] sh_type = new byte[4];
                 public byte[] sh_flags = new byte[4];
                 public byte[] sh_addr = new byte[4];
                 public byte[] sh_offset = new byte[4];
                 public byte[] sh_size = new byte[4];
                 public byte[] sh_link = new byte[4];
                 public byte[] sh_info = new byte[4];
                 public byte[] sh_addralign = new byte[4];
                 public byte[] sh_entsize = new byte[4];
                 */
                byte[] newHeader = new byte[sectionSize];

                //構(gòu)建一個(gè)New Section Header
                newHeader = Utils.replaceByteAry(newHeader, 0, Utils.int2Byte(addSectionStartAddr - stringSectionOffset));
                newHeader = Utils.replaceByteAry(newHeader, 4, Utils.int2Byte(ElfType32.SHT_PROGBITS));                         //type=PROGBITS
                newHeader = Utils.replaceByteAry(newHeader, 8, Utils.int2Byte(ElfType32.SHF_ALLOC + ElfType32.SHF_WRITE));              //改字節(jié)區(qū)可以被寫(xiě)入
                newHeader = Utils.replaceByteAry(newHeader, 12, Utils.int2Byte(addSectionStartAddr+0x10));                        //所代表的字節(jié)區(qū)其實(shí)地址
                newHeader = Utils.replaceByteAry(newHeader, 16, Utils.int2Byte(addSectionStartAddr+0x10));
                newHeader = Utils.replaceByteAry(newHeader, 20, Utils.int2Byte(newSectionSize));        //字節(jié)區(qū)大小
                newHeader = Utils.replaceByteAry(newHeader, 24, Utils.int2Byte(0));
                newHeader = Utils.replaceByteAry(newHeader, 28, Utils.int2Byte(0));
                newHeader = Utils.replaceByteAry(newHeader, 32, Utils.int2Byte(4));
                newHeader = Utils.replaceByteAry(newHeader, 36, Utils.int2Byte(0));

                //在末尾增加Section
                byte[] newSrc = new byte[src.length + newHeader.length];
                newSrc = Utils.replaceByteAry(newSrc, 0, src);
                newSrc = Utils.replaceByteAry(newSrc, src.length, newHeader);

                return newSrc;
        }

完善步驟

修改第一個(gè)LOAD的program header

將其filesize和memsiz改為文件的總長(zhǎng)度

修改section header為.shstrta

將他的size在原有的基礎(chǔ)上加16即可,因?yàn)樵黾恿艘粋€(gè)section name

至此,完結(jié),如果把so文件弄回去報(bào)了這個(gè)錯(cuò)誤
load segment1: p_offset (0x0) + p_filesz (0x53f8) ( = 0x53f8) past end of file (0x53f8)
這是因?yàn)槟銊倓偺砑拥亩蔚拇笮〕隽宋募笮?,因?yàn)槲覀冃薷膒rogram header的filesize和memsize文件總長(zhǎng)度,這個(gè)長(zhǎng)度包括啦文件結(jié)束標(biāo)志0x0a,我們要修改這兩個(gè)值為文件長(zhǎng)度減去1即可

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容