Java通过解析文件获取apk信息
参考:https://github.com/bihe0832/Android-GetAPKInfo
ApkInfo
@Data
@NoArgsConstructor
public class ApkInfo {
public String versionCode = "";
public String versionName = "";
public String packageName = "";
public String signature = "";
public String minSdkVersion = "";
public String targetSdkVersion = "";
public boolean isV1SignatureOK = false;
public boolean isV2Signature = false;
public boolean isV2SignatureOK = false;
public String v2CheckErrorInfo = "";
public ArrayList<String> permissions = new ArrayList<String>();
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(" 包名: " + packageName + "\n");
sb.append(" 版本名: " + versionName + "\n");
sb.append(" 版本号: " + versionCode + "\n");
sb.append(" 签名文件MD5: " + signature + "\n");
sb.append(" SDK版本:\n");
sb.append(" minSdkVersion: " + minSdkVersion + "\n");
sb.append(" targetSdkVersion: " + targetSdkVersion + "\n");
sb.append(" V1签名验证通过: " + isV1SignatureOK + "\n");
sb.append(" 使用V2签名: " + isV2Signature + "\n");
sb.append(" V2签名验证通过: " + isV2SignatureOK + "\n");
if(!isV1SignatureOK || (isV2Signature && !isV2SignatureOK)){
sb.append(" 签名验证失败原因: " + v2CheckErrorInfo + "\n");
}
// sb.append(" 使用权限列表:\n");
// for (String string : permissions) {
// sb.append(" "+ string +"\n");
// }
return sb.toString();
}
}
ApkUtil
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import me.yezhou.model.ApkInfo;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.input.SAXBuilder;
import java.util.Iterator;
import java.util.List;
public class ApkUtil {
private static final Namespace NS = Namespace.getNamespace("http://schemas.android.com/apk/res/android");
public static void getApkInfo(String apkPath, ApkInfo info, boolean showException) {
SAXBuilder builder = new SAXBuilder();
Document document = null;
try {
InputStream stream = new ByteArrayInputStream(AXMLPrinter.getManifestXMLFromAPK(apkPath).getBytes(StandardCharsets.UTF_8));
document = builder.build(stream);
} catch (Exception e) {
if (showException) {
e.printStackTrace();
}
}
Element root = document.getRootElement();
info.versionCode = root.getAttributeValue("versionCode", NS);
info.versionName = root.getAttributeValue("versionName", NS);
String s = root.getAttributes().toString();
String c[] = s.split(",");
for (String a : c) {
if (a.contains("package")) {
info.packageName = a.substring(a.indexOf("package=\"") + 9, a.lastIndexOf("\""));
}
}
List booklist = root.getChildren("uses-sdk");
Element book = (Element) booklist.get(0);
info.minSdkVersion = book.getAttributeValue("minSdkVersion", NS);
info.targetSdkVersion = book.getAttributeValue("targetSdkVersion", NS);
booklist = root.getChildren("uses-permission");
for (Iterator iter = booklist.iterator(); iter.hasNext(); ) {
Element tempBook = (Element) iter.next();
info.permissions.add(tempBook.getAttributeValue("name", NS));
}
}
}
AXMLPrinter
import java.io.File;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.xmlpull.v1.XmlPullParser;
import android.content.res.AXmlResourceParser;
import android.util.TypedValue;
/**
* This is example usage of AXMLParser class.
* <p>
* Prints xml document from Android's binary xml file.
*/
public class AXMLPrinter {
private static final String DEFAULT_XML = "AndroidManifest.xml";
public static String getManifestXMLFromAPK(String apkPath) {
ZipFile file = null;
StringBuilder xmlSb = new StringBuilder(100);
try {
File apkFile = new File(apkPath);
file = new ZipFile(apkFile, ZipFile.OPEN_READ);
ZipEntry entry = file.getEntry(DEFAULT_XML);
AXmlResourceParser parser = new AXmlResourceParser();
parser.open(file.getInputStream(entry));
StringBuilder sb = new StringBuilder(10);
final String indentStep = " ";
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
switch (type) {
case XmlPullParser.START_DOCUMENT: {
log(xmlSb, "<?xml version=\"1.0\" encoding=\"utf-8\"?>");
break;
}
case XmlPullParser.START_TAG: {
log(false, xmlSb, "%s<%s%s", sb,
getNamespacePrefix(parser.getPrefix()), parser.getName());
sb.append(indentStep);
int namespaceCountBefore = parser.getNamespaceCount(parser.getDepth() - 1);
int namespaceCount = parser.getNamespaceCount(parser.getDepth());
for (int i = namespaceCountBefore; i != namespaceCount; ++i) {
log(xmlSb, "%sxmlns:%s=\"%s\"",
i == namespaceCountBefore ? " " : sb,
parser.getNamespacePrefix(i),
parser.getNamespaceUri(i));
}
for (int i = 0, size = parser.getAttributeCount(); i != size; ++i) {
log(false, xmlSb, "%s%s%s=\"%s\"", " ",
getNamespacePrefix(parser.getAttributePrefix(i)),
parser.getAttributeName(i),
getAttributeValue(parser, i));
}
// log("%s>",sb);
log(xmlSb, ">");
break;
}
case XmlPullParser.END_TAG: {
sb.setLength(sb.length() - indentStep.length());
log(xmlSb, "%s</%s%s>", sb,
getNamespacePrefix(parser.getPrefix()),
parser.getName());
break;
}
case XmlPullParser.TEXT: {
log(xmlSb, "%s%s", sb, parser.getText());
break;
}
}
}
parser.close();
} catch (Exception e) {
e.printStackTrace();
}
// System.out.println(xmlSb.toString());
return xmlSb.toString();
}
private static String getNamespacePrefix(String prefix) {
if (prefix == null || prefix.length() == 0) {
return "";
}
return prefix + ":";
}
private static String getAttributeValue(AXmlResourceParser parser, int index) {
int type = parser.getAttributeValueType(index);
int data = parser.getAttributeValueData(index);
if (type == TypedValue.TYPE_STRING) {
return parser.getAttributeValue(index);
}
if (type == TypedValue.TYPE_ATTRIBUTE) {
return String.format("?%s%08X", getPackage(data), data);
}
if (type == TypedValue.TYPE_REFERENCE) {
return String.format("@%s%08X", getPackage(data), data);
}
if (type == TypedValue.TYPE_FLOAT) {
return String.valueOf(Float.intBitsToFloat(data));
}
if (type == TypedValue.TYPE_INT_HEX) {
return String.format("0x%08X", data);
}
if (type == TypedValue.TYPE_INT_BOOLEAN) {
return data != 0 ? "true" : "false";
}
if (type == TypedValue.TYPE_DIMENSION) {
return Float.toString(complexToFloat(data)) +
DIMENSION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];
}
if (type == TypedValue.TYPE_FRACTION) {
return Float.toString(complexToFloat(data)) +
FRACTION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];
}
if (type >= TypedValue.TYPE_FIRST_COLOR_INT && type <= TypedValue.TYPE_LAST_COLOR_INT) {
return String.format("#%08X", data);
}
if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) {
return String.valueOf(data);
}
return String.format("<0x%X, type 0x%02X>", data, type);
}
private static String getPackage(int id) {
if (id >>> 24 == 1) {
return "android:";
}
return "";
}
private static void log(StringBuilder xmlSb, String format, Object... arguments) {
log(true, xmlSb, format, arguments);
}
private static void log(boolean newLine, StringBuilder xmlSb, String format, Object... arguments) {
// System.out.printf(format,arguments);
// if(newLine) System.out.println();
xmlSb.append(String.format(format, arguments));
if (newLine) xmlSb.append("\n");
}
/////////////////////////////////// ILLEGAL STUFF, DONT LOOK :)
public static float complexToFloat(int complex) {
return (float) (complex & 0xFFFFFF00) * RADIX_MULTS[(complex >> 4) & 3];
}
private static final float RADIX_MULTS[] = {
0.00390625F, 3.051758E-005F, 1.192093E-007F, 4.656613E-010F
};
private static final String DIMENSION_UNITS[] = {
"px", "dip", "sp", "pt", "in", "mm", "", ""
};
private static final String FRACTION_UNITS[] = {
"%", "%p", "", "", "", "", "", ""
};
}
GetSignature
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class GetSignature {
public GetSignature() {
}
public static String getApkSignInfo(String apkFilePath, boolean showException) {
byte[] readBuffer = new byte[8192];
Certificate[] certs = null;
try {
JarFile e = new JarFile(apkFilePath);
Enumeration entries = e.entries();
while (entries.hasMoreElements()) {
JarEntry je = (JarEntry) entries.nextElement();
if (!je.isDirectory() && !je.getName().startsWith("META-INF/")) {
Certificate[] localCerts = loadCertificates(e, je, readBuffer);
boolean found = false;
if (certs == null) {
certs = localCerts;
} else {
for (int i = 0; i < certs.length; ++i) {
for (int j = 0; j < localCerts.length; ++j) {
if (certs[i] != null && certs[i].equals(localCerts[j])) {
found = true;
break;
}
}
if (certs.length != localCerts.length) {
e.close();
return null;
}
}
}
if (found) {
break;
}
}
}
e.close();
return getSignValidString(certs[0].getEncoded());
} catch (Exception var10) {
if (showException) {
var10.printStackTrace();
}
return "get signInfo failed, please use --debug get more info";
}
}
private static Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer) {
try {
InputStream e = jarFile.getInputStream(je);
while (e.read(readBuffer, 0, readBuffer.length) != -1) {
;
}
e.close();
return je != null ? je.getCertificates() : null;
} catch (Exception var4) {
var4.printStackTrace();
System.err.println("Exception reading " + je.getName() + " in " + jarFile.getName() + ": " + var4);
return null;
}
}
public static String toHexString(byte[] keyData) {
if (keyData == null) {
return null;
} else {
int expectedStringLen = keyData.length * 2;
StringBuilder sb = new StringBuilder(expectedStringLen);
for (int i = 0; i < keyData.length; ++i) {
String hexStr = Integer.toString(keyData[i] & 255, 16);
if (hexStr.length() == 1) {
hexStr = "0" + hexStr;
}
sb.append(hexStr);
}
return sb.toString();
}
}
private static String getSignValidString(byte[] sign) throws NoSuchAlgorithmException {
MessageDigest alga = null;
alga = MessageDigest.getInstance("MD5");
alga.update(sign);
return toHexString(alga.digest());
}
}
ApkMain
public class ApkMain {
private static boolean sShowDebug = false;
private static final int RET_GET_INFO_BAD = -1;
public static void main(String[] params) throws Exception {
getApkInfo("E:\\AppBlog\\AndroidX-1.0.apk");
}
private static void getApkInfo(String filePath) {
ApkInfo info = new ApkInfo();
try {
ApkUtil.getApkInfo(filePath, info, sShowDebug);
} catch (Exception e) {
showFailedCheckResult(RET_GET_INFO_BAD, "get apkinfo failed, throw an Exception ;please use --debug get more info");
return;
}
String v2Signature = ApkSignerTool.verify(filePath, sShowDebug);
try {
JSONObject jsonobject = JSON.parseObject(v2Signature);
info.isV1SignatureOK = jsonobject.getBoolean(ApkSignerTool.KEY_RESULT_IS_V1_OK);
info.isV2Signature = jsonobject.getBoolean(ApkSignerTool.KEY_RESULT_IS_V2);
info.isV2SignatureOK = jsonobject.getBoolean(ApkSignerTool.KEY_RESULT_IS_V2_OK);
info.v2CheckErrorInfo = jsonobject.getString(ApkSignerTool.KEY_RESULT_MSG);
info.signature = GetSignature.getApkSignInfo(filePath, sShowDebug);
info.v2CheckErrorInfo = jsonobject.getString(ApkSignerTool.KEY_RESULT_MSG);
} catch (Exception e) {
showFailedCheckResult(RET_GET_INFO_BAD, "get apk info failed, throw an Exception;please use --debug get more info");
return;
}
showSuccssedCheckResult(info);
}
private static void showSuccssedCheckResult(ApkInfo info) {
System.out.println("执行结果: 成功");
System.out.println("应用信息: \n" + info.toString());
}
private static void showFailedCheckResult(int ret, String Msg) {
System.out.println("执行结果: 失败(" + ret + ")");
System.out.println("错误信息:" + Msg);
}
}
运行输出
执行结果: 成功
应用信息:
包名: cn.appblog.androidx
版本名: 1.0
版本号: 1
签名文件MD5: a11e91a81004207c93fd57ee52cfcc3c
SDK版本:
minSdkVersion: 16
targetSdkVersion: 29
V1签名验证通过: true
使用V2签名: true
V2签名验证通过: true
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/19/java-obtains-apk-information-by-parsing-files/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
THE END
0
二维码
打赏
海报
Java通过解析文件获取apk信息
参考:https://github.com/bihe0832/Android-GetAPKInfo
ApkInfo
@Data
@NoArgsConstructor
public class ApkInfo {
public String versionCode = "……
文章目录
关闭
共有 0 条评论