feat: MariBank 风控 bypass、澳洲银行 Hook 与 reverse 逆向工作区

新增 MariBank/SeaBank PH Root 与 SHPSSDK bypass、riskToken 净化及 Up/Suncorp/ubank 消息 Hook;整理 reverse/ 脚本与 Frida 工具链,并补充当日工作说明文档。
This commit is contained in:
2026-07-03 17:15:16 +08:00
parent 125dfe583b
commit 59970a84a8
121 changed files with 7606 additions and 37 deletions

13
reverse/scripts/_paths.py Normal file
View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
"""Shared paths for reverse/ scripts (scripts live in reverse/scripts/)."""
from pathlib import Path
REVERSE_ROOT = Path(__file__).resolve().parent.parent
APKS_DIR = REVERSE_ROOT / "apks"
EXTRACTED_DIR = REVERSE_ROOT / "extracted"
NATIVE_DIR = EXTRACTED_DIR / "native"
TMP_DIR = REVERSE_ROOT / "tmp"
OUTPUT_DIR = REVERSE_ROOT / "output"
LOGS_DIR = REVERSE_ROOT / "logs"
DEFAULT_APK = APKS_DIR / "seabank_ph_base.apk"

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import re
import subprocess
from pathlib import Path
SO = Path(__file__).resolve().parent.parent / "extracted" / "native" / "libshpssdk_bank.so"
ndk = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\ndk\21.4.7075529\toolchains\llvm\prebuilt\windows-x86_64\bin")
readelf = ndk / "llvm-readelf.exe"
out = subprocess.check_output([str(readelf), "-Ws", str(SO)], universal_newlines=True, errors="replace")
print("=== JNI Java_* ===")
for line in out.splitlines():
if "Java_com_shopee" in line:
print(line)
print("\n=== risk/root/token strings in .dynsym FUNC ===")
data = SO.read_bytes()
for m in re.finditer(rb"[\x20-\x7e]{4,}", data):
s = m.group().decode("latin1")
if any(k in s.lower() for k in ["risk", "root", "hook", "token", "proc/", "magisk", "xposed", "emulator", "assess"]):
if len(s) < 120:
print(s)

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
data = open(r"C:\Users\Administrator\Desktop\notiMessage\reverse\extracted\classes6.dex", "rb").read()
idx = data.find(b"WBRootDetectionModule")
print("offset", idx)
print(data[idx-120:idx+200].decode("latin1", "ignore"))

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
"""Decode SHPSSDK obfuscated hex strings via uvuwwwuwu."""
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
# sample pairs from SHPSSDK.wwwwvwwwu
samples = [
("0F580514065B111F0E1A10", "wuvuvvwvv"),
("100F3C013C1C19113F18271F3A36371F2C253C403F323C053F353C013F1C19123F1F3C4F", "uvvvuvvvv"),
("0E5806170758131F0D1B10", "vuuvwuuvu"),
("120E3D023E1C1A113C1A261E3934371C2C263E413E313A053C353F033E1D1A103F1C3C4C", "wwwuwvuvu"),
]
# dump uvuwwwuwu implementation
with zipfile.ZipFile(str(APK)) as zf:
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp11.dex"
tmp.write_bytes(zf.read("classes11.dex"))
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
cap = False
print("=== uvuwwwuwu implementation ===")
for line in out.splitlines():
if "uvuwwwuwu:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" in line:
cap = True
if cap:
print(line.encode("ascii", "replace").decode())
if cap and "locals :" in line:
break
print("\n=== try XOR decode (common pattern) ===")
for hex_str, key in samples:
data = bytes.fromhex(hex_str) if all(c in "0123456789ABCDEFabcdef" for c in hex_str) else hex_str.encode()
# try simple xor with key bytes cycling
kb = key.encode()
dec = bytes(b ^ kb[i % len(kb)] for i, b in enumerate(data))
try:
txt = dec.decode("utf-8")
except Exception:
txt = dec.decode("latin1", errors="replace")
print(hex_str[:20], "...", "->", repr(txt[:80]))

View File

@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
"""Dump JNI/native methods for sdkutils crypto + register-related classes."""
import re
import subprocess
import zipfile
from pathlib import Path
from typing import List
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
OUT = Path(__file__).resolve().parent.parent / "output" / "crypto_jni_targets.txt"
CLASS_PATTERNS = [
rb"Lcom/shopee/bke/lib/jni/[^;]{1,80};",
rb"Lcom/shopee/shpssdkbank/wvvvuwwu;",
rb"Lcom/shopee/shpssdkbank/uwuvuvvww/vvuuuuvvv;",
rb"Lcom/shopee/shpssdkbank/uwuvuvvww/[^;]{1,40};",
]
EXTRA_KEYWORDS = (
b"NativeEncrypt",
b"CharacterCrypto",
b"SecurityMain",
b"encrypt",
b"decrypt",
b"register",
)
def dump_class(out: str, target: str) -> List[str]:
lines: list[str] = []
cap = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
cap = True
elif cap and line.startswith(" Class descriptor") and target not in line:
break
if cap:
lines.append(line)
return lines
def main() -> None:
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
classes: set[str] = set()
for pat in CLASS_PATTERNS:
for m in re.finditer(pat, data):
classes.add(m.group().decode()[1:-1].replace("/", "."))
for kw in EXTRA_KEYWORDS:
for m in re.finditer(rb"Lcom/[^;]{0,120};", data):
s = m.group()
if kw in s or kw in data[data.find(s) : data.find(s) + 4000]:
classes.add(s.decode()[1:-1].replace("/", "."))
# log tags -> classes from crash log
for tag in [
"com.shopee.bke.lib.jni.utils.f", # SoUtils
"com.shopee.bke.lib.jni.uwuwuwuw",
"com.shopee.bke.lib.jni.uvuvuvuv",
"com.shopee.bke.lib.jni.uvwwwwuv",
]:
classes.add(tag)
native_entries: List[str] = []
with zipfile.ZipFile(str(APK)) as zf:
for dex_name in zf.namelist():
if not dex_name.endswith(".dex"):
continue
dex = zf.read(dex_name)
hits = [c for c in classes if c.replace(".", "/") in dex.decode("latin1", errors="ignore")]
if not hits:
continue
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_crypto_jni.dex"
tmp.write_bytes(dex)
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for cls in sorted(hits):
block = dump_class(out, "L" + cls.replace(".", "/") + ";")
if not block:
continue
native_entries.append(f"\n=== {dex_name} {cls} ===")
for line in block:
if any(
k in line
for k in ("NATIVE", "name :", "type :", "loadLibrary")
):
native_entries.append(line)
text = "\n".join(native_entries)
OUT.write_text(text, encoding="utf-8")
print(text)
print(f"\nwritten {OUT}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
targets = [
"Lcom/shopee/bke/biz/user/errorcodehandler/b;",
"Lcom/shopee/bke/biz/user/errorcodehandler/b$a;",
"Lcom/shopee/bke/biz/user/errorcodehandler/a;",
]
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp6.dex"
with zipfile.ZipFile(str(APK)) as zf:
tmp.write_bytes(zf.read("classes6.dex"))
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace")
for target in targets:
print("=" * 60, target)
capture = False
lines = 0
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
capture = True
elif capture and line.startswith(" Class descriptor") and target not in line:
break
if capture:
if "name :" in line or "type :" in line or "Class descriptor" in line:
print(line.strip())
lines += 1
if lines > 200:
break

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
target = "Lcom/shopee/bke/biz/user/errorcodehandler/b$a;"
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp6.dex"
with zipfile.ZipFile(str(APK)) as zf:
tmp.write_bytes(zf.read("classes6.dex"))
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace")
capture = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
capture = True
elif capture and line.startswith(" Class descriptor"):
break
if capture and ("name :" in line or "type :" in line or "Class descriptor" in line):
print(line.encode("ascii", "replace").decode())

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp6.dex"
with zipfile.ZipFile(str(APK)) as zf:
tmp.write_bytes(zf.read("classes6.dex"))
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace")
cls = "GlobalAuthErrorImpl"
capture = False
for line in out.splitlines():
if f"Lcom/shopee/bke/digitalbank/container/user/errorcodehandler/{cls};" in line and "Class descriptor" in line:
capture = True
if capture:
print(line)
if line.strip() == "" and "Method" not in line and capture:
pass
if capture and line.startswith(" Class descriptor") and cls not in line:
break

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
methods = [
("Lcom/shopee/shpssdkbank/SHPSSDK;", "getRiskToken"),
("Lcom/shopee/shpssdkbank/wvvvuwwu;", "vvuwuuvuu"),
("Lcom/shopee/shpssdkbank/wvvvuwwu;", "vuwuuwvw"),
("Lcom/shopee/shpssdkbank/wvvvuwwu;", "vuwuuuwv"),
]
with zipfile.ZipFile(str(APK)) as zf:
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp11.dex"
tmp.write_bytes(zf.read("classes11.dex"))
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for cls, method in methods:
needle = f"{cls.replace('L', '').replace(';', '').split('/')[-1]}.{method}:"
print("\n" + "=" * 70, cls, method)
cap = False
for line in out.splitlines():
if needle in line.replace("com.shopee.shpssdkbank.", ""):
cap = True
if cap:
print(line.encode("ascii", "replace").decode())
if line.strip().startswith("catches") or (cap and line.strip() == "locals :"):
pass
if cap and line.strip() == "" and "positions" in line:
break
if cap and line.startswith(" name") and method not in line and cap:
# next method
if methods.index((cls, method)) < len(methods) - 1:
break
# classes8 - sdkutils / crypto
with zipfile.ZipFile(str(APK)) as zf:
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp8.dex"
tmp.write_bytes(zf.read("classes8.dex"))
out8 = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for tag in ["CharacterCrypto", "SoUtils", "SecurityMain", "sdkutils", "encrypt"]:
print("\n--- search", tag, "in classes8 ---")
for line in out8.splitlines():
if tag.lower() in line.lower() and ("Class descriptor" in line or "name :" in line):
print(line.encode("ascii", "replace").decode()[:200])

View File

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
targets = [
"Lcom/shopee/shpssdkbank/wvvvuwwu;",
"Lcom/shopee/shpssdkbank/uwuvuvvww/wvvuuwvwu;",
"Lcom/shopee/shpssdkbank/uwuvuvvww/uvwuuuuuw/vvvvuwwvu;",
"Lcom/shopee/shpssdkbank/SHPSSDK;",
"Lcom/shopee/shpssdkbank/ShpssInstall;",
]
with zipfile.ZipFile(str(APK)) as zf:
for dex_name in zf.namelist():
if not dex_name.endswith(".dex"):
continue
dex = zf.read(dex_name)
hit = any(t.replace("L", "").replace(";", "") in dex.decode("latin1", errors="ignore") for t in targets)
if not hit:
continue
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_native.dex"
tmp.write_bytes(dex)
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for target in targets:
if target.replace("L", "").replace(";", "") not in dex.decode("latin1", errors="ignore"):
continue
print("\n" + "=" * 70)
print(dex_name, target)
cap = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
cap = True
elif cap and line.startswith(" Class descriptor") and target not in line:
break
if cap:
safe = line.encode("ascii", "replace").decode()
if any(k in safe for k in ["name", "type", "access", "NATIVE", "Method", "loadLibrary", "register"]):
print(safe)
# find sdkutils / crypto manager classes
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
print("\n--- sdkutils / crypto / dfp classes ---")
for pat in [
rb"Lcom/[^;]{0,80}sdkutils[^;]{0,20};",
rb"Lcom/[^;]{0,80}[Cc]rypto[^;]{0,40};",
rb"Lcom/[^;]{0,80}dfp[^;]{0,30};",
rb"Lcom/[^;]{0,80}SecurityMain[^;]{0,20};",
]:
found = set()
for m in re.finditer(pat, data):
s = m.group().decode()[1:-1].replace("/", ".")
if s not in found:
found.add(s)
print(s)

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
targets = [
"Lcom/shopee/bke/biz/user/viewmodel/PhoneNumViewModel;",
"Lcom/shopee/bke/biz/user/viewmodel/RegisterViewModel;",
]
with zipfile.ZipFile(str(APK)) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
if b"PhoneNumViewModel" not in data and b"RegisterViewModel" not in data:
continue
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_user.dex"
tmp.write_bytes(data)
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for target in targets:
if target.replace("L", "").replace(";", "") not in data.decode("latin1", errors="ignore"):
continue
print("=" * 60, name, target)
cap = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
cap = True
elif cap and line.startswith(" Class descriptor") and target not in line:
break
if cap and ("name :" in line or "type :" in line
or "Method" in line or "register" in line.lower()):
safe = line.strip().encode("ascii", "replace").decode()
print(safe)

View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for m in re.finditer(rb"Lcom/shopee/bke/[^;]{0,80}RegisterRequest[^;]{0,20};", data):
print(m.group().decode()[1:-1].replace("/", "."))
for name in zf.namelist():
if not name.endswith(".dex"):
continue
dex = zf.read(name)
if b"RegisterRequest" not in dex:
continue
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_reg.dex"
tmp.write_bytes(dex)
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for target in re.findall(r"Lcom/shopee/bke/[^;]*RegisterRequest[^;]*;", data.decode("latin1", errors="ignore")):
cap = False
print("\n" + "=" * 60, name, target)
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
cap = True
elif cap and line.startswith(" Class descriptor") and target not in line:
break
if cap:
print(line)

View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
targets = [
"Lcom/shopee/bke/biz/user/viewmodel/RegisterViewModel;",
"Lcom/shopee/bke/biz/user/ui/PhoneNumActivity;",
]
with zipfile.ZipFile(str(APK)) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
if b"RegisterViewModel" not in data:
continue
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_reg.dex"
tmp.write_bytes(data)
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for target in targets:
if target.replace("L", "").replace(";", "") not in data.decode("latin1", errors="ignore"):
continue
print("=" * 60, name, target)
cap = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
cap = True
elif cap and line.startswith(" Class descriptor") and target not in line:
break
if cap and ("name :" in line or "type :" in line):
print(line.strip().encode("ascii", "replace").decode())

View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
"""Dump riskToken + requestDefense + register crypto call chain."""
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
TARGETS = [
("classes11.dex", "Lcom/shopee/shpssdkbank/SHPSSDK;", "getRiskToken"),
("classes11.dex", "Lcom/shopee/shpssdkbank/SHPSSDK;", "requestDefense"),
("classes11.dex", "Lcom/shopee/shpssdkbank/uwuvuvvww/vvuuuuvvv;", "wwvuwuwvu"),
("classes11.dex", "Lcom/shopee/shpssdkbank/uwuvuvvww/vvuuuuvvv;", "wuvwuvwwu"),
("classes11.dex", "Lcom/shopee/shpssdkbank/wvvvuwwu;", "wwvwvwuvv"),
("classes11.dex", "Lcom/shopee/shpssdkbank/wvvvuwwu;", "vvuwuuvuu"),
]
def dump_method(out, cls, method):
cls_short = cls.replace("L", "").replace(";", "").replace("/", ".")
needle = cls_short + "." + method + ":"
print("\n" + "=" * 72)
print(cls_short, method)
cap = False
lines = []
for line in out.splitlines():
if needle in line:
cap = True
if cap:
lines.append(line)
if len(lines) > 1 and line.strip().startswith("name :") and method not in line:
break
for line in lines[:80]:
print(line.encode("ascii", "replace").decode())
with zipfile.ZipFile(str(APK)) as zf:
for dex_name, cls, method in TARGETS:
tmp = Path(__file__).resolve().parent.parent / "tmp" / "_tmp.dex"
tmp.write_bytes(zf.read(dex_name))
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
dump_method(out, cls, method)
# classes8 CharacterCrypto / SoUtils
print("\n" + "=" * 72, "classes8 crypto classes")
with zipfile.ZipFile(str(APK)) as zf:
tmp = Path(__file__).resolve().parent.parent / "tmp" / "_tmp8.dex"
tmp.write_bytes(zf.read("classes8.dex"))
out8 = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp8)], universal_newlines=True, errors="replace"
)
cap = False
for line in out8.splitlines():
if "Class descriptor" in line and (
"CharacterCrypto" in line or "SoUtils" in line or "sdkutils" in line.lower()
):
print("\n---", line.strip())
cap = True
continue
if cap:
if line.startswith(" Class descriptor") and "CharacterCrypto" not in line:
cap = False
continue
if "name :" in line or "NATIVE" in line or "loadLibrary" in line:
print(line.strip()[:180])

View File

@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
import subprocess
import sys
import zipfile
import tempfile
import os
TARGET = "com/shopee/bke/lib/safemode/b;"
APK = sys.argv[1] if len(sys.argv) > 1 else "reverse/apks/seabank_ph_base.apk"
BT = None
def find_build_tools():
base = os.environ.get("ANDROID_HOME") or os.path.expanduser(
r"~\AppData\Local\Android\Sdk"
)
tools = os.path.join(base, "build-tools")
versions = sorted(os.listdir(tools), reverse=True)
return os.path.join(tools, versions[0], "dexdump.exe")
def main():
dexdump = find_build_tools()
with zipfile.ZipFile(APK) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
if TARGET.encode() not in data and b"Lcom/shopee/bke/lib/safemode/b;" not in data:
continue
print("FOUND in", name)
tmp = tempfile.NamedTemporaryFile(suffix=".dex", delete=False)
tmp.write(data)
tmp.close()
try:
out = subprocess.check_output(
[dexdump, "-f", tmp.name], stderr=subprocess.STDOUT, text=True,
errors="ignore"
)
capture = False
for line in out.splitlines():
if "Class descriptor : 'Lcom/shopee/bke/lib/safemode/b;'" in line:
capture = True
elif capture and line.startswith(" Class descriptor"):
break
if capture:
if "name :" in line or "type :" in line or "Class descriptor" in line:
print(line)
finally:
os.unlink(tmp.name)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
target = "Lcom/shopee/shpssdkbank/SHPSSDK;"
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp11.dex"
with zipfile.ZipFile(str(APK)) as zf:
tmp.write_bytes(zf.read("classes11.dex"))
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace")
capture = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
capture = True
elif capture and line.startswith(" Class descriptor"):
break
if capture and ("name :" in line or "type :" in line or "access :" in line):
print(line.encode("ascii", "replace").decode())

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
target = "Lcom/shopee/shpssdkbank/SHPSSDK;"
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp11.dex"
with zipfile.ZipFile(str(APK)) as zf:
tmp.write_bytes(zf.read("classes11.dex"))
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace")
capture = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
capture = True
elif capture and line.startswith(" Class descriptor"):
break
if capture:
print(line.encode("ascii", "replace").decode())

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
targets = [
"Lcom/shopee/shpssdkbank/ShpssInstall;",
"Lcom/shopee/shpssdkbank/vuvuwwwuw;",
"Lcom/shopee/shpssdkbank/vwuuwwvwv;",
"Lcom/shopee/shpssdkbank/uvuwwuvwv/uvwwuuvvw;",
]
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp11.dex"
with zipfile.ZipFile(str(APK)) as zf:
tmp.write_bytes(zf.read("classes11.dex"))
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace")
for target in targets:
print("=" * 60, target)
cap = False
n = 0
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
cap = True
elif cap and line.startswith(" Class descriptor"):
break
if cap:
print(line.encode("ascii", "replace").decode())
n += 1
if n > 80:
print("...truncated...")
break

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp11.dex"
with zipfile.ZipFile(APK) as zf:
tmp.write_bytes(zf.read("classes11.dex"))
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for line in out.splitlines():
if "SHPSSDK;" in line and any(
k in line for k in ("getRisk", "assessRisk", "getRiskToken", "getExtRisk")
):
print(line.strip())

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp11.dex"
with zipfile.ZipFile(str(APK)) as zf:
tmp.write_bytes(zf.read("classes11.dex"))
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace")
in_shps = False
for line in out.splitlines():
if "Class descriptor : 'Lcom/shopee/shpssdk" in line:
in_shps = "SHPSSDK;" in line or "shpssdkbank" in line
if in_shps and line.startswith(" Class descriptor") and "shpssdk" not in line:
break
if in_shps and ("NATIVE" in line or "name :" in line):
print(line.strip())

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
import sys
import zipfile
from pathlib import Path
SPLIT = Path(__file__).resolve().parent.parent / "extracted" / "native" / "split_config.arm64_v8a.apk"
if not SPLIT.exists():
native_dir = SPLIT.parent
candidates = list(native_dir.glob("split_config.arm64*.apk"))
if not candidates:
print("missing split APK at", SPLIT, file=sys.stderr)
print("run: reverse/frida/pull_split_apk.ps1", file=sys.stderr)
sys.exit(1)
SPLIT = candidates[0]
OUT = SPLIT.parent
with zipfile.ZipFile(str(SPLIT)) as zf:
for name in zf.namelist():
if name.endswith(".so"):
path = OUT / Path(name).name
path.write_bytes(zf.read(name))
print(path.name, path.stat().st_size)

View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
import os
import zipfile
from pathlib import Path
split = Path(__file__).resolve().parent.parent / "extracted" / "native" / "split_arm64.apk"
out = split.parent
with zipfile.ZipFile(str(split)) as zf:
for name in zf.namelist():
if "libshpssdk" in name:
dest = out / Path(name).name
dest.write_bytes(zf.read(name))
print(dest, dest.stat().st_size)

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
import os
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
OUT = Path(__file__).resolve().parent.parent / "extracted" / "native"
OUT.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(str(APK)) as zf:
for name in zf.namelist():
if "libshpssdk" in name and name.endswith(".so"):
path = OUT / Path(name).name
path.write_bytes(zf.read(name))
print("extracted", path, path.stat().st_size)

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
import os
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
OUT = Path(__file__).resolve().parent.parent / "extracted" / "native"
OUT.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(str(APK)) as zf:
for name in zf.namelist():
if "libshpssdk" in name and name.endswith(".so"):
dest = OUT / Path(name).name
dest.write_bytes(zf.read(name))
print(dest, dest.stat().st_size)

View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
import os
import zipfile
from pathlib import Path
SPLIT = Path(__file__).resolve().parent.parent / "extracted" / "native" / "split_config.arm64_v8a.apk"
OUT = SPLIT.parent
with zipfile.ZipFile(str(SPLIT)) as zf:
for name in zf.namelist():
if "libshpssdk" in name and name.endswith(".so"):
path = OUT / Path(name).name
path.write_bytes(zf.read(name))
print("extracted", path, path.stat().st_size)

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import json
import glob
import os
from pathlib import Path
root = str(Path(__file__).resolve().parent.parent / "extracted" / "apk_extract")
needle = "this service has been temporarily blocked"
for fp in glob.glob(os.path.join(root, "**", "en.json"), recursive=True):
try:
with open(fp, encoding="utf-8") as f:
d = json.load(f)
if not isinstance(d, dict):
continue
for k, v in d.items():
if isinstance(v, str) and needle in v.lower():
print(fp)
print(k, "->", v)
except Exception:
pass

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
with zipfile.ZipFile(str(APK)) as zf:
for dex_name in zf.namelist():
if not dex_name.endswith(".dex"):
continue
data = zf.read(dex_name)
if b"CharacterCrypto" not in data and b"SoUtils" not in data:
continue
print("===", dex_name, "===")
for m in re.finditer(rb"L[^;]{0,120};", data):
s = m.group().decode("latin1", errors="replace")
if "CharacterCrypto" in s or "SoUtils" in s or "SecurityMain" in s:
print(s[1:-1].replace("/", "."))

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
with zipfile.ZipFile(APK) as zf:
for name in sorted(zf.namelist()):
if not name.endswith(".dex"):
continue
data = zf.read(name)
found = set()
for m in re.finditer(rb"L[a-zA-Z0-9_$/]+;", data):
s = m.group().decode()[1:-1].replace("/", ".")
low = s.lower()
if any(k in low for k in ("rootdetect", "emulatordetect", "safemode", "risk", "integrity", "xposed", "hookdetect")):
found.add(s)
if "WBRoot" in s or "WBEmulator" in s:
found.add(s)
if found:
print("=== %s ===" % name)
for s in sorted(found):
print(s)

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needle = b"Lcom/shopee/bke/digitalbank/container/user/errorcodehandler/GlobalAuthErrorImpl;"
with zipfile.ZipFile(str(APK)) as zf:
for name in zf.namelist():
if name.endswith(".dex") and needle in zf.read(name):
print("found in", name)

View File

@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needles = [b"IV_Monitor", b"register scene", b"dfp is empty", b"CharacterCrypto"]
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for n in needles:
print(n.decode(), data.count(n))
idx = data.find(n)
if idx >= 0:
ctx = data[max(0, idx - 80): idx + 120]
import re
for m in re.finditer(rb"Lcom/[^;\x00]{5,120};", ctx):
print(" class", m.group().decode()[1:-1].replace("/", "."))

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
with zipfile.ZipFile(str(APK)) as zf:
for dex_name in zf.namelist():
if not dex_name.endswith(".dex"):
continue
data = zf.read(dex_name)
if b"loadSoLibrary" not in data:
continue
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_loadso.dex"
tmp.write_bytes(data)
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
current_class = ""
for line in out.splitlines():
m = re.search(r"Class descriptor\s+:\s+'([^']+)'", line)
if m:
current_class = m.group(1)
if "loadSoLibrary" not in line:
continue
cls = current_class.replace("L", "").replace(";", "").replace("/", ".")
print(f"{dex_name}\t{cls}\t{line.strip()}")

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for kw in [b"NativeEncrypt", b"CharacterCrypto", b"IV_Monitor", b"register scene", b"dfp is empty"]:
print(kw.decode(), data.count(kw))
print("\n--- bke.lib.jni crypto/security ---")
seen = set()
for m in re.finditer(rb"Lcom/shopee/bke/lib/jni/[^;]{1,120};", data):
s = m.group().decode()[1:-1].replace("/", ".")
if s in seen:
continue
if any(x in s.lower() for x in ("crypto", "encrypt", "security", "native", "tee")):
seen.add(s)
print(s)

View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
needle = b"NativeEncrypt"
with zipfile.ZipFile(str(APK)) as zf:
for dex_name in zf.namelist():
if not dex_name.endswith(".dex"):
continue
dex = zf.read(dex_name)
if needle not in dex:
continue
print("===", dex_name, "===")
for m in re.finditer(rb"const-string[^/]*// string@[0-9a-f]+", dex):
pass
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_ne.dex"
tmp.write_bytes(dex)
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
cap = False
for line in out.splitlines():
if "NativeEncrypt" in line:
print(line.strip())
if "Class descriptor" in line:
current = line
if "NativeEncrypt" in line:
# print previous class context
idx = out.splitlines().index(line)
for prev in out.splitlines()[max(0, idx - 40) : idx + 5]:
if "Class descriptor" in prev or "name :" in prev or "NATIVE" in prev:
print(prev.strip())

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
import subprocess
import zipfile
from pathlib import Path
split_apk = Path(__file__).resolve().parent.parent / "extracted" / "native" / "split_arm64.apk"
base_apk = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
apk = base_apk if base_apk.exists() else None
if apk is None:
# use device base if needed - skip
import sys
print("no base apk")
sys.exit(0)
with zipfile.ZipFile(str(apk)) as zf:
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp11.dex"
tmp.write_bytes(zf.read("classes11.dex"))
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace")
for line in out.splitlines():
if "0x0101" in line or "NATIVE" in line:
print(line.strip())

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needles = [
b"getRiskTokenAsync",
b"getRiskToken",
b"getRiskSync",
b"getRiskAsync",
b"assessRisk",
]
with zipfile.ZipFile(str(APK)) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
hits = [n.decode() for n in needles if n in data]
if not hits:
continue
print(name, hits)
for m in re.finditer(
rb"Lcom/shopee/shpssdk(?:bank)?/SHPSSDK;\.(\w+):\([^)]+\)[^;]+;", data
):
print(" ", m.group().decode())

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
with zipfile.ZipFile(APK) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
if b"WBRootDetectionModule" not in data:
continue
print("===", name, "===")
for m in re.finditer(rb"Lcom/shopee/[a-zA-Z0-9_$/]+;", data):
s = m.group().decode()[1:-1].replace("/", ".")
if "Root" in s or "Detect" in s or "Emulator" in s or "Safe" in s or "Risk" in s or "WB" in s:
print(s)

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
needles = [b"WBRootDetectionModule", b"WBEmulatorDetectionModule", b"SPSAssessRisk", b"shpssdk"]
with zipfile.ZipFile(APK) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
hits = [n.decode() for n in needles if n in data]
if hits:
print(name, hits)
for m in re.finditer(rb"L[a-zA-Z0-9_$/]*WBRootDetectionModule;", data):
print(" ", m.group().decode()[1:-1].replace("/", "."))
for m in re.finditer(rb"L[a-zA-Z0-9_$/]*SPSAssessRisk[^;]*;", data):
print(" ", m.group().decode()[1:-1].replace("/", "."))
for m in re.finditer(rb"Lcom/shopee/shpssdk[a-zA-Z0-9_$/]+;", data):
s = m.group().decode()[1:-1].replace("/", ".")
if "shpssdk" in s.lower():
print(" ", s)

View File

@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
import re
data = open(r"C:\Users\Administrator\Desktop\notiMessage\reverse\extracted\classes6.dex", "rb").read()
# method names in dex are plain utf8 strings
methods = set(re.findall(rb"[a-zA-Z][a-zA-Z0-9_]{2,60}", data))
interesting = sorted(
m.decode("ascii", "ignore")
for m in methods
if any(k in m.lower() for k in (
b"root", b"jail", b"safe", b"xposed", b"frida", b"emulator",
b"integrity", b"detect", b"hook", b"debug", b"tamper", b"risk"
))
)
for m in interesting:
print(m)

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
import re
data = open(r"C:\Users\Administrator\Desktop\notiMessage\reverse\extracted\classes6.dex", "rb").read()
classes = set()
for m in re.finditer(rb"Lcom/shopee/bke/lib/safemode/[a-zA-Z0-9_$/]+;", data):
s = m.group().decode()[1:-1].replace("/", ".")
if "R$" in s:
continue
classes.add(s)
for c in sorted(classes):
print(c)
print("\n--- interesting strings ---")
for pat in [b"isRoot", b"jailbroken", b"rooted", b"SafeMode", b"checkRoot", b"detect", b"xposed", b"frida", b"integrity"]:
if pat in data:
print(pat.decode(), "YES")

View File

@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
import re
from pathlib import Path
so = Path(__file__).resolve().parent.parent / "extracted" / "native" / "libsdkutils.so"
data = so.read_bytes()
for name in sorted(set(m.group().decode() for m in re.finditer(rb"Java_com_shopee_bke_lib_jni_[A-Za-z0-9_]+", data))):
if "utils" in name or "encrypt" in name.lower():
print(name)

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
with zipfile.ZipFile(APK) as zf:
for name in zf.namelist():
if "alc" in name.lower() and name.endswith(".json"):
print("===", name, "===")
print(zf.read(name).decode("utf-8", "ignore")[:2000])
print()

View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
needles = [b"-1201", b"1201", b"ErrorCode", b"error has occurred"]
with zipfile.ZipFile(APK) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for n in needles:
idx = data.find(n)
if idx >= 0:
print(n.decode(), "at", idx, "context:", data[max(0,idx-40):idx+60])

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needle = b"4067"
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
idx = 0
while True:
idx = data.find(needle, idx)
if idx < 0:
break
ctx = data[max(0, idx - 80): idx + 120]
import re
strings = [m.group().decode("latin1") for m in re.finditer(rb"[\x20-\x7e]{3,80}", ctx)]
print("--- at", idx, "---")
for s in strings:
print(" ", s)
idx += 1

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
targets = [
b"Lcom/shopee/bke/lib/commonui/widget/CommonDialog;",
b"Lcom/shopee/bke/lib/commonui/widget/CommonDialog$Builder;",
]
with zipfile.ZipFile(APK) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for t in targets:
print("===", t.decode()[1:-1].replace("/", "."), "===")
idx = 0
c = 0
while c < 5:
idx = data.find(t, idx)
if idx < 0:
break
ctx = data[max(0, idx - 100): idx + 300]
for m in re.finditer(rb"(show|build|create|setMessage|setContent|display)[a-zA-Z0-9_$<>]*", ctx):
print(" ", m.group().decode())
idx += 1
c += 1

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for needle in [b"sdkutils", b"SoUtils", b"CharacterCrypto", b"vvuwuuvuu", b"vuwuuwvw", b"wwvwvwuvv", b"dfp is empty"]:
print(needle.decode(), data.count(needle))
print("\n--- classes referencing sdkutils ---")
for m in re.finditer(rb"Lcom/[^;]{0,120};", data):
s = m.group()
if b"sdkutils" in s.lower() or b"SoUtils" in s or b"Crypto" in s:
print(s.decode()[1:-1].replace("/", "."))
# dump vvuuuuvvv.wwvuwuwvu (getRiskToken core)
target = "Lcom/shopee/shpssdkbank/uwuvuvvww/vvuuuuvvv;"
for dex_name in zf.namelist():
if not dex_name.endswith(".dex"):
continue
dex = zf.read(dex_name)
if target.encode() not in dex:
continue
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_vv.dex"
tmp.write_bytes(dex)
out = subprocess.check_output([str(DEXDUMP), "-d", str(tmp)], text=True, errors="replace")
print("\n===", dex_name, "vvuuuuvvv methods (native only) ===")
cap = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
cap = True
elif cap and line.startswith(" Class descriptor"):
break
if cap and ("NATIVE" in line or "name :" in line):
print(line.encode("ascii", "replace").decode())

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
needles = [
b"CharacterCrypto",
b"sdkutils",
b"SoUtils",
b"dfp is empty",
b"register scene",
b"deviceToken",
b"getRiskToken",
b"encrypt",
b"decrypt",
b"/uapi/v2/register",
]
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for n in needles:
print(n.decode(), data.count(n))
print("\n--- CharacterCrypto classes ---")
for m in re.finditer(rb"L[^;]{0,100}CharacterCrypto[^;]{0,40};", data):
print(m.group().decode()[1:-1].replace("/", "."))
print("\n--- SoUtils classes ---")
for m in re.finditer(rb"L[^;]{0,80}SoUtils[^;]{0,20};", data):
print(m.group().decode()[1:-1].replace("/", "."))
print("\n--- native methods in shpssdkbank ---")
for dex_name in zf.namelist():
if not dex_name.endswith(".dex"):
continue
dex = zf.read(dex_name)
if b"shpssdkbank" not in dex:
continue
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_shps.dex"
tmp.write_bytes(dex)
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
cap = False
cls = ""
for line in out.splitlines():
if "Class descriptor : 'Lcom/shopee/shpssdkbank/" in line:
cap = True
cls = line.split("'")[1]
elif cap and line.startswith(" Class descriptor") and "shpssdkbank" not in line:
cap = False
if cap and ("0x0101" in line or "NATIVE" in line):
print(cls.replace("L", "").replace(";", "").replace("/", "."), line.strip())

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
import os, re, sys
def scan_dir(base, pat):
rx = re.compile(pat, re.I)
all_m = set()
for dex in sorted(os.listdir(base)):
if not dex.endswith('.dex'):
continue
data = open(os.path.join(base, dex), 'rb').read()
strs = set(m.group().decode('ascii', 'ignore') for m in re.finditer(rb'[\x20-\x7e]{5,}', data))
all_m |= {s for s in strs if rx.search(s)}
return sorted(all_m)
apps = {
'up': (r'(HandlerService|NotificationHandler|showNotification|RemoteMessage|Util\$NotificationType|processPush|handleMessage|up/money/notifications)'),
'suncorp': (r'SuncorpMessagingService|onMessageReceived|showNotification|NotificationDetails|pushNotification|FirebaseService'),
'ubank': (r'MoEFireBase|MessagingService|onMessageReceived|showNotification|Will try to show|bank86400|MoEngage'),
}
root = sys.argv[1]
for app, pat in apps.items():
print('\n====', app, '====')
for s in scan_dir(os.path.join(root, app), pat)[:60]:
print(s)

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
import os
import re
import sys
PATTERNS = [
r'FirebaseMessagingService',
r'onMessageReceived',
r'NotificationManager',
r'NotificationCompat',
r'NotificationChannel',
r'PushNotification',
r'PushMessage',
r'Transaction',
r'transaction',
r'InboxMessage',
r'AlertMessage',
r'showNotification',
r'postNotification',
r'NotificationReceiver',
r'FCM',
r'FirebaseMessaging',
r'MessagingService',
r'PaymentNotification',
r'TransferNotification',
r'BankNotification',
]
CLASS_LIKE = re.compile(r'[A-Za-z][\w$/]{3,120}')
def extract_strings(data, min_len=4):
out = set()
for m in re.finditer(rb'[\x20-\x7e]{' + str(min_len).encode() + rb',}', data):
try:
out.add(m.group().decode('ascii'))
except Exception:
pass
return out
def scan_file(path):
with open(path, 'rb') as f:
data = f.read()
strings = extract_strings(data, 5)
hits = {}
for pat in PATTERNS:
rx = re.compile(pat, re.I)
matched = sorted({s for s in strings if rx.search(s)})
if matched:
hits[pat] = matched[:40]
# interesting fully-qualified class names
fqcn = sorted({
s for s in strings
if ('/' in s or s.startswith('L')) and any(k in s.lower() for k in (
'notif', 'push', 'fcm', 'firebase', 'message', 'transaction', 'alert', 'inbox', 'payment', 'transfer'
))
})
return hits, fqcn[:80]
def main(root):
for app in sorted(os.listdir(root)):
app_dir = os.path.join(root, app)
if not os.path.isdir(app_dir):
continue
print('\n' + '=' * 70)
print('APP:', app)
print('=' * 70)
dex_files = [f for f in os.listdir(app_dir) if f.endswith('.dex')]
all_hits = {}
all_fqcn = set()
for dex in sorted(dex_files):
path = os.path.join(app_dir, dex)
hits, fqcn = scan_file(path)
for k, v in hits.items():
all_hits.setdefault(k, set()).update(v)
all_fqcn.update(fqcn)
for pat in PATTERNS:
if pat in all_hits:
print('\n[%s]' % pat)
for s in sorted(all_hits[pat])[:25]:
print(' ', s)
print('\n[interesting class-like strings]')
for s in sorted(all_fqcn)[:60]:
print(' ', s)
if __name__ == '__main__':
root = sys.argv[1] if len(sys.argv) > 1 else 'extracted'
main(root)

View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
with zipfile.ZipFile(APK) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for m in re.finditer(rb"Lcom/shopee/[a-zA-Z0-9_$/]*(error|Error|dialog|Dialog|Risk|risk|Otp|otp|Phone|Register)[a-zA-Z0-9_$/]*;", data):
s = m.group().decode()[1:-1].replace("/", ".")
if "bke" in s or "seabank" in s or "shpssdk" in s:
print(s)

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
import zipfile
import struct
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
targets = [
b"Lcom/shopee/bke/biz/user/errorcodehandler/a;",
b"Lcom/shopee/bke/biz/user/errorcodehandler/b;",
b"Lcom/shopee/shpssdkbank/SPSAssessRisk;",
b"Lcom/shopee/bke/biz/user/rn/helper/ErrorFlowHelper;",
]
with zipfile.ZipFile(APK) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for t in targets:
print("===", t.decode()[1:-1].replace("/", "."), "===")
idx = 0
while True:
idx = data.find(t, idx)
if idx < 0:
break
ctx = data[max(0, idx - 200): idx + 400]
# crude string extraction nearby
for m in __import__("re").finditer(rb"[\x20-\x7e]{4,80}", ctx):
s = m.group().decode()
if any(k in s.lower() for k in ["1201", "error", "kill", "finish", "risk", "root", "code", "handle"]):
print(" ", s)
idx += 1

View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
needles = [b"An error has occurred", b"-1201", b"8424 8050", b"Error --", b"killProcess", b"finishAffinity"]
with zipfile.ZipFile(APK) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for n in needles:
idx = data.find(n)
if idx >= 0:
ctx = re.sub(rb"[^\x20-\x7e]+", b" ", data[max(0, idx - 60): idx + len(n) + 80])
print(n.decode(), "->", ctx.decode())

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
libs = [
b"com/google/gson/Gson",
b"com/fasterxml/jackson",
b"com/alibaba/fastjson",
b"org/json/JSONObject",
b"okhttp3/RequestBody",
b"retrofit2/",
]
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for lib in libs:
print(lib.decode(), data.count(lib))

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
METHOD_HINTS = [
b"isRoot", b"isRooted", b"jailbroken", b"checkRoot", b"detectRoot",
b"detectXposed", b"checkXposed", b"isXposed", b"checkFrida", b"isEmulator",
b"checkIntegrity", b"SafeMode", b"needSafeMode", b"enterSafeMode",
b"showRoot", b"rooted", b"factory settings",
]
with zipfile.ZipFile(APK) as zf:
for name in sorted(zf.namelist()):
if not name.endswith(".dex"):
continue
data = zf.read(name)
hits = [h.decode("ascii", "ignore") for h in METHOD_HINTS if h in data]
if hits:
print(name, ":", ", ".join(hits))

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
TARGETS = [b"isXposed", b"isRooted", b"checkRoot", b"isRoot", b"isEmulator", b"jailbroken"]
with zipfile.ZipFile(APK) as zf:
for dex_name in ["classes6.dex", "classes9.dex", "classes15.dex", "classes3.dex"]:
data = zf.read(dex_name)
print("=== %s ===" % dex_name)
for needle in TARGETS:
if needle not in data:
continue
idx = 0
shown = 0
while shown < 8:
i = data.find(needle, idx)
if i < 0:
break
s = max(0, i - 80)
e = min(len(data), i + 80)
chunk = re.sub(rb"[^\x20-\x7e]+", b" ", data[s:e]).decode("ascii", "ignore")
if "shopee" in chunk.lower() or "seabank" in chunk.lower() or "bke" in chunk.lower() or "alc" in chunk.lower():
print(" ", needle.decode(), "->", chunk.strip())
shown += 1
idx = i + 1
print()

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needles = [
b"GlobalAuthErrorImpl",
b"errorcodehandler",
b"sendOtp",
b"register",
b"verifyMobile",
b"mobile/register",
b"preRegister",
b"riskToken",
b"risk_token",
b"8424 8050",
b"-1201",
]
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for n in needles:
print(n.decode(), data.count(n))
print("\n--- urls ---")
for m in re.finditer(rb"https?://[a-zA-Z0-9._/-]{8,120}", data):
u = m.group().decode()
if "seabank" in u or "register" in u or "otp" in u or "mobile" in u:
print(u)

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needles = [
b"v2/register",
b"/register",
b"RegisterRequest",
b"registerPhone",
b"signUp",
b"preRegister",
b"riskToken",
]
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for n in needles:
c = data.count(n)
if c:
print(n.decode(), c)
print("\n--- classes near register ---")
for m in re.finditer(rb"Lcom/shopee/bke/[^;]{0,120}register[^;]{0,40};", data, re.I):
print(m.group().decode()[1:-1].replace("/", "."))

View File

@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needle = b"uapi/v2/register"
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
idx = data.find(needle)
print("found at", idx)
if idx >= 0:
ctx = data[max(0, idx - 400): idx + 400]
for m in re.finditer(rb"[\x20-\x7e]{4,120}", ctx):
print(" ", m.group().decode("latin1"))

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needles = [
b"mobile",
b"register",
b"otp",
b"signUp",
b"signup",
b"preCheck",
b"checkMobile",
b"sendSms",
b"verifyPhone",
b"4067004",
b"4067",
]
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for n in needles:
if n in data:
print("hit", n.decode())
print("\n--- api paths ---")
for m in re.finditer(rb"/v[0-9]/[a-zA-Z0-9_/-]{6,80}", data):
s = m.group().decode()
if any(k in s.lower() for k in ("user", "auth", "register", "mobile", "otp", "sign", "risk", "phone")):
print(s)

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
with zipfile.ZipFile(str(APK)) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for needle in [b"riskToken", b"risk_token", b"deviceToken", b"secToken", b"shpsToken", b"mobileNo", b"phoneNo"]:
print(needle.decode(), data.count(needle))
print("\n--- context riskToken ---")
idx = 0
while True:
idx = data.find(b"riskToken", idx)
if idx < 0:
break
ctx = data[max(0, idx - 60): idx + 120]
for m in re.finditer(rb"[\x20-\x7e]{3,60}", ctx):
s = m.group().decode("latin1")
if any(k in s.lower() for k in ["risk", "token", "mobile", "phone", "register", "device"]):
print(" ", s)
idx += 1
if idx > 5000000:
break

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
needle = b"riskToken"
with zipfile.ZipFile(str(APK)) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
idx = data.find(needle)
if idx < 0:
continue
ctx = data[max(0, idx - 300): idx + 300]
print("===", name, "===")
import re
for m in re.finditer(rb"[\x20-\x7e]{4,100}", ctx):
print(" ", m.group().decode("latin1"))

View File

@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
import re
import sys
import zipfile
KEYS = [
b"rooted", b"jailbroken", b"RootBeer", b"isRoot", b"checkRoot", b"detectRoot",
b"SafetyNet", b"PlayIntegrity", b"magisk", b"/su", b"tamper", b"safemode",
b"SafeMode", b"xposed", b"lsposed", b"frida", b"emulator", b"debuggable",
b"Integrity", b"jailbreak", b"factory settings", b"RiskDevice", b"DeviceRisk",
b"root device", b"seabank", b"SeaBank", b"MariBank", b"alc", b"ALC",
]
def scan_apk(apk_path):
with zipfile.ZipFile(apk_path) as zf:
for name in sorted(zf.namelist()):
if not name.endswith(".dex"):
continue
data = zf.read(name)
print("=== %s (%d bytes) ===" % (name, len(data)))
hits = set()
for key in KEYS:
start = 0
while True:
idx = data.find(key, start)
if idx < 0:
break
s = max(0, idx - 40)
e = min(len(data), idx + len(key) + 60)
chunk = re.sub(rb"[^\x20-\x7e]+", b" ", data[s:e])
chunk = chunk.decode("ascii", "ignore").strip()
if len(chunk) > 8:
hits.add(chunk)
start = idx + 1
for hit in sorted(hits):
print(" ", hit)
print()
if __name__ == "__main__":
scan_apk(sys.argv[1] if len(sys.argv) > 1 else "reverse/apks/seabank_ph_base.apk")

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
needles = [
b"rooted or jailbroken",
b"factory settings",
b"cannot be accessed",
b"bke_toast_not_support_root",
b"not_support_root",
]
with zipfile.ZipFile(APK) as zf:
for name in zf.namelist():
if not (name.endswith(".dex") or name.endswith(".xml") or name.endswith(".json")):
continue
data = zf.read(name)
for n in needles:
if n in data:
print(name, n.decode())

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
with zipfile.ZipFile(APK) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
if b"bke_toast_not_support_root" not in data:
continue
print("===", name, "===")
idx = data.find(b"bke_toast_not_support_root")
ctx = re.sub(rb"[^\x20-\x7e]+", b" ", data[max(0, idx - 120): idx + 200])
print(ctx.decode())
for m in re.finditer(rb"Lcom/shopee/bke[^;]{0,120};", data[max(0, idx - 800): idx + 800]):
s = m.group().decode()[1:-1].replace("/", ".")
if "dialog" in s.lower() or "root" in s.lower() or "safemode" in s.lower() or "risk" in s.lower() or "toast" in s.lower():
print(" ", s)

View File

@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
"""Scan dex class descriptors for Shopee/SeaBank security SDK."""
import re
import sys
import zipfile
TARGETS = (
"safemode",
"SafeMode",
"alc/",
"ALC",
"integrity",
"rooted",
"jailbroken",
"RootBeer",
"xposed",
"frida",
"isRoot",
"detectRoot",
)
def scan(apk_path):
with zipfile.ZipFile(apk_path) as zf:
for name in sorted(zf.namelist()):
if not name.endswith(".dex"):
continue
data = zf.read(name)
classes = set(re.findall(rb"L[a-zA-Z0-9_$/]+;", data))
hits = []
for raw in classes:
s = raw.decode("ascii", "ignore")
low = s.lower()
if any(t.lower() in low for t in TARGETS):
hits.append(s[1:-1].replace("/", "."))
if hits:
print("=== %s ===" % name)
for h in sorted(set(hits)):
print(h)
print()
if __name__ == "__main__":
scan(sys.argv[1])

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import re
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
with zipfile.ZipFile(str(APK)) as zf:
data = zf.read("classes11.dex")
for pat in [b"loadLibrary", b"libshpssdk", b"JNI_OnLoad", b"RegisterNatives", b"native "]:
print(pat.decode(), data.count(pat))
print("\n--- classes with shpssdkbank ---")
for m in re.finditer(rb"Lcom/shopee/shpssdkbank/[a-zA-Z0-9_$;/]+;", data):
s = m.group().decode()
if "uvu" in s or "SPS" in s or "SHPS" in s or "Native" in s:
if s not in []:
pass
classes = sorted(set(m.group().decode()[1:-1].replace("/", ".") for m in re.finditer(rb"Lcom/shopee/shpssdkbank/[a-zA-Z0-9_$]+;", data)))
for c in classes[:60]:
print(c)

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
classes = [
b"Lcom/shopee/shpssdk/SPSRiskTokenCallback;",
b"Lcom/shopee/shpssdkbank/SPSRiskTokenCallback;",
b"Lcom/shopee/shpssdk/SPSResultCallback;",
b"Lcom/shopee/shpssdkbank/SPSResultCallback;",
b"Lcom/shopee/shpssdk/SHPSSDK;",
b"Lcom/shopee/shpssdkbank/SPSAssessRisk;",
]
with zipfile.ZipFile(APK) as zf:
data = b"".join(zf.read(n) for n in zf.namelist() if n.endswith(".dex"))
for c in classes:
name = c.decode()[1:-1].replace("/", ".")
print("===", name, "===")
idx = 0
while True:
idx = data.find(c, idx)
if idx < 0:
break
ctx = data[max(0, idx - 150): idx + 400]
for m in re.finditer(rb"[a-zA-Z][a-zA-Z0-9_$]{2,40}", ctx):
s = m.group().decode()
if any(k in s.lower() for k in ["token", "risk", "result", "callback", "assess", "get", "on"]):
if len(s) > 4:
print(" ", s)
idx += 1
break

View File

@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
import re
import subprocess
import zipfile
from pathlib import Path
APK = Path(__file__).resolve().parent.parent / "apks" / "seabank_ph_base.apk"
DEXDUMP = Path(r"C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\37.0.0\dexdump.exe")
targets = [
"Lcom/shopee/shpssdkbank/SHPSSDK;",
"Lcom/shopee/shpssdk/SHPSSDK;",
]
with zipfile.ZipFile(str(APK)) as zf:
for dex_name in ["classes11.dex", "classes10.dex", "classes6.dex"]:
tmp = Path(__file__).resolve().parent.parent / "tmp" / "tmp_scan.dex"
tmp.write_bytes(zf.read(dex_name))
out = subprocess.check_output(
[str(DEXDUMP), "-d", str(tmp)], universal_newlines=True, errors="replace"
)
for target in targets:
capture = False
for line in out.splitlines():
if f"Class descriptor : '{target}'" in line:
capture = True
print("===", dex_name, target, "===")
elif capture and line.startswith(" Class descriptor"):
break
if capture and ("native" in line.lower() or "loadLibrary" in line
or "System" in line and "load" in line):
print(line.strip())
if capture and "name :" in line and "type :" in line:
pass
if capture and "access : 0x0101" in line or (
capture and "NATIVE" in line):
print(line.strip())
# also grep dex binary for loadLibrary strings near shpssdk
with zipfile.ZipFile(str(APK)) as zf:
data = zf.read("classes11.dex")
for m in re.finditer(rb"libshpssdk[^\x00]{0,40}", data):
print("str", m.group().decode("latin1"))

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
import re
import zipfile
APK = r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\seabank_ph_base.apk"
needles = [
b"SPSRiskTokenCallback",
b"SPSResultCallback",
b"SPSCallback",
b"getRiskToken",
b"riskToken",
b"RiskToken",
b"assessRisk",
b"AssessRisk",
]
with zipfile.ZipFile(APK) as zf:
for name in zf.namelist():
if not name.endswith(".dex"):
continue
data = zf.read(name)
hits = [n.decode() for n in needles if n in data]
if hits:
print(name, hits)

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import re
from pathlib import Path
native = Path(__file__).resolve().parent.parent / "extracted" / "native"
needles = [b"uvwuvwuv", b"NativeEncrypt", b"encryptByRSA", b"aesEncrypt"]
for so in sorted(native.glob("lib*.so")):
data = so.read_bytes()
hits = []
for n in needles:
if n in data:
hits.append(n.decode())
if not hits:
continue
print("\n===", so.name, hits, "===")
for m in sorted(set(re.findall(rb"Java_com_shopee_bke_[A-Za-z0-9_]+", data))):
s = m.decode()
if any(k in s.lower() for k in ("encrypt", "utils", "crypto", "jni")):
print(" ", s)

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
import os, re, sys
def extract_strings(data, min_len=5):
out = set()
for m in re.finditer(rb'[\x20-\x7e]{' + str(min_len).encode() + rb',}', data):
out.add(m.group().decode('ascii', 'ignore'))
return out
def scan_app(app_dir, filters):
strings = set()
for name in os.listdir(app_dir):
if not name.endswith('.dex'):
continue
with open(os.path.join(app_dir, name), 'rb') as f:
strings |= extract_strings(f.read())
print('\n===', os.path.basename(app_dir), '===')
for label, rx in filters:
matched = sorted({s for s in strings if re.search(rx, s, re.I)})
print('\n[%s] count=%d' % (label, len(matched)))
for s in matched[:50]:
print(' ', s)
filters = [
('up notifications', r'au\.com\.up\.money\.notifications|Lau/com/up/money/notifications'),
('suncorp messaging', r'au\.com\.suncorp\.marketplace.*(Messaging|Firebase|Notification|Push)'),
('ubank messaging', r'au\.com\.bank86400|bank86400|86400.*(Messaging|Firebase|Notification|Push|MoEngage)'),
('ubank onMessage', r'onMessageReceived|Will try to show push|MoEngage'),
('custom FCM services', r'MessagingService;|HandlerService|FirebaseService'),
]
root = sys.argv[1]
for app in sorted(os.listdir(root)):
p = os.path.join(root, app)
if os.path.isdir(p):
scan_app(p, filters)

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import re
from pathlib import Path
SO = Path(__file__).resolve().parent.parent / "extracted" / "native" / "libshpssdk_bank.so"
data = SO.read_bytes()
seen = set()
for m in re.finditer(rb"[\x20-\x7e]{3,}", data):
s = m.group().decode("latin1")
if s in seen or len(s) > 200:
continue
low = s.lower()
if any(k in low for k in [
"proc", "root", "hook", "xposed", "magisk", "frida", "emulator",
"risk", "token", "su", "debug", "maps", "version", "selinux",
"shpssdk", "detect", "jail", "integrity"
]):
seen.add(s)
print(s)