commit a6efc648960067826ce90a711bd2884ce4c62742 Author: MeowCracker Date: Fri Apr 4 18:12:23 2025 +0800 feat: 完成了基于 FastAPI 的重制,重新写了 web 页面 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5e96db --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +venv \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e05c8d5 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# MobaGenkey + +你应该知道这是干啥的 + +## 开始使用 + +先安装环境 + +```shell +$ pip install -r requirements.txt +``` + +然后直接运行 + +```shell +$ python app.py +``` + +打开浏览器访问控制台展示的地址(例如[http://127.0.0.1:8000](http://127.0.0.1:8000)),按照网页的指示自己玩 \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..1390923 --- /dev/null +++ b/app.py @@ -0,0 +1,155 @@ +from fastapi import FastAPI, HTTPException, Query +from fastapi.responses import FileResponse, StreamingResponse, HTMLResponse +import os +import zipfile +from io import BytesIO + +app = FastAPI() + +VariantBase64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" +VariantBase64Dict = {i: VariantBase64Table[i] for i in range(len(VariantBase64Table))} +VariantBase64ReverseDict = { + VariantBase64Table[i]: i for i in range(len(VariantBase64Table)) +} + + +def VariantBase64Encode(bs: bytes): + result = b"" + blocks_count, left_bytes = divmod(len(bs), 3) + + for i in range(blocks_count): + coding_int = int.from_bytes(bs[3 * i : 3 * i + 3], "little") + block = VariantBase64Dict[coding_int & 0x3F] + block += VariantBase64Dict[(coding_int >> 6) & 0x3F] + block += VariantBase64Dict[(coding_int >> 12) & 0x3F] + block += VariantBase64Dict[(coding_int >> 18) & 0x3F] + result += block.encode() + + if left_bytes == 0: + return result + elif left_bytes == 1: + coding_int = int.from_bytes(bs[3 * blocks_count :], "little") + block = VariantBase64Dict[coding_int & 0x3F] + block += VariantBase64Dict[(coding_int >> 6) & 0x3F] + result += block.encode() + return result + else: + coding_int = int.from_bytes(bs[3 * blocks_count :], "little") + block = VariantBase64Dict[coding_int & 0x3F] + block += VariantBase64Dict[(coding_int >> 6) & 0x3F] + block += VariantBase64Dict[(coding_int >> 12) & 0x3F] + result += block.encode() + return result + + +def VariantBase64Decode(s: str): + result = b"" + blocks_count, left_bytes = divmod(len(s), 4) + + for i in range(blocks_count): + block = VariantBase64ReverseDict[s[4 * i]] + block += VariantBase64ReverseDict[s[4 * i + 1]] << 6 + block += VariantBase64ReverseDict[s[4 * i + 2]] << 12 + block += VariantBase64ReverseDict[s[4 * i + 3]] << 18 + result += block.to_bytes(3, "little") + + if left_bytes == 0: + return result + elif left_bytes == 2: + block = VariantBase64ReverseDict[s[4 * blocks_count]] + block += VariantBase64ReverseDict[s[4 * blocks_count + 1]] << 6 + result += block.to_bytes(1, "little") + return result + elif left_bytes == 3: + block = VariantBase64ReverseDict[s[4 * blocks_count]] + block += VariantBase64ReverseDict[s[4 * blocks_count + 1]] << 6 + block += VariantBase64ReverseDict[s[4 * blocks_count + 2]] << 12 + result += block.to_bytes(2, "little") + return result + else: + raise ValueError("Invalid encoding.") + + +def EncryptBytes(key: int, bs: bytes): + result = bytearray() + for i in range(len(bs)): + result.append(bs[i] ^ ((key >> 8) & 0xFF)) + key = result[-1] & key | 0x482D + return bytes(result) + + +def DecryptBytes(key: int, bs: bytes): + result = bytearray() + for i in range(len(bs)): + result.append(bs[i] ^ ((key >> 8) & 0xFF)) + key = bs[i] & key | 0x482D + return bytes(result) + + +class LicenseType: + Professional = 1 + Educational = 3 + Persional = 4 + + +class LicenseType: + Professional = 1 + Educational = 3 + Persional = 4 + + +def generate_license_bytes( + Type: LicenseType, Count: int, UserName: str, MajorVersion: int, MinorVersion: int +): + assert Count >= 0 + LicenseString = f"{Type}#{UserName}|{MajorVersion}{MinorVersion}#{Count}#{MajorVersion}3{MinorVersion}6{MinorVersion}#0#0#0#" + EncodedLicenseString = VariantBase64Encode( + EncryptBytes(0x787, LicenseString.encode()) + ).decode() + FileName = EncodedLicenseString.replace("/", "").replace("\\", "") + + # 使用内存文件 + zip_buffer = BytesIO() + with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file: + zip_file.writestr("Pro.key", EncodedLicenseString) + + zip_buffer.seek(0) + return (FileName, zip_buffer) + + +@app.get("/api/generate") +async def generate_license( + name: str = Query(..., min_length=1), + ver: str = Query(..., regex=r"^\d+\.\d+$"), + count: int = Query(1, ge=1), +): + try: + MajorVersion, MinorVersion = map(int, ver.split(".")[:2]) + except ValueError: + raise HTTPException(status_code=400, detail="Invalid version format") + + try: + filename, file_data = generate_license_bytes( + LicenseType.Professional, count, name, MajorVersion, MinorVersion + ) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + return StreamingResponse( + content=file_data, + media_type="application/zip", + headers={ + "Content-Disposition": f"attachment; filename=Custom.mxtpro", + "Content-Type": "application/zip", + }, + ) + + +@app.get("/", response_class=HTMLResponse) +async def index(): + return FileResponse("templates/index.html") + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d1f80a3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +uvicorn +fastapi \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..1ce19ea --- /dev/null +++ b/templates/index.html @@ -0,0 +1,155 @@ + + + + + + + License Generator + + + + + + + + + + + +
MobaXterm Key Generator
+
+ + +
+ +
欢迎使用许可证生成器
+
+

请填写以下信息以生成许可证。

+

注意:生成的许可证仅供学习和测试使用,请勿用于商业目的。

+ https://github.com/MeowCracker/MobaGenkey + + + + + +
+ 生成并保存 +
+
+
+
+
+
+
+ + + + \ No newline at end of file