.net core 从(本地)服务器获取APK文件并解析APK信息
1、apk解析除了使用客户端利用aapt.exe、unzip.exe开发客户端解析外,还可以直接利用服务进行解析
/// <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解析帮助类
/// <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

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解析返回类
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信息的介绍,做此记录,如有帮助,欢迎点赞关注收藏!
.net core 从(本地)服务器获取APK文件并解析APK信息的更多相关文章
- [Android Pro] 查看 keystore文件的签名信息 和 检查apk文件中的签名信息
1: 查看 keystore文件的签名信息 keytool -list -v -keystore keystoreName -storepass keystorePassword 2: 检查apk文件 ...
- sublime text3在指定浏览器上本地服务器(localhost)运行文件(php)
昨天在使用sublime text3时,发现能在本地服务器上运行php文件,于是百度了一下有关知识, 终于成功了,今天总结一下. 首先要让sublime text3 出现侧边栏sidebar,不会的可 ...
- Ajax——从服务器获取各种文件
ajax.js内容 function ajax(url,fnWin,fnFaild){ //1.创建ajax对象 var xhr = window.XMLHttpRequest ? new XMLHt ...
- php自定义函数: 下载本地服务器的大文件
// 使用方法 $file_path = './a.zip'; // 只能是本地服务器文件, 多大的文件都支持!! down_file($file_path); // 函数参数: 服务器文件路径,下载 ...
- [转]sublime text3在指定浏览器上本地服务器(localhost)运行文件(php)
昨天在使用sublime text3时,发现能在本地服务器上运行php文件,于是百度了一下有关知识, 终于成功了,今天总结一下. 首先要让sublime text3 出现侧边栏sidebar,不会的可 ...
- 使用node建立本地服务器访问静态文件
最终目录结构 demo │ node_modules └───public │ │ index.html │ │ index.css │ └───index.js └───server.js 一.使用 ...
- sublime text3:sublime text3本地服务器方式运行文件
网址:https://blog.csdn.net/md1688/article/details/70562381 1.Ctrl + Shift +P,启动Sublime Text的命令行(如果没有需要 ...
- java从远程服务器获取PDF文件并后台打印(使用pdfFox)
一.java原生方式打印PDF文件 正反面都打印,还未研究出只打印单面的方法,待解决 public static void printFile(String path) throws Exceptio ...
- Android从网络中获取xml文件并解析数据
public class XmlwebData { @SuppressLint("UseValueOf") public static List<Person> get ...
- iOS - 音乐播放器需要获取音乐文件的一些数据信息(封装获取封面图片的类)
// // AVMetadataInfo.h // AVMetadata // // Created by Wengrp on 15/10/27. // Copyright © 2015年 Wengr ...
随机推荐
- 【云原生 · DevOps】DevOps 解决方案
DevOps 解决方案 1.1 容器化 CI/CD 1.2 容器化流水线 1.3 深度集成 Jenkins 1.4 灰度发布 1.5 制品库设计 1.6 DevOps 安全 1.6.1 CI/CD 安 ...
- 关于Linux pyinstaller打包zmq.h报错
报错信息 6:10: fatal error: zmq.h: No such file or directory #include <zmq.h> ^~~~~~~ compilation ...
- day32 6 请求转发与重定向的区别、session会话对象 & cookie & 8 应用程序上下文对象ServletContext & 5 请求转发与jsp页面内置对象
1 请求转发与重定向的区别 2 session与cookie的区别 3 过滤器与监听器的区别 4 web-inf目录 web-inf目录是安全目录,无法从客户端访问,只能通过(服务端的)servlet ...
- 【每日一题】【小根堆&边出队边入队后续节点&注意判空】23. 合并K个升序链表-211128/220213
给你一个链表数组,每个链表都已经按升序排列. 请你将所有链表合并到一个升序链表中,返回合并后的链表. 答案1(参数是数组): /** * Definition for singly-linked li ...
- 配置文件 数据库存储引擎 严格模式 MySQL字段基本数据类型
目录 字符编码与配置文件 \s查看MySQL相关信息 修改配置文件my-default.ini 解决5.6版本字符编码问题 配置文件什么时候加载? 偷懒操作:输入mysql直接登录root账户 数据库 ...
- vue 项目引入 echarts折线图
一.components文件下新建 lineCharts.vue <template> <div :class="className" :style=" ...
- 前端h5适配刘海屏和滴水屏
前端适配苹果刘海屏,安卓刘海屏水滴瓶 其实w3c早就为我们提供了解决方法(CSS3新特性viewport-fit) 在w3c.org官方给出的关于圆形展示(Round display)的标准中, 提到 ...
- NOIP-2022游寄
NOIP-2022游寄 Day 1 虽然没有上次去南京CSP-S那么激动,但还是有点小开心的,毕竟能水掉两天课.Phigros重度沉迷患者,推了4个小时的分.坐右前方那哥们好卷,在车上写图论-- JS ...
- vivo 游戏中心低代码平台的提效秘诀
作者:vivo 互联网服务器团队- Chen Wenyang 本文根据陈文洋老师在"2022 vivo开发者大会"现场演讲内容整理而成.公众号回复[2022 VDC]获取互联网技术 ...
- Python实验报告(第9章)
实验9:异常处理及程序调试 一.实验目的和要求 1.了解代码异常知识: 2.掌握异常处理的try-except语句.try-except-else语句.try-except-finally语句.rai ...