读取AXML二进制流修改DEMO

很重要的参考博客:http://blog.csdn.net/jiangwei0910410003/article/details/50568487 

在此感谢原作者

本DEMO目标为隐藏APP启动图标。

方法为修改AXML文件中<category android:name="android.intent.category.LAUNCHER"/>至<category android:name="android.intent.category.DEFAULT"/>同时修改各项参数:

文件大小段,字符串数目,字符串偏移量,字符串chunk参数,字符串段开始位置偏移量。

注意:

修改AXML文件后要重新签名

最后的文件一定要4位对齐不然会导致APP无法安装

代码如下:

package cn.com.silence;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

public class MainClass {
	/**
	 * 用来保存所有的字符串,在xmlcontent里会调用
	 */
	public static ArrayList<String> stringList = new ArrayList<String>();
	/**
	 * 存储所有的resouceID暂时没用上
	 */
	private static ArrayList<Integer> resIdList = new ArrayList<Integer>();
	/**
	 * android.intent.category数据段
	 */
	private static final byte[] CATEGOTY = new byte[] { 0x20, 0x00, 0x61, 0x00,
			0x6E, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x69, 0x00, 0x64,
			0x00, 0x2E, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x65, 0x00,
			0x6E, 0x00, 0x74, 0x00, 0x2E, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74,
			0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x79, 0x00,
			0x2E, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x43,
			0x00, 0x48, 0x00, 0x45, 0x00, 0x52, 0x00, 0x00, 0x00 };
	/**
	 * android.intent.DEFAULT数据段后面的0是用来4位对齐,文件不是4的整数倍无法安装
	 */
	private static final byte[] DEFAULT = new byte[] { 0x1F, 0x00, 0x61, 0x00,
			0x6E, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x69, 0x00, 0x64,
			0x00, 0x2E, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x65, 0x00,
			0x6E, 0x00, 0x74, 0x00, 0x2E, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74,
			0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x79, 0x00,
			0x2E, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x41, 0x00, 0x55,
			0x00, 0x4c, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00 };
	/**
	 * categroy数据段长度
	 */
	private static final int CATEGORYLENGTH = CATEGOTY.length;
	/**
	 * category string字段位置
	 */
	private static int categoryPosition = -1;
	/**
	 * string段结束位置
	 */
	private static int stringEndPosition;
	/**
	 * 偏移量段结束位置
	 */
	private static int stringStartPosition;
	/**
	 * category后string段偏移量
	 */
//	private static ArrayList<HashMap<String, Integer>> laterStringOffset;
	private static int xmlSize;
	private static byte[] xmlData;

	/**
	 * 修改xmlcontent段中,category块里参数对应value的指向stringlist的值,即从launcher重新指向default
	 * 
	 * @param pos tag中value所在的位置
	 */
	private static void modifyContent(int pos) {
		byte[] buffer = Utils.intToByte(stringList.size());
		System.arraycopy(buffer, 0, xmlData, pos, 4);
		System.out.println("Content modify finished...");
	}

	/**
	 * 修改文件大小
	 */
	private static void modifyFileSize() {
		System.out.println(xmlSize);
		byte[] sizeData = Utils.intToByte(xmlSize);
		System.arraycopy(sizeData, 0, xmlData, 4, 4);
		System.out.println("file size modified...");
	}

	/**
	 * 修改字符串端开始前的参数
	 */
	private static void modifyOffset() {
		byte[] stringCount = new byte[4];
		System.arraycopy(xmlData, 16, stringCount, 0, 4);
		int lastStringOffset = 36 + Utils.byteToInt(stringCount) * 4;
		stringCount = Utils.intToByte(Utils.byteToInt(stringCount) + 1);
		System.arraycopy(stringCount, 0, xmlData, 16, 4);// string count + 1

		byte[] newStringChunkSize = new byte[4];
		System.arraycopy(xmlData, 12, newStringChunkSize, 0, 4);
		newStringChunkSize = Utils.intToByte(Utils
				.byteToInt(newStringChunkSize) + DEFAULT.length + 4);
		System.arraycopy(newStringChunkSize, 0, xmlData, 12, 4);// stringChunkSize
																// + 72
		int z = stringStartPosition - 8 + 4;
		byte[] newStringStartOffset = Utils.intToByte(z);
		System.arraycopy(newStringStartOffset, 0, xmlData, 28, 4);// string
																	// start
																	// pos++

		byte[] temp = new byte[xmlSize + 4];
		byte[] nStringOffset = Utils.intToByte(stringEndPosition
				- stringStartPosition - DEFAULT.length);// add new offset

		System.arraycopy(xmlData, 0, temp, 0, lastStringOffset);
		System.arraycopy(nStringOffset, 0, temp, lastStringOffset, 4);
		System.arraycopy(xmlData, lastStringOffset, temp, lastStringOffset + 4,
				xmlSize - lastStringOffset);
		xmlSize = xmlSize + 4;
		xmlData = new byte[xmlSize];
		System.arraycopy(temp, 0, xmlData, 0, xmlSize);
		stringEndPosition += 4;
		System.out.println("offset modify finished...");
	}

	/**
	 * 增加android.intent.category.default字符串
	 */
	private static void modifyString() {
		byte[] temp = new byte[xmlSize + DEFAULT.length];
		System.arraycopy(xmlData, 0, temp, 0, stringEndPosition);
		System.arraycopy(DEFAULT, 0, temp, stringEndPosition, DEFAULT.length);
		System.arraycopy(xmlData, stringEndPosition, temp, stringEndPosition
				+ DEFAULT.length, xmlSize - stringEndPosition);

		stringEndPosition += DEFAULT.length;
		xmlSize = xmlSize + DEFAULT.length;
		xmlData = new byte[xmlSize];
		System.arraycopy(temp, 0, xmlData, 0, xmlSize);
	}
	
	/**
	 * 获取string段结束的位置
	 * @param data 二进制axml文件
	 * @return 位置
	 */
	private static int getEndPosition(byte[] data) {
		int i = 0;
		byte[] temp = new byte[4];
		System.arraycopy(data, 12, temp, 0, 4);
		i = Utils.byteToInt(temp);
		i = i + 8;
		return i;
	}
	
	/**
	 * 一大堆里最后用上的只有string偏移量段结束的位置——字符串段开始的位置
	 * @param data AXML二进制文件
	 */
	private static void getCategory(byte[] data) {
//		int i = -1;
		byte[] tempNum = new byte[4];
		System.arraycopy(data, 28, tempNum, 0, 4);
		stringStartPosition = Utils.byteToInt(tempNum) + 8;// 偏移量段结束位置
		System.out.println("string off set position: " + stringStartPosition);
		byte[] temp = new byte[CATEGORYLENGTH];
		for (int j = 36; j < stringStartPosition; j += 4) {
			System.arraycopy(data, j, tempNum, 0, 4);
			int stringOffset = Utils.byteToInt(tempNum) + stringStartPosition;
			// System.out.println(stringOffset);
			System.arraycopy(data, stringOffset, temp, 0, CATEGORYLENGTH);
			if (Arrays.equals(temp, CATEGOTY)) {
				categoryPosition = stringOffset;
				break;
			}
		}
	}

	public static void main(String[] args) throws Exception {
		FileInputStream fis = new FileInputStream(new File(
				"E:/AndroidManifest.xml"));
		xmlSize = fis.available();
		xmlData = new byte[xmlSize];
		fis.read(xmlData, 0, xmlSize);
		fis.close(); 
		stringEndPosition = getEndPosition(xmlData);
		System.out.println("StringChunk end position: " + stringEndPosition);

		getCategory(xmlData);// category位置

		if (categoryPosition == -1) {
			System.out.println("Category not found");
			Runtime.getRuntime().exit(0);
		}
		System.out.println("category string position: " + categoryPosition);
		System.out.println("string start pos: " + stringStartPosition);
		System.out.println("string end pos: " + stringEndPosition);

		stringList = ReadData.strings(xmlData, stringStartPosition);
		resIdList = ReadData.resID(xmlData, stringEndPosition);

//		int count = 0;
		int pos = 0;

		modifyString();
		modifyOffset();

		Find find = new Find(xmlData, stringEndPosition);
		pos = find.getPosition();
		byte[] buffer = new byte[4];
		System.arraycopy(xmlData, pos + 44, buffer, 0, 4);
		System.out.println("valueString = "
				+ stringList.get(Utils.byteToInt(buffer)));

		modifyContent(pos + 44);
		modifyFileSize();

		FileOutputStream fos = new FileOutputStream(new File(
				"E:/AndroidManifest.xml"));
		fos.write(xmlData, 0, xmlSize);
		fos.flush();
		fos.close();

		System.out.println("All finished....");
	}
}

下面这个类用于读取string和resourceID

package cn.com.silence;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

public class ReadData {
	public static ArrayList<String> strings(byte[] data, int stringOffsetPosition){
		ArrayList<String> stringList = new ArrayList<String>();
		byte[] tempNum = new byte[4];
		byte[] string;
		System.out.println("********************Strings below***********************");
		for (int j = 36; j < stringOffsetPosition; j += 4) {
			System.arraycopy(data, j, tempNum, 0, 4);
			int stringOffset = Utils.byteToInt(tempNum) + stringOffsetPosition;
			tempNum = new byte[]{0, 0, 0, 0};
			System.arraycopy(data, stringOffset, tempNum, 0, 2);
			int stringLength = Utils.byteToInt(tempNum);
			string = new byte[stringLength * 2];
			System.arraycopy(data, stringOffset + 2, string, 0, string.length);
			byte[] printUse = new byte[stringLength];
			for (int i = 0; i < stringLength; i++) {
				System.arraycopy(string, i * 2, printUse, i, 1);
			}
			System.out.println(new String(printUse));
			stringList.add(new String(printUse));
		}
		System.out.println("********************Strings finished***********************");
		return stringList;
	}
	
	public static ArrayList<Integer> resID(byte[] data, int startPos){
		byte[] sizeData = new byte[4];
		byte[] idData = new byte[4];
		System.arraycopy(data, startPos + 4, sizeData, 0, 4);
		int size = Utils.byteToInt(sizeData);
		ArrayList<Integer> resourceIdList = new ArrayList<Integer>(size - 2);
		
		System.out.println("********************resID below***********************");
		System.arraycopy(data, startPos, idData, 0, 4);
		System.out.println("start data: " + Arrays.toString(idData) + "\nsize: " + size);
		for (int i = 0; i  < (size - 8); i += 4) {
			System.arraycopy(data, startPos + 8 + i, idData, 0, 4);
			int resID = Utils.byteToInt(idData);
			System.out.println("id: "+ resID + ", hex: " + Arrays.toString(idData));
			resourceIdList.add(resID);
		}
		System.out.println("********************resID finished***********************");
		return resourceIdList;
	}
}

package cn.com.silence;

public class Utils {
	public static int byteToInt(byte[] buffer) {
		int i = (((buffer[3] << 24) & 0xff000000)
				| ((buffer[2] << 16) & 0xff0000) | ((buffer[1] << 8) & 0xff00) | ((buffer[0]) & 0xff));
		return i;
	}

	public static byte[] intToByte(int i) {
		byte[] buffer = new byte[4];
		buffer[3] = (byte) (((i) >> 24) & 0xff);
		buffer[2] = (byte) (((i) >> 16) & 0xff);
		buffer[1] = (byte) (((i) >> 8) & 0xff);
		buffer[0] = (byte) ((i) & 0xff);
		return buffer;
	}
}
下面这个类用于查找<category android:name="android.intent.category.LAUNCHER"/>字段在xmlcontent的位置,并返回
package cn.com.silence;

import java.util.Arrays;

public class Find {
	private int position = 0;
	private int output = -1;
	private byte[] data;
	private final byte[] tagBegin = { 0x02, 0x01, 0x10, 0x00 };
	private final byte[] tagEnd = { 0x03, 0x01, 0x10, 0x00 };

	public Find(byte[] b, int pos) {
		data = b;
		int count = 0;
		for (position = pos; position + 5 < data.length; position++) {
			if (tagStart()) {
				count++;
				if (count == 5 && check()) {
					output = position;
					System.out.println("xmlContent Category Position:  "
							+ position);
					break;
				}
			} else if (tagEnd()) {
				count--;
			}
		}
	}

	private boolean check() {
		byte[] buffer = new byte[4];
		System.arraycopy(data, position + 20, buffer, 0, 4);
		byte[] buffer1 = new byte[4];
		System.arraycopy(data, position + 44, buffer1, 0, 4);
		System.out.println(position);
		System.out.println(Utils.byteToInt(buffer) + "###############################"
				+ MainClass.stringList.get(Utils.byteToInt(buffer)).equals(
						"category"));
		System.out.println(Utils.byteToInt(buffer1) + "###############################"
				+ MainClass.stringList.get(Utils.byteToInt(buffer1)).equals(
						"android.intent.category.LAUNCHER"));
		
		if (MainClass.stringList.get(Utils.byteToInt(buffer))
				.equals("category")
				&& MainClass.stringList.get(Utils.byteToInt(buffer1)).equals(
						"android.intent.category.LAUNCHER")) {
			return true;
		} else {
			return false;
		}
	}

	private boolean tagStart() {
		byte[] a = new byte[4];
		System.arraycopy(data, position, a, 0, 4);
		if (Arrays.equals(a, tagBegin)) {
			return true;
		} else {
			return false;
		}

	}

	private boolean tagEnd() {
		byte[] a = new byte[4];
		System.arraycopy(data, position, a, 0, 4);
		if (Arrays.equals(a, tagEnd)) {
			return true;
		} else {
			return false;
		}
	}

	public int getPosition() {
		return output;
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二进制XML存储方案BinXML实现方法 BinXML是我个人杜撰出来的名字,不知道是否合适,也不知道是否已存在类似的解决方案。在vcer.net上我将前一段时间的这个BinXML方案贡献出来,希望能够与广大vcer分享。 当时问题的背景是这样的,项目需要确定存储方案,这种方案需要满足如下要求: · 生成的单机板exe尽可能的不依赖于其它软件,如:数据库管理系统; · 存储的数据最好能够方便的拷贝,以满足项目的上报、汇总的功能; · 存储的格式以后可能还会变,变化不频繁,但是最好能支持这种变化; · 只需要数据的持久化与反持久化的功能,不需要查询统计等复杂功能; · 存储的数据中可能包括一个或者多个的文件附件,如:word文档; · 存储的数据可能被不同的平台使用,如:windows或者linux; · 作为web项目的一部分,除了提供VC的接口之外,存储的数据格式需要提供java的接口; 我首先考虑到了MsAccess格式,文件型数据库,方便拷贝,而且表的设计很柔性化。在windows环境下,MsAccess似乎可以不需要再安装额外的驱动,但是在linux环境下,如何被java调用是个问题(当然,这个问题也是可以解决的,但是很别扭)。其次,使用RDB还有一个问题:数据的层次表达与多值问题,将树型数据扁平化存储的方案是有的,但是,将几层简单的节点拆分成N个表格,岂不是杀鸡用牛刀? 其次的考虑当然是XML,然而XML是基于可读文本的,如何解决二进制数据问题?当然可以通过编码的途径来解决,但是这样使用XML是不是太牵强了?而且,XML有个缺陷,数据都是文本型的。要使用数值型、布尔型、日期型的数据,需要做进一步的解析。 那么就使用自定义的数据文件格式?传统情况下,我们会用一个或者若干个struct将数据打包,一下子塞进文件。但是现在用户说了:我们现在定义的数据结构可能会变:) 看来,我们的存储方案还必须要足够的柔性化。 想到最后,我决定还是借鉴XML的树型标签形式,来实现一种二进制的存储结构,即BinXML:) 关于src包 对于BinXML-src里面的例子,数据的结构类似于: BinXML-src.zip包括两部分,一部分是vc的工程,一部分是java的工程,在Visual C++6.0和eclipse+JDK1.4中都已编译、运行通过。大家可以运行BinXML-bin.zip里面的exe程序,测试一下BinXML文件的加载和保存功能。 对象模型 BinXML的对象模型主要包括:文档(document)和节点(node)。每个文档包含一个根节点(root),根节点下面包含一个或者多个子节点,如此类推。 文档包含一个文件头,其中包含了一些标识串、操作系统版本、文件大小、文档创建时间等信息。 每个节点也包含一个节点头,表明该节点的名称以及大小。一般来说,用户不会直接接触到文件头和节点头这些信息。 如下为VC里的类声明: 在java里,定义了binxml.io包,其中的类、接口定义与以上相似。 你很容易想到,使用BinXML,可以很方便地完成CTreeCtrl的持久化。 如何使用? 如下是VC中加载BinXML的代码片断: 对应的,写入BinXML的代码片断: 是不是很简单?在java里面,BinXML的一个测试用例: TODO 希望BinXML能给你带来一定的帮助和启发,更多的是希望能带来启发:)希望有兴趣的哥们继续完善BinXML,别忘了在你的大名之前保留我的版权信息: BinXML需要完善的地方表现在: · 暂时只实现了string/long/byte[],还没有提供其它类型的解析与转换; · XML标签不支持属性,只支持子元素:) · 是不是可以提供一个oxm模型,完成BinXML与Object之间的直接映射,现在这个映射工作还是人工代码实现的,譬如:GetString/GetLong,等等。应该可以完善一个映射机制,自动根据定义好的类型进行转换;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值