Created
May 27, 2020 01:14
-
-
Save physics-sec/ad39736f162a2a3984b10c20675afb5d to your computer and use it in GitHub Desktop.
Little Python3 script that decrypts and encrypts the JS files contained in the Android apps made with Appcelerator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# -*- coding: utf-8 -*- | |
import struct | |
from Crypto.Cipher import AES | |
def unpad(s): | |
if len(s) % 16 != 0: | |
raise Exception('Bad Padding!') | |
pad_len = s[-1] | |
if pad_len > 16 or pad_len < 1: | |
raise Exception('Bad Padding!') | |
for i in range(1, pad_len + 1): | |
if s[-i] != pad_len: | |
raise Exception('Bad Padding!') | |
return s[:-pad_len] | |
def pad(s, pad_len=16): | |
s_len = len(s) | |
resto = s_len % pad_len | |
b_len = pad_len - resto | |
pad = bytes([ b_len ]) * b_len | |
return s + pad | |
def xor(x1, x2): | |
assert len(x1) == len(x2) | |
r = b'' | |
for i in range(len(x1)): | |
r += bytes([ x1[i] ^ x2[i] ]) | |
return r | |
def encrypt_AES_ECB(plaintext, key): | |
assert len(key) == 128/8 | |
cipher = AES.new(key, AES.MODE_ECB) | |
assert len(plaintext) % 16 == 0 | |
ct = cipher.encrypt(plaintext) | |
return ct | |
def encrypt_AES_CBC(plaintext, key, iv): | |
n = 16 | |
assert len(key) == n | |
assert len(iv) == n | |
plaintext = pad(plaintext) | |
blocks = [plaintext[i:i+n] for i in range(0, len(plaintext), n)] | |
ct = b'' | |
previus_block = iv | |
for block in blocks: | |
block_x = xor(block, previus_block) | |
block_en = encrypt_AES_ECB(block_x, key) | |
previus_block = block_en | |
ct += block_en | |
return ct | |
def decrypt_AES_ECB(data, key): | |
assert len(key) == 128/8 | |
cipher = AES.new(key, AES.MODE_ECB) | |
pt = cipher.decrypt(data) | |
return pt | |
def decrypt_AES_CBC(ciphertext, key, iv): | |
n = 16 | |
assert len(key) == n | |
assert len(iv) == n | |
ciphertext_len = len(ciphertext) | |
assert ciphertext_len % n == 0 | |
blocks = [ciphertext[i:i+n] for i in range(0, ciphertext_len, n)] | |
pt = b'' | |
previus_block = iv | |
for block in blocks: | |
d_block = decrypt_AES_ECB(block, key) | |
pt_block = xor(d_block, previus_block) | |
pt += pt_block | |
previus_block = block | |
pt = unpad(pt) | |
return pt | |
def derive_key_iv(salt): | |
# $ md5sum libti.cloak.so | |
# 1f1eb9acca962d57f63d86ce1b476dec libti.cloak.so | |
# these values are hardcoded in libti.cloak.so | |
# if the library is different for you, decompile it with ghidra get the new values | |
xor_bytes = [0xfe, 0xe7, 0x32, 0x23, 0x42, 0x25, 0x71, 0x7f, 0xc0, 0x61, 0x07, 0xec, 0x35, 0x00, 0x09, 0xc7] | |
key = b'' | |
iv = b'' | |
for i in range(16): | |
byte = salt[i] | |
byte = struct.pack('b', byte) | |
iv += byte | |
byte = ord(byte) | |
byte = byte ^ xor_bytes[i] | |
key += bytes( [byte] ) | |
return (key, iv) | |
def main(): | |
fh = open('encrypted_js_file.bin', 'rb') | |
cipher_text = fh.read() | |
fh.close() | |
# hardcoded in AssetCryptImpl.java | |
# change this! | |
salt = [-51, 20, 45, -50, 61, 10, -51, -108, -100, 121, -55, -27, -83, -11, 59, 22] | |
key, iv = derive_key_iv(salt) | |
js_code = decrypt_AES_CBC(cipher_text, key, iv) | |
print(js_code.decode('utf-8')) | |
# modify js_code | |
cipher_text = encrypt_AES_CBC(js_code, key, iv) | |
fh = open('encrypted_js_file.bin', 'wb') | |
fh.write(cipher_text) | |
fh.close() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment