XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

STHApplication

【MAUI】寄当家——安卓端交易管理系统

公开
关注 0 Fork 0 Star 0
UTF-8
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 缺少应用清单");
    }
}