Skip to content

Instantly share code, notes, and snippets.

@zeta709
Last active November 20, 2022 21:00
Show Gist options
  • Save zeta709/a04495a4b3235a6c6f82 to your computer and use it in GitHub Desktop.
Save zeta709/a04495a4b3235a6c6f82 to your computer and use it in GitHub Desktop.
Battle.net Authenticator (Android) to TOTP
#!/usr/bin/env python
# Python port of https://gist.github.com/stbuehler/8616943
# Disclaimer
# There is absolutely no guarantee.
#
# Guide
# 0. Install Android SDK and Android Backup Extractor
# 1. Backup Battle.net Authenticator
# adb backup com.blizard.bma -f bma.ab
# 2. Extract files
# java -jar abe.jar unpack bma.ab bma.tar
# tar -xf bma.tar
# 3. Open a file named "com.blizzard.bma.AUTH_STORE.xml"
# 4. Get the value of "com.blizzard.bma.AUTH_STORE.HASH"
# 5. Use this tool to get the secret key
# 6. (Optional) Use any QR code generator
# (Offline QR code generators are recommended)
# 7. Use RFC6238-compliant TOTP application with the result
# (algorithm=SHA1, digits=8, period=30)
import sys
import base64
def bma_decode(bma_code, account_name):
# Since the original ruby code does not specify where the mask come from,
# some research(?) has been executed.
# The mask in hex string can be found in the following post:
# Quote: http://forum.xda-developers.com/showpost.php?p=7303107&postcount=94
# > "398e27fc50276a656065b0e525f4c06c04c61075286b8e7aeda59da98"
# > "13b5dd6c80d2fb38068773fa59ba47c17ca6c6479015c1d5b8b8f6b9a"
# Let this hex string be mask_hex and get the mask with the following code:
# mask = [ord(x) for x in mask_hex.decode("hex")]
mask = [57,142,39,252,80,39,106,101,
96,101,176,229,37,244,192,108,
4,198,16,117,40,107,142,122,
237,165,157,169,129,59,93,214,
200,13,47,179,128,104,119,63,
165,155,164,124,23,202,108,100,
121,1,92,29,91,139,143,107,
154]
x = bma_code.decode("hex")
y = "".join(chr(ord(x) ^ y) for x, y in zip(x, mask))
secret_hex = y[0:40]
secret = base64.b32encode(y[0:40].decode("hex"))
serial = y[40:]
print("secret (hex):", secret_hex)
print("secret:", secret)
print("serial:", serial)
print("otpauth://totp/Battle.net:{}?secret={}&issuer=Battle.net&digits=8".
format(account_name, secret))
def main():
print("Enter bma_code: ")
bma_code = sys.stdin.readline().strip()
print("Enter account name: ")
account_name = sys.stdin.readline().strip()
bma_decode(bma_code, account_name)
if __name__ == "__main__":
main()
--- bma_decode.py 2014-11-16 01:19:40.239176666 +0900
+++ bma_decode.py 2014-11-16 01:35:01.953837454 +0900
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Python port of https://gist.github.com/stbuehler/8616943
# Guide
@@ -36,12 +36,14 @@
165,155,164,124,23,202,108,100,
121,1,92,29,91,139,143,107,
154]
- x = bma_code.decode("hex")
- y = "".join(chr(ord(x) ^ y) for x, y in zip(x, mask))
+ x = base64.b16decode(bytes(bma_code, "utf-8"), True)
+ y = "".join(chr(x ^ y) for x, y in zip(x, mask))
- secret_hex = y[0:40]
- secret = base64.b32encode(y[0:40].decode("hex"))
+ secret_hex = bytes(y[0:40], "utf-8")
+ secret = base64.b32encode(base64.b16decode(secret_hex, True))
serial = y[40:]
+ secret_hex = secret_hex.decode("utf-8")
+ secret = secret.decode("utf-8")
print("secret (hex):", secret_hex)
print("secret:", secret)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment