using System.IO.Compression;
using System.Text;
namespace JiDangJia.Server;
// Reads Android's binary XML without extracting or executing uploaded files.
internal sealed record ApkManifest(string PackageId,int VersionCode,string VersionName)
{
public static ApkManifest Read(string path)
{
using var zip=ZipFile.OpenRead(path);var entry=zip.GetEntry("AndroidManifest.xml")??throw new ArgumentException("请选择完整的 APK 安装包");
if(entry.Length is <8 or >2097152||zip.GetEntry("classes.dex") is null)throw new ArgumentException("APK 内容无效");
using var stream=entry.Open();using var buffer=new MemoryStream();var block=new byte[8192];int read;while((read=stream.Read(block))>0){if(buffer.Length+read>2097152)throw new InvalidDataException();buffer.Write(block,0,read);}byte[] bytes=buffer.ToArray();
int U16(int p){if(p<0||p>bytes.Length-2)throw new InvalidDataException();return BitConverter.ToUInt16(bytes,p);}
uint U32(int p){if(p<0||p>bytes.Length-4)throw new InvalidDataException();return BitConverter.ToUInt32(bytes,p);}
var strings=new List<string>();long decodedSize=0;string S(uint index)=>index<strings.Count?strings[(int)index]:"";
if(U16(0)!=3||U32(4)!=bytes.Length)throw new ArgumentException("APK 清单格式无效");
for(int p=U16(2);p<bytes.Length;)
{
int type=U16(p),header=U16(p+2),size=checked((int)U32(p+4));if(header<8||size<header||size>bytes.Length-p)throw new InvalidDataException("APK 清单损坏");
if(type==1)
{
int count=checked((int)U32(p+8)),start=checked((int)U32(p+20));bool utf8=(U32(p+16)&256)!=0;
if(count<0||count>100000||header+4L*count>size)throw new InvalidDataException();
for(int i=0;i<count;i++)
{
int q=checked(p+start+(int)U32(p+header+i*4));if(q<p+header||q>=p+size)throw new InvalidDataException();int length;
if(utf8)
{
int Len(){if(q>=p+size)throw new InvalidDataException();int n=bytes[q++];if((n&128)!=0){if(q>=p+size)throw new InvalidDataException();n=((n&127)<<8)|bytes[q++];}return n;}
Len();length=Len();decodedSize+=length;if(length>p+size-q||decodedSize>4194304)throw new InvalidDataException();strings.Add(Encoding.UTF8.GetString(bytes,q,length));
}
else
{length=U16(q);q+=2;if((length&32768)!=0){length=((length&32767)<<16)|U16(q);q+=2;}int n=checked(length*2);decodedSize+=n;if(n>p+size-q||decodedSize>4194304)throw new InvalidDataException();strings.Add(Encoding.Unicode.GetString(bytes,q,n));}
}
}
else if(type==0x102&&S(U32(p+20))=="manifest")
{
int attr=p+16+U16(p+24),stride=U16(p+26),count=U16(p+28);if(stride<20||attr+count*(long)stride>p+size)throw new InvalidDataException();
string package="",name="";int version=0;
for(int i=0;i<count;i++)
{
int a=attr+i*stride;string key=S(U32(a+4));uint data=U32(a+16);string value=U32(a+8)!=uint.MaxValue?S(U32(a+8)):bytes[a+15]==3?S(data):data.ToString(System.Globalization.CultureInfo.InvariantCulture);
switch(key){case "package":package=value;break;case "versionCode":if(!int.TryParse(value,out version))throw new ArgumentException("APK 版本号无效");break;case "versionName":name=value;break;}
}
if(package.Length is <3 or >200||version<=0||name.Length is <1 or >80)throw new ArgumentException("APK 缺少应用或版本信息");return new(package,version,name);
}
p+=size;
}
throw new ArgumentException("APK 缺少应用清单");
}
}
using System.IO.Compression;
using System.Text;
namespace JiDangJia.Server;
// Reads Android's binary XML without extracting or executing uploaded files.
internal sealed record ApkManifest(string PackageId,int VersionCode,string VersionName)
{
public static ApkManifest Read(string path)
{
using var zip=ZipFile.OpenRead(path);var entry=zip.GetEntry("AndroidManifest.xml")??throw new ArgumentException("请选择完整的 APK 安装包");
if(entry.Length is <8 or >2097152||zip.GetEntry("classes.dex") is null)throw new ArgumentException("APK 内容无效");
using var stream=entry.Open();using var buffer=new MemoryStream();var block=new byte[8192];int read;while((read=stream.Read(block))>0){if(buffer.Length+read>2097152)throw new InvalidDataException();buffer.Write(block,0,read);}byte[] bytes=buffer.ToArray();
int U16(int p){if(p<0||p>bytes.Length-2)throw new InvalidDataException();return BitConverter.ToUInt16(bytes,p);}
uint U32(int p){if(p<0||p>bytes.Length-4)throw new InvalidDataException();return BitConverter.ToUInt32(bytes,p);}
var strings=new List<string>();long decodedSize=0;string S(uint index)=>index<strings.Count?strings[(int)index]:"";
if(U16(0)!=3||U32(4)!=bytes.Length)throw new ArgumentException("APK 清单格式无效");
for(int p=U16(2);p<bytes.Length;)
{
int type=U16(p),header=U16(p+2),size=checked((int)U32(p+4));if(header<8||size<header||size>bytes.Length-p)throw new InvalidDataException("APK 清单损坏");
if(type==1)
{
int count=checked((int)U32(p+8)),start=checked((int)U32(p+20));bool utf8=(U32(p+16)&256)!=0;
if(count<0||count>100000||header+4L*count>size)throw new InvalidDataException();
for(int i=0;i<count;i++)
{
int q=checked(p+start+(int)U32(p+header+i*4));if(q<p+header||q>=p+size)throw new InvalidDataException();int length;
if(utf8)
{
int Len(){if(q>=p+size)throw new InvalidDataException();int n=bytes[q++];if((n&128)!=0){if(q>=p+size)throw new InvalidDataException();n=((n&127)<<8)|bytes[q++];}return n;}
Len();length=Len();decodedSize+=length;if(length>p+size-q||decodedSize>4194304)throw new InvalidDataException();strings.Add(Encoding.UTF8.GetString(bytes,q,length));
}
else
{length=U16(q);q+=2;if((length&32768)!=0){length=((length&32767)<<16)|U16(q);q+=2;}int n=checked(length*2);decodedSize+=n;if(n>p+size-q||decodedSize>4194304)throw new InvalidDataException();strings.Add(Encoding.Unicode.GetString(bytes,q,n));}
}
}
else if(type==0x102&&S(U32(p+20))=="manifest")
{
int attr=p+16+U16(p+24),stride=U16(p+26),count=U16(p+28);if(stride<20||attr+count*(long)stride>p+size)throw new InvalidDataException();
string package="",name="";int version=0;
for(int i=0;i<count;i++)
{
int a=attr+i*stride;string key=S(U32(a+4));uint data=U32(a+16);string value=U32(a+8)!=uint.MaxValue?S(U32(a+8)):bytes[a+15]==3?S(data):data.ToString(System.Globalization.CultureInfo.InvariantCulture);
switch(key){case "package":package=value;break;case "versionCode":if(!int.TryParse(value,out version))throw new ArgumentException("APK 版本号无效");break;case "versionName":name=value;break;}
}
if(package.Length is <3 or >200||version<=0||name.Length is <1 or >80)throw new ArgumentException("APK 缺少应用或版本信息");return new(package,version,name);
}
p+=size;
}
throw new ArgumentException("APK 缺少应用清单");
}
}