java so apk_安卓so文件脱壳思路(java版)附源代码

[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("\n\n********************************************************************************");

System.out.println("************************************ 开始修复 **********************************");

System.out.println("********************************************************************************\n\n");

/**

* 判断是否需要移动Section头信息

*/

int __SECTION_START_OFFSET__ = 0;

List 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();

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值