Java通过解析文件获取apk信息

参考:https://github.com/bihe0832/Android-GetAPKInfo

ApkInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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);
}
}

运行输出

1
2
3
4
5
6
7
8
9
10
11
12
执行结果: 成功
应用信息:
包名: cn.appblog.androidx
版本名: 1.0
版本号: 1
签名文件MD5: a11e91a81004207c93fd57ee52cfcc3c
SDK版本:
minSdkVersion: 16
targetSdkVersion: 29
V1签名验证通过: true
使用V2签名: true
V2签名验证通过: true

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :