APK反编译、重打包、签名之JAVA代码实现

转载自https://blog.csdn.net/yypblog/article/details/40481901
略经修改。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class ApkDemo02 {
	private static final int BUFFER = 1024;
	private static final String BASE_DIR = "";
	/** 符号"/"用来作为目录标识判断符 */
	private static final String PATH = "/";
	/** 签名目录 */
	private static final String SIGN_PATH_NAME = "META-INF";
	/** 解压源文件目录 */
	private static final String SOURCE_PATH_NAME = "\\source\\";
	/** 打包目录 */
	private static final String TARGET_PATH_NAME = "\\target\\";
	/** 签名目录 */
	private static final String RESULT_PATH_NAME = "\\result\\";
	/** JDK BIN 目录 此处依配置情况酌情修改*/
	//此处依配置情况酌情修改
	private static final String JDK_BIN_PATH = "C:\\Program Files\\Java\\jdk1.7.0_21\\bin";
	/** 密钥 目录 */
	//此处依个人情况酌情修改
	private static final String SECRET_KEY_PATH = "C:\\Users\\Jakieenchan\\Desktop\\Analyse_Apk\\keystore\\";
	/** 密钥 名称 */
	//此处依个人情况酌情修改
	private static final String SECRET_KEY_NAME = "abc.keystore";

	/**
	 * 解压缩zip文件
	 * 
	 * @param fileName
	 *            要解压的文件名 包含路径 如:"c:\\test.zip"
	 * @param filePath
	 *            解压后存放文件的路径 如:"c:\\temp"
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public static void unZip(String fileName, String filePath) throws Exception {
		ZipFile zipFile = new ZipFile(fileName);
		Enumeration emu = zipFile.entries();
		while (emu.hasMoreElements()) {
			ZipEntry entry = (ZipEntry) emu.nextElement();
			if (entry.isDirectory()) {
				new File(filePath + entry.getName()).mkdirs();
				continue;
			}
			BufferedInputStream bis = new BufferedInputStream(
					zipFile.getInputStream(entry));
			File file = new File(filePath + entry.getName());
			File parent = file.getParentFile();
			if (parent != null && (!parent.exists())) {
				parent.mkdirs();
			}
			FileOutputStream fos = new FileOutputStream(file);
			BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER);
			byte[] buf = new byte[BUFFER];
			int len = 0;
			while ((len = bis.read(buf, 0, BUFFER)) != -1) {
				fos.write(buf, 0, len);
			}
			bos.flush();
			bos.close();
			bis.close();
		}
		zipFile.close();
	}

	/**
	 * 压缩文件
	 *
	 * @param srcFile
	 * @param destPath
	 * @throws Exception
	 */
	public static void compress(String srcFile, String destPath)
			throws Exception {
		compress(new File(srcFile), new File(destPath));
	}

	/**
	 * 压缩
	 *
	 * @param srcFile
	 *            源路径
	 * @param destPath
	 *            目标路径
	 * @throws Exception
	 */
	public static void compress(File srcFile, File destFile) throws Exception {
		// 对输出文件做CRC32校验
		CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(
				destFile), new CRC32());

		ZipOutputStream zos = new ZipOutputStream(cos);
		compress(srcFile, zos, BASE_DIR);

		zos.flush();
		zos.close();
	}

	/**
	 * 压缩
	 *
	 * @param srcFile
	 *            源路径
	 * @param zos
	 *            ZipOutputStream
	 * @param basePath
	 *            压缩包内相对路径
	 * @throws Exception
	 */
	private static void compress(File srcFile, ZipOutputStream zos,
			String basePath) throws Exception {
		if (srcFile.isDirectory()) {
			compressDir(srcFile, zos, basePath);
		} else {
			compressFile(srcFile, zos, basePath);
		}
	}

	/**
	 * 压缩目录
	 *
	 * @param dir
	 * @param zos
	 * @param basePath
	 * @throws Exception
	 */
	private static void compressDir(File dir, ZipOutputStream zos,
			String basePath) throws Exception {
		File[] files = dir.listFiles();
		// 构建空目录
		if (files.length < 1) {
			ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH);

			zos.putNextEntry(entry);
			zos.closeEntry();
		}
		String dirName = "";
		String path = "";
		for (File file : files) {
			// 当父文件包名为空时,则不把包名添加至路径中(主要是解决压缩时会把父目录文件也打包进去)
			if (basePath != null && !"".equals(basePath)) {
				dirName = dir.getName();
			}
			path = basePath + dirName + PATH;
			// 递归压缩
			compress(file, zos, path);
		}
	}

	/**
	 * 文件压缩
	 *
	 * @param file
	 *            待压缩文件
	 * @param zos
	 *            ZipOutputStream
	 * @param dir
	 *            压缩文件中的当前路径
	 * @throws Exception
	 */
	private static void compressFile(File file, ZipOutputStream zos, String dir)
			throws Exception {
		/**
		 * 压缩包内文件名定义
		 *
		 * 如果有多级目录,那么这里就需要给出包含目录的文件名 如果用WinRAR打开压缩包,中文名将显示为乱码
		 */
		if ("/".equals(dir))
			dir = "";
		else if (dir.startsWith("/"))
			dir = dir.substring(1, dir.length());

		ZipEntry entry = new ZipEntry(dir + file.getName());
		zos.putNextEntry(entry);
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
				file));
		int count;
		byte data[] = new byte[BUFFER];
		while ((count = bis.read(data, 0, BUFFER)) != -1) {
			zos.write(data, 0, count);
		}
		bis.close();

		zos.closeEntry();
	}

	public static void main(String[] args) throws Exception {
		StringBuffer buffer = new StringBuffer();
		BufferedReader br = null;
		OutputStreamWriter osw = null;
		String srcPath = "C:\\Users\\Jakieenchan\\Desktop\\Analyse_Apk\\aa\\TestPrint.APK";
		String content = "channel_id=LD20120926";

		File srcFile = new File(srcPath);
		String parentPath = srcFile.getParent(); // 源文件目录
		String fileName = srcFile.getName(); // 源文件名称
		String prefixName = fileName.substring(0, fileName.lastIndexOf("."));//文件前缀名
		
		
		// 解压源文件保存路径
		String sourcePath = buffer.append(parentPath).append(SOURCE_PATH_NAME)
				.append(prefixName).append("\\").toString();
		// ------解压
		unZip(srcPath, sourcePath);

		// ------删除解压后的签名文件
		String signPathName = sourcePath + SIGN_PATH_NAME;
		File signFile = new File(signPathName);
		if (signFile.exists()) {
			File sonFiles[] = signFile.listFiles();
			if (sonFiles != null && sonFiles.length > 0) {
				// 循环删除签名目录下的文件
				for (File f : sonFiles) {
					f.delete();
				}
			}
			signFile.delete();
		}
		
		// -----------------------------------------------------------------------
		// ---------      如果有需要可在此处填写修改APK内容的代码       -------------
		// -----------------------------------------------------------------------
		
		// ------打包
		String targetPath = parentPath + TARGET_PATH_NAME;
		// 判断创建文件夹
		File targetFile = new File(targetPath);
		if (!targetFile.exists()) {
			targetFile.mkdir();
		}
		String compressSrcFile = parentPath + SOURCE_PATH_NAME + prefixName;
		String compressDesPath = targetPath + fileName;
		compress(compressSrcFile, compressDesPath);
        
		// ------签名
		File ff = new File(JDK_BIN_PATH); 
		String resultPath = parentPath + RESULT_PATH_NAME;
		// 判断创建文件夹
		File resultFile = new File(resultPath);
		if (!resultFile.exists()) {
			resultFile.mkdir();
		}

		// 组合签名命令
		buffer.setLength(0);
		buffer.append("cmd.exe /c jarsigner -keystore ")
				.append(SECRET_KEY_PATH).append(SECRET_KEY_NAME) 
				.append(" -storepass 111111 -signedjar ").append(resultPath)
				.append(fileName).append(" ") // 签名保存路径应用名称
				.append(targetPath).append(fileName).append(" ") // 打包保存路径应用名称
				.append(SECRET_KEY_NAME);
		// 利用命令调用JDK工具命令进行签名
		Process process = Runtime.getRuntime()
				.exec(buffer.toString(), null, ff);
		if (process.waitFor() != 0){
			System.out.println("文件打包失败!!!");
		}else{
			System.out.println("--文件打包成功!!!!--");
		}	
	}
}

注意
1.实际使用中,变量JDK_BIN_PATHSECRET_KEY_PATHSECRET_KEY_NAME请按照个人情况修改。
2.组合签名命令部分中的“111111”是指keystore的密钥库口令,请按照个人实际情况修改。
3.关于密钥文件keystore,可参考本人的另一篇文章https://blog.csdn.net/qq_25844803/article/details/84953332
4.以上方法不能对apk的android代码(程序)进行修改,只能修改类似资源文件,布局文件

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值