转载请注明出处:https://blog.csdn.net/xiangxinzijiwonen/article/details/83547250(来自ZhongGuanYao的csdn博客)
目录
前言
从事游戏SDK接入和打包相关工作时,经常对要APK文件进行信息检查、打包测试、问题排查等等,难免要敲一些反编译、重签名等繁琐的命令行,为方便和提高工作效率,制作一款适合自己工作需要的APKINFO小工具是有必要的。
APKINFO工具下载:https://download.csdn.net/download/xiangxinzijiwonen/10766262
github源码:https://github.com/zhongguanyao/APKINFO
注意:无法在CSDN资源下载时,可到github下载源码工程,直接使用bin/Debug目录即可
先展示张图片看下效果:
使用语言:C#
涉及到的工具:aapt.exe、jarsigner.exe、zipalign.exe、apktool、java.exe、keytool.exe,第三方jadx等
APKINFO的功能是通过C#语言对这些工具的命令行进一步封装来实现的,你会发现APKINFO工具为什么有一百多M那么大,是因为它引用了JDK和ADT的部分工具。
项目结构如下:
一、查看APK信息
工具:aapt.exe
命令行:aapt dump badging apkFilePath
示例截图:
编程思路:C#调用命令行后获得APK信息,然后将应用名、包名、版本号、图标、权限等信息整理出来,最后展示到窗体的控件上。
注意点:
- C#在调用命令行时,需要设置使用Shell执行,输出重定向,可以屏蔽Shell窗体的显示。
- 截图上的中文应用名出现乱码,是因为Shell输出编码默认使用了gb2312,将标准输出编码设置为UTF-8即可。
- 不同版本的aapt.exe,查看APK信息的速度不一样,输出来的信息结构也稍有不同,一般低版本的aapt执行速度比较快。
- 图标读取是先在APK信息中找到图标路径,然后根据路径解压图标文件的,项目中是引入Ionic.Zip.dll解压文件。
- 另外,增加了查看APK的assets目录下的配置,至于查看哪些个配置文件里的参数,由用户设置决定,这里只能读取内容格式为key=value的配置文件。
C#关键代码如下:
BrowseApkInfoBLL类:
/// <summary>
/// 读取APK信息
/// </summary>
/// <param name="apkFilePath"></param>
/// <returns></returns>
public static ApkInfo ReadApkInfo(string apkFilePath)
{
if (String.IsNullOrEmpty(apkFilePath))
return null;
string aaptPath = PathUtils.GetPath(Constants.PATH_AAPT);
var startInfo = new ProcessStartInfo(aaptPath);
string args = string.Format("dump badging \"{0}\"", apkFilePath);
startInfo.Arguments = args;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = true;
startInfo.StandardOutputEncoding = Encoding.UTF8;
// 读取命令行返回的所有txt文本和字符编码
List<string> lineLst = new List<string>();
using (var process = Process.Start(startInfo))
{
var sr = process.StandardOutput;
while (!sr.EndOfStream)
{
lineLst.Add(sr.ReadLine());
}
process.WaitForExit();
}
ApkInfo apkInfo = new ApkInfo();
apkInfo.ApkFilePath = apkFilePath;
apkInfo.CurrentFileName = Path.GetFileName(apkFilePath);
apkInfo.AllInfoList = lineLst;
// 解析相关的配置参数
ParseTxtFile(ref apkInfo);
ParseIcon(ref apkInfo);
ParseAssetsConfig(ref apkInfo);
return apkInfo;
}
/// <summary>
/// 解压并读取icon图片
/// </summary>
/// <param name="apkInfo"></param>
private static void ParseIcon(ref ApkInfo apkInfo)
{
if (apkInfo == null
|| string.IsNullOrEmpty(apkInfo.ApkFilePath)
|| apkInfo.IconDic == null
|| apkInfo.IconDic.Count <= 0) return;
string icon = null;
int i = 0;
// 在多张尺寸的ICON里选择尺寸为240
foreach (KeyValuePair<string, string> kv in apkInfo.IconDic)
{
if (kv.Key.Contains("240"))
{
icon = kv.Value;
apkInfo.IconName = Path.GetFileName(icon);
break;
}
if (i == apkInfo.IconDic.Count - 1)
{
icon = kv.Value;
apkInfo.IconName = Path.GetFileName(icon);
break;
}
i++;
}
// 指定解压目录
string unZipPath = PathUtils.GetPath(Constants.DIR_TEMP_ICON);
// 清空解压目录
FileUtils.ClearDir(unZipPath);
// 解压指定的icon图片文件
ZipUtils.UnZip(apkInfo.ApkFilePath, unZipPath, icon);
// 读取解压出来的图片文件
string iconPath = PathUtils.JoinPath(unZipPath, icon);
apkInfo.Icon = new Bitmap(iconPath);
}
二、查看APK源代码
工具:jadx-0.7.1
命令行:jadx-gui.bat apkFilePath 或jarFilePath 或dexFilePath
之前使用过jd-gui.exe查看源码,但它只能打开jar文件,dex和apk文件需要转换成jar文件才能打开,并不好用;这里推荐使用jadx工具,jar、dex和apk文件它都能直接打开。
编程思路:程序判断用户传递的文件类型,如果为jar或dex文件,则直接通过命令行调用jadx-0.7.1打开,如果为apk文件,则展示APK信息,让用户通过点击《查看源码》按钮打开,当jadx-0.7.1打开后,关闭程序。
C#关键代码如下:
BrowseSourceBLL类:
/// <summary>
/// 用jadx-gui.bat打开文件
/// </summary>
/// <param name="filePath">以.jar/.dex/.apk为后缀的文件路径</param>
public static void OpenJadxGUI(string filePath) {
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) return;
string jadxPath = PathUtils.GetPath(Constants.PATH_JADXGUI);
if (!File.Exists(jadxPath))
{
LogUtils.WriteLine(jadxPath + "不存在!");
return;
}
string cmdArgs = jadxPath + " " + filePath;
CmdUtils.ExecCmd(cmdArgs);
}
CmdUtils类:
/// <summary>
/// 执行CMD命令,但不等待执行结果
/// </summary>
/// <param name="args"></param>
public static void ExecCmd(string args)
{
try
{
Process proc = new Process();
proc.StartInfo.WorkingDirectory = System.Windows.Forms.Application.StartupPath;
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.Arguments = "/C " + args;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.CreateNoWindow = true;//不创建窗口
proc.Start();
}