馬上加入Android 台灣中文網,立即免費下載應用遊戲。
您需要 登錄 才可以下載或查看,沒有帳號?註冊
x
本文針對Android so本加密的情況,且解密算法在.init_array段的函數中,目標是去掉加密的算法,然後對dump出來的so進行修復,然後再放回到apk包中,使之能正常的跑起來。脫殼思路如下:
(1)準備工作:使用IDA調試apk,使之斷點再so的JNI_Onload函數的第一行,一般來說都是「PUSH {R4-R7,LR}」類似的一行。
這時,通過ctrl+s找到so的起始地址和結束地址,然後將起始地址和結束地址之間的資料拷貝出來,保存成一個so。
這裡假定原so名稱為:liba.so,從記憶體中拷貝出來的so名稱為:liba_dump.so
(2) 修復工作一般包含如下方面:
2.1 將Segment(程式段)頭拷貝到liba_dump.so中,這個操作主要是為了在IDA能進行靜態解析。
2.2 將Section(節)頭拷貝到liba_dump.so中,這個操作主要是為了在IDA能進行靜態解析。
2.3 因為ProgramSegment一般位於so的頭部一端(也就是頂部),而Section頭一般位於so的尾部一端(也就是底部)
所以,需要判斷下尾部是否發生了偏移,如果Section頭資料的起始地址(offset)正好處於偏移的資料中,則需要重新計算偏移後的地址(一般
就是+0x1000),然後再拷貝到liba_dump.so中
2.4 更新在映射到記憶體過程中,不變的資料。
2.5 將在.init_array段中出現的,在.rel.dyn的重定位項設定為0,然後將.init_array段全部資料置0
2.6 將修復後的資料寫入到新的檔案中,比如:liba_repair.so, 該so可以直接放入到原apk中,程式可以正常的跑起來。
直接上java代碼吧。歡迎拍磚。
- package com.tudou.soshelltest;
-
- import java.io.ByteArrayOutputStream;
- import java.util.List;
-
- import com.tudou.soshell.ELF32_Phdr;
- import com.tudou.soshell.ELF32_Shdr;
- import com.tudou.soshell.ELFType32;
- import com.tudou.soshell.ELFUtils;
-
-
- public class MGPBaseMainRepair {
-
- public static String sofile = "so/liba.so";
- public static String dumpsofile = "so/liba_dump.so";
- public static String repairsofile = "so/liba_repair.so";
-
- public static void main(String[] args){
- try {
- byte[] srcElfFileBytes = ELFUtils.readFile(sofile);
- ELFType32 elfsrc = new ELFType32(srcElfFileBytes);
- //讀取頭部內容
- elfsrc.parseELFHeader();
-
- //讀取程式頭訊息
- elfsrc.parseSegmentHeader();
- elfsrc.printSegmentHeaderList();
- elfsrc.parseDynamicSegment();
- /*
- * 解析段名稱
- */
- elfsrc.parseSectionNames();
- //讀取段頭訊息
- elfsrc.parseSectionHeader();
- elfsrc.printSectionHeaderList();
- elfsrc.parseSectionHeaderDetail();
-
- byte[] destElfFileBytes = ELFUtils.readFile(dumpsofile);
- ELFType32 elfdest = new ELFType32(destElfFileBytes);
- //讀取頭部內容
- elfdest.parseELFHeader();
-
- //讀取程式頭訊息
- elfdest.parseSegmentHeader();
- elfdest.printSegmentHeaderList();
- // elfdest.parseDynamicSegment();
-
- /*
- * dump出來的so(待處理的so)沒有Section頭訊息,無需解析
- */
- // elfdest.parseSectionHeader();
- // elfdest.printShdrList();
- // elfdest.printShdrDetailList();
- /*
- * 修復步驟
- * 1、將Segment(程式段)頭拷貝到dump.so中
- * 2、將Section(節)頭拷貝到dump.so中
- * 3、因為ProgramSegment一般位於so的頭部一端(也就是頂部),而Section頭一般位於so的尾部一端(也就是底部)
- * 所以,需要判斷下尾部是否發生了偏移,如果Section頭資料的起始地址(offset)正好處於偏移的資料中,則需要重新計算偏移後的地址(一般
- * 就是+0x1000),然後再拷貝到dump.so中
- * 4、更新在映射到記憶體過程中,不變的資料。
- * 5、將在.init_array段中出現的,在.rel.dyn的重定位項設定為0,然後將.init_array段全部資料置0
- */
- System.out.println("
- ********************************************************************************");
- System.out.println("************************************ 開始修復 **********************************");
- System.out.println("********************************************************************************
- ");
- /**
- * 判斷是否需要移動Section頭訊息
- */
- int __SECTION_START_OFFSET__ = 0;
- List<ELF32_Shdr> srcShdrList = elfsrc.secheaderList;
- if(srcShdrList != null && srcShdrList.size() > 0){
- /*
- * 當檔案偏移地址與記憶體載入時地址不一致時,dump出來的so檔案,會按照記憶體中的偏移保存到檔案中。
- */
- int startoffset = 0;
- for(ELF32_Shdr shdr : srcShdrList){
- /*
- * 第一步:更新Section節offset,使之與addr一樣。(offset是在檔案內的偏移,addr是載入後記憶體的偏移)
- */
- int offset = ELFUtils.byte2Int(shdr.sh_offset);
- int addr = ELFUtils.byte2Int(shdr.sh_addr);
- // int size = Utils.byte2Int(shdr.sh_size);
- if(addr > 0 && offset > 0 && Math.abs(addr - offset) == 0x1000){
- if(startoffset == 0){
- startoffset = ELFUtils.byte2Int(shdr.sh_offset);
- }
- if(__SECTION_START_OFFSET__ == 0){
- __SECTION_START_OFFSET__ = startoffset;
- }
- System.arraycopy(shdr.sh_addr, 0, shdr.sh_offset, 0, 4);
- }else if((offset > startoffset) && addr == 0){
- offset += 0x1000;
- byte [] offsetbytes = ELFUtils.int2Byte(offset);
- System.arraycopy(offsetbytes, 0, shdr.sh_offset, 0, 4);
- }
- }
- ELFUtils.log("在源包上,更新section的offset,使之與addr一樣");
- }
-
- List< ELF32_Phdr> srcPhdrList = elfsrc.proheaderList;
- if(srcPhdrList != null && srcPhdrList.size() > 0){
- /*
- * 當檔案偏移地址與記憶體載入時地址不一致時,dump出來的so檔案,會按照記憶體中的偏移保存到檔案中。
- */
- int startoffset = 0;
- for( ELF32_Phdr phdr : srcPhdrList){
- /*
- * 第二步:更新Segment節offset,使之與addr一樣。(offset是在檔案內的偏移,addr是載入後記憶體的偏移)
- */
- int offset = ELFUtils.byte2Int(phdr.p_offset);
- int vaddr = ELFUtils.byte2Int(phdr.p_vaddr);
- if(vaddr > 0 && offset > 0 && Math.abs(vaddr - offset) == 0x1000){
- if(startoffset == 0){
- startoffset = ELFUtils.byte2Int(phdr.p_offset);
- }
- System.arraycopy(phdr.p_vaddr, 0, phdr.p_offset, 0, 4);
- }else if((offset > startoffset) && vaddr == 0){
- offset += 0x1000;
- byte [] offsetbytes = ELFUtils.int2Byte(offset);
- System.arraycopy(offsetbytes, 0, phdr.p_offset, 0, 4);
- }
- }
- ELFUtils.log("在源包上,更新segment的offset,使之與addr一樣");
- }
-
- /*
- * 第四步:將源包的Section頭訊息更新到dump包上
- */
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- for(ELF32_Shdr shdr : srcShdrList){
- baos.write(shdr.toByteArray());
- }
- baos.close();
- baos.flush();
- byte [] updateSectionDumpBytes = baos.toByteArray();
- elfdest.updateSectionHeader(updateSectionDumpBytes);
- System.out.println("將源包的Section頭訊息更新到dump包上");
-
- /*
- * 第五步:將源包的Segment頭訊息更新到dump包上
- */
- ByteArrayOutputStream pbaos = new ByteArrayOutputStream();
- for(ELF32_Phdr shdr : srcPhdrList){
- pbaos.write(shdr.toByteArray());
- }
- pbaos.close();
- pbaos.flush();
- byte [] updateSegmentDumpBytes = pbaos.toByteArray();
- elfdest.updateProgramHeadert(updateSegmentDumpBytes);
- System.out.println("將源包的Segment頭訊息更新到dump包上");
-
- /*
- * 第六步:更新其他Section節
- */
- elfdest.shstrtab = elfsrc.shstrtab;
- elfdest.dynamic = elfsrc.dynamic;
- elfdest.dynstr = elfsrc.dynstr;
- elfdest.comment = elfsrc.comment;
- elfdest.noteGunGoldVe = elfsrc.noteGunGoldVe;
- elfdest.armattributes = elfsrc.armattributes;
- elfdest.data = elfsrc.data;
- elfdest.got = elfsrc.got;
-
- elfdest.copySectionList(elfsrc.secheaderList);
- elfdest.copySegmentList(elfsrc.proheaderList);
- elfdest.setZeroInitArray();
- elfdest.updateSectionData();
-
- /*
- * 第七步:一般情況下,Program Segment頭訊息位於elf的頂部,Section頭訊息位於elf的底部
- * 從記憶體中dump出來時,有可能Section頭訊息需要移動位置
- */
- int shoff = ELFUtils.byte2Int(elfdest.elfheader.e_shoff);
- if(shoff > __SECTION_START_OFFSET__){
- System.out.println("準備移動Section頭訊息。。。");
- shoff += 0x1000;
- byte [] offsetbytes = ELFUtils.int2Byte(shoff);
- System.arraycopy(offsetbytes, 0, elfdest.elfheader.e_shoff, 0, 4);
- byte [] dataupdate = elfdest.elfheader.getSrcDataUpdate();
- elfdest.updateELFHeader();
- elfdest.updateSectionHeader(updateSectionDumpBytes);
- System.out.println("將源包的Section頭訊息更新到dump包上,第二次更新");
- }else{
- System.out.println("不需要移動Section頭訊息");
- }
-
- /*
- * 第八步:保存到檔案
- */
- ELFUtils.saveFile(repairsofile, elfdest.getELFFileBytes());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- }
-
-
- }
複製代碼 |
評分
-
查看全部評分

|