Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- if you're reading this, you're probably attempting to reverse-engineer a certain mobile game's protocol. congratulations! you have just found a gold mine. I put this together over months worth of research i did to keep my bot updated.
- if you use this information wisely and put in some effort and research on your own you will be able to make your very own bot or client
- note that by the time you read this the keys will probably be outdated and you will have to extract them yourself
- uid = random 32-byte array/string
- initial_key = initial_string ^ initial_bytes
- (these are at the bottom, they differ between EN and JP and they often change with updates. especially initial_bytes)
- device_info looks something like this (I censored the values because I don't know if they can be linked back to me, you will have to sniff http traffic and get your exact values, especially the hashes)
- {
- "GreatStockOption":"...",
- "Hardware":"...",
- "adbEnbled":"...",
- "basePath":"...",
- "db_sha1":"...",
- "ro.build.fingerprint":"...",
- "ro.build.tags":"test-keys",
- "ro.build.version.release":"...",
- "ro.product.board":"...",
- "ro.product.brand":"...",
- "ro.product.device":"...",
- "ro.product.manufacturer":"...",
- "ro.product.model":"...",
- "ro.product.name":"...",
- "signature":"...",
- "SuspiciousElement":[]
- }
- device_data = base64(device_info)
- user, pass = your user/pass from the GameEngineActivity.xml or randomly generated if creating a new account
- authkey_json = {"1":"user","2":"passwd","3":"device_data"}
- (crypto is explained later, don't worry)
- authkey_data = base64(encrypt_aes_128_cbc(data=authkey_json, key=uid))
- authkey_token = base64(public_key_encrypt(uid))
- there's a bunch of http headers that you need to send and keep track of.
- http responses are gzipped.
- see https://github.com/twisteroidambassador/llsifclient . it's an outdated implementation that doesn't work anymore, but it has most of the required http headers
- additionally, every http request is signed with a HMAC SHA1 hash of the request body using session_key as the key. this hash is in the request header X-Message-Code
- POST /main.php/login/authkey
- {"dummy_token":"authkey_token","auth_data":"authkey_data"}
- response will look like
- {"response_data":{"authorize_token":"...","dummy_token":"..."},"release_info":[...],"status_code":200}
- session_key = uid ^ base64_decode(response_dummy_token)
- encrypted_user = base64(encrypt_aes_128_cbc(data=user, key=session_key))
- encrypted_pass = base64(encrypt_aes_128_cbc(data=pass, key=session_key))
- POST /main.php/login/login
- {"login_key":"encrypted_user","login_passwd":"encrypted_pass"}
- since 30.2 jp version started using more of the key shuffling functions hidden in the assets lua code. this results in endpoints such as lbonus using a different key for the HMAC SHA1 signature instead of session_key, usually generated by shuffling initial_string and initial_key and xoring them together (for example swapping the first and second half of initial_string and xoring that with initial_key)
- this is now also in the EN version since Client-Version 16.0.79 / Bundle-Version 6.0.2 (maybe even earlier, I haven't touched this in 3 months)
- ####################################################
- # crypto #
- ####################################################
- encrypt_aes_128_cbc cuts the key to the first 16 bytes and uses a random 16-byte IV. the result is iv followed by the encrypted data
- public_key_encrypt uses this public key
- -----BEGIN PUBLIC KEY-----
- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDnBh9E2yTc5p5ryLQi/9wcRHLj
- T14CsJ+gNCf2Yr1p0iQjc7LyOTyRTkxbR0VketWKy9rzVgHtzdH+3T8KlIIPGYc+
- GFSxpXqX0tS4rV2aRIftkse+xJzkY5OlxQvWfQU7anwUiuh6peLJgpoiloQ6KS6z
- LbYcBxRDVsUmYaFDhwIDAQAB
- -----END PUBLIC KEY-----
- with pkcs1 padding
- here's an example of the aforementioned crypto functions implemented in python 2.7 using m2crypto
- import os
- from M2Crypto import BIO, RSA, EVP
- def encrypt_aes_128_cbc(data, key):
- iv = os.urandom(16)
- cipher = EVP.Cipher(alg='aes_128_cbc', key=key[:16], iv=iv, op=1, padding=True)
- v = cipher.update(data)
- v = iv + v + cipher.final()
- return v
- def public_key_encrypt(data):
- bio = BIO.MemoryBuffer('''-----BEGIN PUBLIC KEY-----
- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDnBh9E2yTc5p5ryLQi/9wcRHLj
- T14CsJ+gNCf2Yr1p0iQjc7LyOTyRTkxbR0VketWKy9rzVgHtzdH+3T8KlIIPGYc+
- GFSxpXqX0tS4rV2aRIftkse+xJzkY5OlxQvWfQU7anwUiuh6peLJgpoiloQ6KS6z
- LbYcBxRDVsUmYaFDhwIDAQAB
- -----END PUBLIC KEY-----
- ''')
- pubkey = RSA.load_pub_key_bio(bio)
- return pubkey.public_encrypt(data, RSA.pkcs1_padding)
- example python implementation of the HMAC SHA1 hash for X-Message-Code
- import hmac
- import hashlib
- def gen_xmessagecode(data, key):
- try:
- hmacsha1 = hmac.new(key, digestmod='sha1')
- except AttributeError:
- hmacsha1 = hmac.new(key, digestmod=hashlib.sha1)
- hmacsha1.update(data)
- return hmacsha1.hexdigest()
- ####################################################
- # EN Client-Version 16.0.79 / Bundle-Version 6.0.2 #
- ####################################################
- host = "prod.en-lovelive.klabgames.net"
- initial_string = "vXT9PNz3SRyioCzMHnNcOuq3a6BqJD6C"
- initial_bytes = [ 32 37 37 34 62 39 65 37 39 61 62 31 36 65 35 35 30 35 66 39 65 62 36 62 66 38 34 61 31 39 65 61 ]
- lbonus_key:
- "HnNcOuq3a6BqJD6C" (2nd half of initial_string)
- XOR [ 32 37 37 34 62 39 65 37 39 61 62 31 36 65 35 35 ] (1st half of initial_bytes)
- = [ 7A 59 79 57 2D 4C 14 04 58 57 20 40 7C 21 03 76 ]
- "vXT9PNz3SRyioCzM" (1st half of initial_string)
- XOR [ 30 35 66 39 65 62 36 62 66 38 34 61 31 39 65 61 ] (2nd half of initial_bytes)
- = [ 46 6D 32 00 35 2C 4C 51 35 6A 4D 08 5E 7A 1F 2C ]
- join these together and you have
- lbonus_key = [ 7A 59 79 57 2D 4C 14 04 58 57 20 40 7C 21 03 76 46 6D 32 00 35 2C 4C 51 35 6A 4D 08 5E 7A 1F 2C ]
- ####################################################
- # JP Client-Version 34.2 / Bundle-Version 6.2 #
- ####################################################
- host = "prod-jp.lovelive.ge.klabgames.net"
- initial_string = "eit4Ahph4aiX4ohmephuobei6SooX9xo"
- initial_bytes = [ 30 38 64 65 37 32 38 31 64 32 30 64 63 33 66 62 61 35 31 39 30 63 63 64 30 64 61 32 39 36 32 32 ]
- lbonus_key:
- "ephuobei6SooX9xo" (2nd half of initial_string)
- XOR [ 30 38 64 65 37 32 38 31 64 32 30 64 63 33 66 62 ] (1st half of initial_bytes)
- = [ 55 48 0C 10 58 50 5D 58 52 61 5F 0B 3B 0A 1E 0D ]
- "eit4Ahph4aiX4ohm" (1st half of initial_string)
- XOR [ 61 35 31 39 30 63 63 64 30 64 61 32 39 36 32 32 ] (2nd half of initial_bytes)
- = [ 04 5C 45 0D 71 0B 13 0C 04 05 08 6A 0D 59 5A 5F ]
- join these together and you have
- lbonus_key = [ 55 48 0C 10 58 50 5D 58 52 61 5F 0B 3B 0A 1E 0D 04 5C 45 0D 71 0B 13 0C 04 05 08 6A 0D 59 5A 5F ]
Add Comment
Please, Sign In to add comment