.net core 从(本地)服务器获取APK文件并解析APK信息


1、apk解析除了使用客户端利用aapt.exe、unzip.exe开发客户端解析外,还可以直接利用服务进行解析

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
/// <summary>
/// 从本地服务器获取APK文件并解析APK信息
/// </summary>
/// <param name="fileName">APK文件的完整路径</param>
/// <returns></returns>
[HttpPost, HttpGet, HttpOptions, CorsOptions]
public IActionResult DecodeAPK(string fileName)
{
if(fileName.IndexOf(".apk") == -1 && fileName.IndexOf(".zip") == -1)
{
return ErrorResult("未获取到APP上传路径!", 111111);
}
// 从服务器取文件
if(!string.IsNullOrWhiteSpace(fileName))
{
fileName = fileName.Replace(@"\", @" / ");
ApkInfo apk = new ApkInfo();
// 处理apk信息
try
apk = ReadAPK.ReadApkFromPath(fileName);
catch(Exception ex)
return ErrorResult("APP上传失败!--> APK解析失败,失败原因为:" + ex.Message, 111150);
return SuccessResult(apk, "APK解析成功");
}
else
return ErrorResult("APP上传失败!--> 从服务器获取APK文件失败,请联系网站管理员!", 111151);
}

2、ReadAPK APK解析帮助类

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
/// <summary>
/// 读取APK信息
/// </summary>
public class ReadAPK
{
/// <summary>
/// 从上传apk的路径读取并解析apk信息
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static ApkInfo ReadApkFromPath(string path)
{
byte[] manifestData = null;
byte[] resourcesData = null;
var manifest = "AndroidManifest.xml";
var resources = "resources.arsc";
//读取apk,通过解压的方式读取
using(var zip = ZipFile.Read(path))
{
using(Stream zipstream = zip[manifest].OpenReader())
{
//将解压出来的文件保存到一个路径(必须这样)
using(var fileStream = File.Create(manifest, (int) zipstream.Length))
{
manifestData = new byte[zipstream.Length];
zipstream.Read(manifestData, 0, manifestData.Length);
fileStream.Write(manifestData, 0, manifestData.Length);
}
}
using(Stream zipstream = zip[resources].OpenReader())
{
//将解压出来的文件保存到一个路径(必须这样)
using(var fileStream = File.Create(resources, (int) zipstream.Length))
{
resourcesData = new byte[zipstream.Length];
zipstream.Read(resourcesData, 0, resourcesData.Length);
fileStream.Write(resourcesData, 0, resourcesData.Length);
}
}
}
ApkReader apkReader = new ApkReader();
ApkInfo info = apkReader.extractInfo(manifestData, resourcesData);
return info;
}
}

3、APK解析类

注:此段代码解析APK时,若APK包含中文会极其的卡顿,建议上传前先用Npinyin重命名再次上传,至于原因已提交GitHub,暂未得到回复,所以先自己重命名再上传吧

Wrong Local header signature: 0xFF8

img

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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
public class ApkReader
{
private static int VER_ID = 0;
private static int ICN_ID = 1;
private static int LABEL_ID = 2;
String[] VER_ICN = new String[3];
String[] TAGS = {
"manifest", "application", "activity"
};
String[] ATTRS = {
"android:", "a:", "activity:", "_:"
};
Dictionary < String, object > entryList = new Dictionary < String, object > ();
List < String > tmpFiles = new List < String > ();
public String fuzzFindInDocument(XmlDocument doc, String tag, String attr)
{
foreach(String t in TAGS)
{
XmlNodeList nodelist = doc.GetElementsByTagName(t);
for(int i = 0; i < nodelist.Count; i++)
{
XmlNode element = (XmlNode) nodelist.Item(i);
if(element.NodeType == XmlNodeType.Element)
{
XmlAttributeCollection map = element.Attributes;
for(int j = 0; j < map.Count; j++)
{
XmlNode element2 = map.Item(j);
if(element2.Name.EndsWith(attr))
{
return element2.Value;
}
}
}
}
}
return null;
}
private XmlDocument initDoc(String xml)
{
XmlDocument retval = new XmlDocument();
retval.LoadXml(xml);
retval.DocumentElement.Normalize();
return retval;
}
private void extractPermissions(ApkInfo info, XmlDocument doc)
{
ExtractPermission(info, doc, "uses-permission", "name");
ExtractPermission(info, doc, "permission-group", "name");
ExtractPermission(info, doc, "service", "permission");
ExtractPermission(info, doc, "provider", "permission");
ExtractPermission(info, doc, "activity", "permission");
}
private bool readBoolean(XmlDocument doc, String tag, String attribute)
{
String str = FindInDocument(doc, tag, attribute);
bool ret = false;
try
{
ret = Convert.ToBoolean(str);
}
catch
{
ret = false;
}
return ret;
}
private void extractSupportScreens(ApkInfo info, XmlDocument doc)
{
info.supportSmallScreens = readBoolean(doc, "supports-screens", "android:smallScreens");
info.supportNormalScreens = readBoolean(doc, "supports-screens", "android:normalScreens");
info.supportLargeScreens = readBoolean(doc, "supports-screens", "android:largeScreens");
if(info.supportSmallScreens || info.supportNormalScreens || info.supportLargeScreens) info.supportAnyDensity = false;
}
public ApkInfo extractInfo(byte[] manifest_xml, byte[] resources_arsx)
{
string manifestXml = string.Empty;
APKManifest manifest = new APKManifest();
try
{
manifestXml = manifest.ReadManifestFileIntoXml(manifest_xml);
}
catch(Exception ex)
{
throw ex;
}
XmlDocument doc = new XmlDocument();
doc.LoadXml(manifestXml);
return extractInfo(doc, resources_arsx);
}
public ApkInfo extractInfo(XmlDocument manifestXml, byte[] resources_arsx)
{
ApkInfo info = new ApkInfo();
VER_ICN[VER_ID] = "";
VER_ICN[ICN_ID] = "";
VER_ICN[LABEL_ID] = "";
try
{
XmlDocument doc = manifestXml;
if(doc == null) throw new Exception("Document initialize failed");
info.resourcesFileName = "resources.arsx";
info.resourcesFileBytes = resources_arsx;
// Fill up the permission field 不需要返回,注释
//extractPermissions(info, doc);
// Fill up some basic fields
info.minSdkVersion = FindInDocument(doc, "uses-sdk", "minSdkVersion");
info.targetSdkVersion = FindInDocument(doc, "uses-sdk", "targetSdkVersion");
info.versionCode = FindInDocument(doc, "manifest", "versionCode");
info.versionName = FindInDocument(doc, "manifest", "versionName");
info.packageName = FindInDocument(doc, "manifest", "package");
int labelID;
info.label = FindInDocument(doc, "application", "label");
if(info.label.StartsWith("@")) VER_ICN[LABEL_ID] = info.label;
else if(int.TryParse(info.label, out labelID)) VER_ICN[LABEL_ID] = String.Format("@{0}", labelID.ToString("X4"));
// Fill up the support screen field 不需要返回,注释
//extractSupportScreens(info, doc);
if(info.versionCode == null) info.versionCode = fuzzFindInDocument(doc, "manifest", "versionCode");
if(info.versionName == null) info.versionName = fuzzFindInDocument(doc, "manifest", "versionName");
else if(info.versionName.StartsWith("@")) VER_ICN[VER_ID] = info.versionName;
String id = FindInDocument(doc, "application", "android:icon");
if(null == id)
{
id = fuzzFindInDocument(doc, "manifest", "icon");
}
if(null == id)
{
Debug.WriteLine("icon resId Not Found!");
return info;
}#
region 获取APK名称的代码暂时注释, 运行时间太卡顿
// Find real strings
if(!info.hasIcon && id != null)
{
if(id.StartsWith("@android:")) VER_ICN[ICN_ID] = "@" + (id.Substring("@android:".Length));
else VER_ICN[ICN_ID] = String.Format("@{0}", Convert.ToInt32(id).ToString("X4"));
List < String > resId = new List < String > ();
for(int i = 0; i < VER_ICN.Length; i++)
{
if(VER_ICN[i].StartsWith("@")) resId.Add(VER_ICN[i]);
}
ApkResourceFinder finder = new ApkResourceFinder();
info.resStrings = finder.processResourceTable(info.resourcesFileBytes, resId);
if(!VER_ICN[VER_ID].Equals(""))
{
List < String > versions = null;
if(info.resStrings.ContainsKey(VER_ICN[VER_ID].ToUpper())) versions = info.resStrings[VER_ICN[VER_ID].ToUpper()];
if(versions != null)
{
if(versions.Count > 0) info.versionName = versions[0];
}
else
{
throw new Exception("VersionName Cant Find in resource with id " + VER_ICN[VER_ID]);
}
}
List < String > iconPaths = null;
if(info.resStrings.ContainsKey(VER_ICN[ICN_ID].ToUpper())) iconPaths = info.resStrings[VER_ICN[ICN_ID].ToUpper()];
if(iconPaths != null && iconPaths.Count > 0)
{
info.iconFileNameToGet = new List < String > ();
info.iconFileName = new List < string > ();
foreach(String iconFileName in iconPaths)
{
if(iconFileName != null)
{
if(iconFileName.Contains(@"/"))
{
info.iconFileNameToGet.Add(iconFileName);
info.iconFileName.Add(iconFileName);
info.hasIcon = true;
}
}
}
}
else
{
throw new Exception("Icon Cant Find in resource with id " + VER_ICN[ICN_ID]);
}
if(!VER_ICN[LABEL_ID].Equals(""))
{
List < String > labels = null;
if(info.resStrings.ContainsKey(VER_ICN[LABEL_ID])) labels = info.resStrings[VER_ICN[LABEL_ID]];
if(labels.Count > 0)
{
info.label = labels[0];
}
}
}#
endregion
}
catch(Exception e)
{
throw e;
}
return info;
}
private void ExtractPermission(ApkInfo info, XmlDocument doc, String keyName, String attribName)
{
XmlNodeList usesPermissions = doc.GetElementsByTagName(keyName);
if(usesPermissions != null)
{
for(int s = 0; s < usesPermissions.Count; s++)
{
XmlNode permissionNode = usesPermissions.Item(s);
if(permissionNode.NodeType == XmlNodeType.Element)
{
XmlNode node = permissionNode.Attributes.GetNamedItem(attribName);
if(node != null) info.Permissions.Add(node.Value);
}
}
}
}
private String FindInDocument(XmlDocument doc, String keyName, String attribName)
{
XmlNodeList usesPermissions = doc.GetElementsByTagName(keyName);
if(usesPermissions != null)
{
for(int s = 0; s < usesPermissions.Count; s++)
{
XmlNode permissionNode = usesPermissions.Item(s);
if(permissionNode.NodeType == XmlNodeType.Element)
{
XmlNode node = permissionNode.Attributes.GetNamedItem(attribName);
if(node != null) return node.Value;
}
}
}
return null;
}
}

4、APK解析返回类

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
public class ApkInfo
{
/// <summary>
/// APK名称
/// </summary>
public string label
{
get;
set;
}
/// <summary>
/// APK版本号
/// </summary>
public string versionName
{
get;
set;
}
/// <summary>
/// APK版本编号
/// </summary>
public string versionCode
{
get;
set;
}
/// <summary>
/// APK支持的最小SDK版本
/// </summary>
public string minSdkVersion
{
get;
set;
}
/// <summary>
/// APK的目标SDK版本
/// </summary>
public string targetSdkVersion
{
get;
set;
}
/// <summary>
/// APK包名称
/// </summary>
public string packageName
{
get;
set;
}
public static int FINE = 0;
public static int NULL_VERSION_CODE = 1;
public static int NULL_VERSION_NAME = 2;
public static int NULL_PERMISSION = 3;
public static int NULL_ICON = 4;
public static int NULL_CERT_FILE = 5;
public static int BAD_CERT = 6;
public static int NULL_SF_FILE = 7;
public static int BAD_SF = 8;
public static int NULL_MANIFEST = 9;
public static int NULL_RESOURCES = 10;
public static int NULL_DEX = 13;
public static int NULL_METAINFO = 14;
public static int BAD_JAR = 11;
public static int BAD_READ_INFO = 12;
public static int NULL_FILE = 15;
public static int HAS_REF = 16;
// 其他不返回属性权限、其他资源文件等等
public List < String > Permissions;
public List < String > iconFileName;
public List < String > iconFileNameToGet;
public List < String > iconHash;
public String resourcesFileName;
public byte[] resourcesFileBytes;
public bool hasIcon;
public bool supportSmallScreens;
public bool supportNormalScreens;
public bool supportLargeScreens;
public bool supportAnyDensity;
public Dictionary < String, List < String >> resStrings;
public Dictionary < String, String > layoutStrings;
public static bool supportSmallScreen(byte[] dpi)
{
if(dpi[0] == 1) return true;
return false;
}
public static bool supportNormalScreen(byte[] dpi)
{
if(dpi[1] == 1) return true;
return false;
}
public static bool supportLargeScreen(byte[] dpi)
{
if(dpi[2] == 1) return true;
return false;
}
//public byte[] getDPI()
//{
// byte[] dpi = new byte[3];
// if (this.supportAnyDensity)
// {
// dpi[0] = 1;
// dpi[1] = 1;
// dpi[2] = 1;
// }
// else
// {
// if (this.supportSmallScreens)
// dpi[0] = 1;
// if (this.supportNormalScreens)
// dpi[1] = 1;
// if (this.supportLargeScreens)
// dpi[2] = 1;
// }
// return dpi;
//}
public ApkInfo()
{
hasIcon = false;
supportSmallScreens = false;
supportNormalScreens = false;
supportLargeScreens = false;
supportAnyDensity = true;
versionCode = null;
versionName = null;
iconFileName = null;
iconFileNameToGet = null;
Permissions = new List < String > ();
}
private bool isReference(List < String > strs)
{
try
{
foreach(String str in strs)
{
if(isReference(str)) return true;
}
}
catch(Exception e)
{
throw e;
}
return false;
}
private bool isReference(String str)
{
try
{
if(str != null && str.StartsWith("@"))
{
int.Parse(str, System.Globalization.NumberStyles.HexNumber);
return true;
}
}
catch(Exception e)
{
throw e;
}
return false;
}
}

以上就是.net core 从(本地)服务器获取APK文件并解析APK信息的介绍,做此记录,如有帮助,欢迎点赞关注收藏!


评论
  目录