remove axinom res
This commit is contained in:
parent
1a6afdabd4
commit
104d00b23c
|
@ -1,47 +0,0 @@
|
||||||
.method private isNetworkAvailable()Z
|
|
||||||
.registers 2
|
|
||||||
|
|
||||||
# const-string v0, "connectivity"
|
|
||||||
|
|
||||||
# .line 139
|
|
||||||
# invoke-virtual {p0, v0}, Lcom/axinom/drm/sample/activity/SampleChooserActivity;->getSystemService(Ljava/lang/String;)Ljava/lang/Object;
|
|
||||||
|
|
||||||
# move-result-object v0
|
|
||||||
|
|
||||||
# check-cast v0, Landroid/net/ConnectivityManager;
|
|
||||||
|
|
||||||
# if-eqz v0, :cond_f
|
|
||||||
|
|
||||||
# .line 142
|
|
||||||
# invoke-virtual {v0}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
|
|
||||||
|
|
||||||
# move-result-object v0
|
|
||||||
|
|
||||||
# goto :goto_10
|
|
||||||
|
|
||||||
# :cond_f
|
|
||||||
# const/4 v0, 0x0
|
|
||||||
|
|
||||||
# :goto_10
|
|
||||||
# if-eqz v0, :cond_1a
|
|
||||||
|
|
||||||
# .line 144
|
|
||||||
# invoke-virtual {v0}, Landroid/net/NetworkInfo;->isConnected()Z
|
|
||||||
|
|
||||||
# move-result v0
|
|
||||||
|
|
||||||
# if-eqz v0, :cond_1a
|
|
||||||
|
|
||||||
# const/4 v0, 0x1
|
|
||||||
|
|
||||||
# goto :goto_1b
|
|
||||||
|
|
||||||
# :cond_1a
|
|
||||||
# const/4 v0, 0x0
|
|
||||||
|
|
||||||
# :goto_1b
|
|
||||||
|
|
||||||
const/4 v0, 0x1
|
|
||||||
|
|
||||||
return v0
|
|
||||||
.end method
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,47 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
# @Info: values to be patch
|
|
||||||
PATCH = {
|
|
||||||
"assets/samplelist.json": [
|
|
||||||
[None, "samplelist.json"]
|
|
||||||
],
|
|
||||||
"com/axinom/drm/sample/activity/SampleChooserActivity.smali": [
|
|
||||||
["SampleChooserActivity.smali", None]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
# @Info: Keystore to sign the application
|
|
||||||
KEYSTORE = {
|
|
||||||
"algo": "RSA",
|
|
||||||
"size": 2048,
|
|
||||||
"sign": "SHA-256",
|
|
||||||
"validity": 365 * 25,
|
|
||||||
"password": "Axinom_PASSWORD",
|
|
||||||
"alias": "Axinom_DRM_DEMO",
|
|
||||||
"meta": {
|
|
||||||
"common_name": "Axinom",
|
|
||||||
"organizational_unit": "Front-End",
|
|
||||||
"organization": "Axinom",
|
|
||||||
"locality": "Tartu",
|
|
||||||
"state": "Tartumaa",
|
|
||||||
"country": "EE",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# @Info: Info about application
|
|
||||||
METADATA = {
|
|
||||||
# "name": "Axinom DRM Sample Player",
|
|
||||||
"version": "202211021",
|
|
||||||
"source": "https://github.com/Axinom/drm-sample-player-android",
|
|
||||||
"input": "axinom.apk",
|
|
||||||
"output": "axinom_signed.apk",
|
|
||||||
}
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
Path("config.yaml").write_text(yaml.dump({
|
|
||||||
"metadata": METADATA,
|
|
||||||
"keystore": KEYSTORE,
|
|
||||||
"patch": PATCH
|
|
||||||
}))
|
|
|
@ -1,26 +0,0 @@
|
||||||
keystore:
|
|
||||||
algo: RSA
|
|
||||||
alias: Axinom_DRM_DEMO
|
|
||||||
meta:
|
|
||||||
common_name: Axinom
|
|
||||||
country: EE
|
|
||||||
locality: Tartu
|
|
||||||
organization: Axinom
|
|
||||||
organizational_unit: Front-End
|
|
||||||
state: Tartumaa
|
|
||||||
password: Axinom_PASSWORD
|
|
||||||
sign: SHA-256
|
|
||||||
size: 2048
|
|
||||||
validity: 9125
|
|
||||||
metadata:
|
|
||||||
input: axinom.apk
|
|
||||||
output: axinom_signed.apk
|
|
||||||
source: https://github.com/Axinom/drm-sample-player-android
|
|
||||||
version: '202211021'
|
|
||||||
patch:
|
|
||||||
assets/samplelist.json:
|
|
||||||
- - null
|
|
||||||
- samplelist.json
|
|
||||||
com/axinom/drm/sample/activity/SampleChooserActivity.smali:
|
|
||||||
- - SampleChooserActivity.smali
|
|
||||||
- null
|
|
|
@ -1,260 +0,0 @@
|
||||||
import hashlib
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import shutil
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import xmltodict
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
|
|
||||||
def any2str(data: any) -> str:
|
|
||||||
if isinstance(data, (bytes, bytearray)):
|
|
||||||
data = data.decode("utf-8")
|
|
||||||
|
|
||||||
if isinstance(data, (dict, list)):
|
|
||||||
data = json.dumps(data, indent=2, separators=(",", ":"))
|
|
||||||
|
|
||||||
return str(data)
|
|
||||||
|
|
||||||
|
|
||||||
class Keystore:
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
algo: str = "RSA",
|
|
||||||
size: int = 2048,
|
|
||||||
sign: str = "SHA-256",
|
|
||||||
validity: int = 365,
|
|
||||||
password: str = None,
|
|
||||||
alias: str = None,
|
|
||||||
meta: dict = None,
|
|
||||||
path: Path = Path("..")
|
|
||||||
):
|
|
||||||
assert path.is_dir(), "Invalid Dir Path"
|
|
||||||
assert algo in ["RSA", "EC", "DSA"], "Invalid Algorithm"
|
|
||||||
assert sign in ["MD5", "SHA-1", "SHA-256", "SHA-512"], "Invalid Signature"
|
|
||||||
|
|
||||||
if algo == "RSA":
|
|
||||||
assert size in [1024, 2048, 3072, 4096], "Invalid RSA Size"
|
|
||||||
assert sign in ["MD5", "SHA-1", "SHA-256", "SHA-512"], "Invalid RSA Signature"
|
|
||||||
elif algo == "EC":
|
|
||||||
assert size in [192, 224, 256, 384, 521], "Invalid EC Size"
|
|
||||||
assert sign in ["SHA-256", "SHA-512"], "Invalid EC Signature"
|
|
||||||
elif algo == "DSA":
|
|
||||||
assert size in [1024], "Invalid DSA Size"
|
|
||||||
assert sign in ["SHA-1"], "Invalid DSA Signature"
|
|
||||||
|
|
||||||
self.algorithm = algo
|
|
||||||
self.size = size
|
|
||||||
self.signature = "{}with{}".format(
|
|
||||||
sign.replace("-", ""),
|
|
||||||
"ECDSA" if algo == "EC" else algo
|
|
||||||
)
|
|
||||||
self.digest = sign
|
|
||||||
self.validity = validity
|
|
||||||
meta = meta if meta else {}
|
|
||||||
self.metadata = {
|
|
||||||
"common_name": meta.get("common_name", "Unknown"),
|
|
||||||
"organizational_unit": meta.get("organizational_unit", "Unknown"),
|
|
||||||
"organization": meta.get("organization", "Unknown"),
|
|
||||||
"locality": meta.get("locality", "Unknown"),
|
|
||||||
"state": meta.get("state", "Unknown"),
|
|
||||||
"country": meta.get("country", "Unknown"),
|
|
||||||
}
|
|
||||||
|
|
||||||
match = re.search(r'[\s:]?([a-zA-Z]+)', self.metadata["common_name"])
|
|
||||||
name = re.sub(r'[^A-Za-z0-9]', "", match.group(1)).lower() if match else "keystore"
|
|
||||||
self.path = path / f"{name}_{algo.lower()}.p12"
|
|
||||||
self.password = password or f"{name}_password"
|
|
||||||
self.alias = alias or f"{name}_alias"
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return json.dumps({
|
|
||||||
"path": str(self.path),
|
|
||||||
"algorithm": self.algorithm,
|
|
||||||
"size": self.size,
|
|
||||||
"signature": self.signature,
|
|
||||||
"digest": self.digest,
|
|
||||||
"validity": self.validity,
|
|
||||||
"password": self.password,
|
|
||||||
"alias": self.alias,
|
|
||||||
"metadata": self.metadata
|
|
||||||
}, indent=2)
|
|
||||||
|
|
||||||
def sign(self, path: Path) -> None:
|
|
||||||
assert path.is_file() and path.suffix == ".apk", "Invalid APK Path"
|
|
||||||
|
|
||||||
if not self.path.is_file():
|
|
||||||
tmp = Path("keystore.jks")
|
|
||||||
os.system(
|
|
||||||
'keytool -genkeypair -keystore "{}" -alias "{}" -keyalg "{}" -keysize "{}" -sigalg "{}" -validity "{}" -storepass "{}" -keypass "{}" -dname "CN=\\"{}\\", OU=\\"{}\\", O=\\"{}\\", L=\\"{}\\", ST=\\"{}\\", C=\\"{}\\"" -noprompt'.format(
|
|
||||||
tmp, self.alias, self.algorithm, self.size, self.signature,
|
|
||||||
self.validity, self.password, self.password, self.metadata["common_name"],
|
|
||||||
self.metadata["organizational_unit"], self.metadata["organization"],
|
|
||||||
self.metadata["locality"], self.metadata["state"], self.metadata["country"]
|
|
||||||
))
|
|
||||||
|
|
||||||
os.system(
|
|
||||||
'keytool -importkeystore -srckeystore "{}" -srcstorepass "{}" -destkeystore "{}" -deststoretype "PKCS12" -deststorepass "{}" -destkeypass "{}" -srcalias "{}"'.format(
|
|
||||||
tmp, self.password, self.path, self.password, self.password, self.alias
|
|
||||||
))
|
|
||||||
tmp.unlink(missing_ok=True)
|
|
||||||
os.system('apksigner sign --ks "{}" --ks-key-alias "{}" --ks-pass "pass:{}" --key-pass "pass:{}" "{}"'.format(
|
|
||||||
self.path, self.alias, self.password, self.password, path
|
|
||||||
))
|
|
||||||
Path(str(path) + ".idsig").unlink(missing_ok=True)
|
|
||||||
|
|
||||||
def info(self, path: Path) -> None:
|
|
||||||
assert path.is_file() and path.suffix == ".apk", "Invalid APK Path"
|
|
||||||
os.system(f'apksigner verify --print-certs "{path}"')
|
|
||||||
|
|
||||||
|
|
||||||
class ApkTool:
|
|
||||||
|
|
||||||
def __init__(self, instance: Path = Path(".apktool")):
|
|
||||||
self.instance = instance
|
|
||||||
|
|
||||||
def decompile(self, path: Path) -> None:
|
|
||||||
assert path.is_file() and path.suffix == ".apk", "Invalid APK Path"
|
|
||||||
if not self.instance.is_dir():
|
|
||||||
os.system(f'apktool d "{path}" -o "{self.instance}" -f --no-crunch --only-main-classes')
|
|
||||||
|
|
||||||
def compile(self, path: Path) -> None:
|
|
||||||
assert path.suffix == ".apk", "Invalid APK Path"
|
|
||||||
if not path.is_file():
|
|
||||||
assert self.instance.is_dir(), "Invalid ApkTool Path"
|
|
||||||
tmp = Path("unaligned.apk")
|
|
||||||
|
|
||||||
os.system(f'apktool b "{self.instance}" -o "{tmp}" -f --no-crunch')
|
|
||||||
if tmp.is_file(): os.system(f'zipalign -f -p "4" "{tmp}" "{path}"')
|
|
||||||
if path.is_file(): shutil.rmtree(self.instance, ignore_errors=True)
|
|
||||||
tmp.unlink(missing_ok=True)
|
|
||||||
|
|
||||||
|
|
||||||
def rename_app(parent: Path, name: str) -> None:
|
|
||||||
manifest_path = parent / "AndroidManifest.xml"
|
|
||||||
if not manifest_path.is_file():
|
|
||||||
raise FileNotFoundError(manifest_path)
|
|
||||||
|
|
||||||
manifest_dict = xmltodict.parse(manifest_path.read_bytes(), encoding="utf-8")
|
|
||||||
value = str(manifest_dict["manifest"]["application"]["@android:label"])
|
|
||||||
if value.startswith("@string/"):
|
|
||||||
key = value.split("@string/")[1]
|
|
||||||
|
|
||||||
source = None
|
|
||||||
for path in (parent / "res").iterdir():
|
|
||||||
strings_path = path / "strings.xml"
|
|
||||||
if "values" in str(path) and strings_path.is_file():
|
|
||||||
strings_dict = xmltodict.parse(strings_path.read_bytes(), encoding="utf-8")
|
|
||||||
|
|
||||||
for item in strings_dict["resources"]["string"]:
|
|
||||||
if isinstance(item, dict) and item["@name"] == key:
|
|
||||||
source = item["#text"]
|
|
||||||
print(f"I: Patching {strings_path.name} ({strings_path.parent})")
|
|
||||||
if source != name:
|
|
||||||
item["#text"] = name
|
|
||||||
strings_path.write_bytes(
|
|
||||||
xmltodict.unparse(strings_dict, encoding="utf-8", pretty=True).encode("utf-8"))
|
|
||||||
break
|
|
||||||
if not source:
|
|
||||||
raise ImportError(value)
|
|
||||||
else:
|
|
||||||
manifest_dict["manifest"]["application"]["@android:label"] = name
|
|
||||||
manifest_path.write_bytes(xmltodict.unparse(manifest_dict, encoding="utf-8", pretty=True).encode("utf-8"))
|
|
||||||
print(f"I: Patching {manifest_path.name} ({manifest_path.parent})")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
config = Path("config.yaml")
|
|
||||||
if not config.is_file():
|
|
||||||
config = Path(input("Config Path: "))
|
|
||||||
if not config.is_file():
|
|
||||||
raise FileNotFoundError(config)
|
|
||||||
|
|
||||||
content = yaml.safe_load(config.read_text())
|
|
||||||
|
|
||||||
apktool = ApkTool()
|
|
||||||
jks = Keystore(**content["keystore"])
|
|
||||||
src = Path(content["metadata"]["input"])
|
|
||||||
opt = Path(content["metadata"]["output"])
|
|
||||||
|
|
||||||
for key, value in content["metadata"].items():
|
|
||||||
print(f"I: {key.capitalize()}: {value}")
|
|
||||||
|
|
||||||
if not opt.is_file():
|
|
||||||
apktool.decompile(src)
|
|
||||||
|
|
||||||
# @Info: Patch apk
|
|
||||||
for key, value in content["patch"].items(): # {Path: list[tuple]}
|
|
||||||
|
|
||||||
path = apktool.instance / key
|
|
||||||
if not path.is_file():
|
|
||||||
exist = False
|
|
||||||
for subp in apktool.instance.iterdir():
|
|
||||||
path = subp / key
|
|
||||||
if path.is_file():
|
|
||||||
exist = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not exist:
|
|
||||||
raise FileNotFoundError(key)
|
|
||||||
|
|
||||||
src_data = path.read_text()
|
|
||||||
for v in value:
|
|
||||||
if v[0] is None:
|
|
||||||
if isinstance(v[1], str):
|
|
||||||
# @Info: Replace complet file using [None, Path]
|
|
||||||
v[1] = Path(v[1])
|
|
||||||
if not v[1].is_file():
|
|
||||||
raise FileNotFoundError(v[1])
|
|
||||||
src_data = v[1].read_text()
|
|
||||||
elif v[1] is None:
|
|
||||||
# @Info: Replace with empty file
|
|
||||||
src_data = ""
|
|
||||||
else:
|
|
||||||
# @Info: Replace with custom char
|
|
||||||
src_data = any2str(v[1])
|
|
||||||
elif v[1] is None:
|
|
||||||
# @Info: Replace functon using [Path, None]
|
|
||||||
if not isinstance(v[0], str):
|
|
||||||
raise ImportError(v[0])
|
|
||||||
|
|
||||||
v[0] = Path(v[0])
|
|
||||||
if not v[0].is_file():
|
|
||||||
raise FileNotFoundError(v[0])
|
|
||||||
|
|
||||||
opt_data = v[0].read_text()
|
|
||||||
if opt_data not in src_data:
|
|
||||||
try:
|
|
||||||
keys = opt_data.split("\n")
|
|
||||||
start = next(filter(None, keys), None)
|
|
||||||
stop = next(filter(None, reversed(keys)), None)
|
|
||||||
start_index = src_data.index(start)
|
|
||||||
stop_index = src_data.index(stop, start_index) + len(stop)
|
|
||||||
src_data = src_data.replace(src_data[start_index:stop_index], opt_data)
|
|
||||||
except Exception as e:
|
|
||||||
raise ValueError(v[0])
|
|
||||||
else:
|
|
||||||
# @Info: Replace char using [str, str]
|
|
||||||
if not v[0] in src_data and not v[1] in src_data:
|
|
||||||
raise ImportError(v[0])
|
|
||||||
src_data = src_data.replace(*v)
|
|
||||||
|
|
||||||
path.write_text(src_data)
|
|
||||||
print(f"I: Patching {path.name} ({path.parent})")
|
|
||||||
|
|
||||||
# @Info: Rename apk
|
|
||||||
name = content["metadata"].get("name")
|
|
||||||
if name:
|
|
||||||
rename_app(apktool.instance, name)
|
|
||||||
|
|
||||||
apktool.compile(opt)
|
|
||||||
print(f"I: Keystore: {jks.path}")
|
|
||||||
print(f"I: Validity: {jks.validity}")
|
|
||||||
jks.sign(opt)
|
|
||||||
|
|
||||||
jks.info(opt)
|
|
||||||
|
|
||||||
print(f'I: MD5: {hashlib.md5(opt.read_bytes()).hexdigest()}')
|
|
|
@ -1,9 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"title": "Axinom demo video - single key (DASH; cenc)",
|
|
||||||
"videoUrl": "https://media.axprod.net/VTB/DrmQuickStart/AxinomDemoVideo-SingleKey/Encrypted_Cenc/Manifest.mpd",
|
|
||||||
"drmScheme": "widevine",
|
|
||||||
"licenseServer": "https://drm-widevine-licensing.axtest.net/AcquireLicense",
|
|
||||||
"licenseToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6eyJ2ZXJzaW9uIjoyLCJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImxpY2Vuc2UiOnsiYWxsb3dfcGVyc2lzdGVuY2UiOnRydWV9LCJjb250ZW50X2tleXNfc291cmNlIjp7ImlubGluZSI6W3siaWQiOiIyMTFhYzFkYy1jOGEyLTQ1NzUtYmFmNy1mYTRiYTU2YzM4YWMiLCJ1c2FnZV9wb2xpY3kiOiJUaGVPbmVQb2xpY3kifV19LCJjb250ZW50X2tleV91c2FnZV9wb2xpY2llcyI6W3sibmFtZSI6IlRoZU9uZVBvbGljeSIsInBsYXlyZWFkeSI6eyJwbGF5X2VuYWJsZXJzIjpbIjc4NjYyN0Q4LUMyQTYtNDRCRS04Rjg4LTA4QUUyNTVCMDFBNyJdfX1dfX0.D9FM9sbTFxBmcCOC8yMHrEtTwm0zy6ejZUCrlJbHz_U"
|
|
||||||
}
|
|
||||||
]
|
|
Loading…
Reference in New Issue