Skip to content

Commit f04faa8

Browse files
authored
Add files via upload
npk decryption/decompression and compilation tool
1 parent c1a3580 commit f04faa8

9 files changed

+827
-0
lines changed

encode.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import rotor
2+
import marshal
3+
import zlib
4+
import sys
5+
6+
def init_rotor():
7+
asdf_dn = 'j2h56ogodh3se'
8+
asdf_dt = '=dziaq.'
9+
asdf_df = '|os=5v7!"-234'
10+
asdf_tm = asdf_dn * 4 + (asdf_dt + asdf_dn + asdf_df) * 5 + '!' + '#' + asdf_dt * 7 + asdf_df * 2 + '*' + '&' + "'"
11+
rot = rotor.newrotor(asdf_tm)
12+
return rot
13+
14+
def _reverse_string(s):
15+
l = list(s)
16+
l = map(lambda x: chr(ord(x) ^ 154), l[0:128]) + l[128:]
17+
l.reverse()
18+
return ''.join(l)
19+
20+
def reverse_string(s):
21+
l = list(s)
22+
l.reverse()
23+
sub_l = l[0:128]
24+
sub_l = map(lambda x: chr(ord(x) ^ 154), l[0:128])
25+
l = sub_l + l[128:]
26+
return ''.join(l)
27+
28+
def encode(data):
29+
rot = init_rotor()
30+
#print 'original data:'
31+
#print repr(data)
32+
data = reverse_string(data)
33+
#print '_reverse string data:'
34+
#print repr(data)
35+
data = zlib.compress(data, 9)
36+
#print 'zlib compressed data:'
37+
#print repr(data)
38+
data = rot.encrypt(data)
39+
#print 'rot encrypt data:'
40+
#print repr(data)
41+
return data
42+
43+
file = open(sys.argv[1], 'rb')
44+
save = open(sys.argv[2], 'wb')
45+
data = file.read()
46+
save.write(encode(data))
47+
file.close()
48+
save.close()

extract.py

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import struct
2+
import os
3+
import redirect
4+
import marshal
5+
import pyc_decryptor
6+
7+
print('Usage: unpack the apk file using npktool and execute this script. Scripts stored in decompile/python_file/pyc')
8+
9+
path = 'python_file'
10+
if not os.path.exists(path):
11+
os.makedirs(path)
12+
os.makedirs(path + '/pyc')
13+
14+
file = open('original_script.npk', 'rb')
15+
file_size = os.path.getsize('original_script.npk')
16+
file.seek(0x14)
17+
base, = struct.unpack('<L', bytes(file.read(4)))
18+
encryptor = pyc_decryptor.PYCEncryptor()
19+
while base < file_size:
20+
base += 0x4
21+
file.seek(base)
22+
py_addr, = struct.unpack('<L', bytes(file.read(4)))
23+
py_size_ptr = base + 0x8
24+
file.seek(py_size_ptr)
25+
py_size, = struct.unpack('<I', bytes(file.read(4)))
26+
file.seek(py_addr)
27+
28+
py_encrypted = file.read(py_size)
29+
try:
30+
pyc_original = redirect.decrypt(py_encrypted)
31+
except:
32+
print 'File at 0x%x failed to decrypt.' % py_addr
33+
34+
file_name = path + '/' + hex(py_addr)
35+
save_file = open(file_name, 'wb')
36+
marshal.dump(pyc_original, save_file)
37+
save_file.close()
38+
encryptor.decrypt_file(file_name, path + '/pyc/' + hex(py_addr) + '.pyc')
39+
base += 0x18
40+
41+
file.close()

npktool.py

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import os
2+
import sys
3+
import shutil
4+
import struct
5+
import marshal
6+
7+
def search_bytes(src, dat):
8+
t = len(src) - len(dat) + 1
9+
for i in range(t):
10+
ret = True
11+
for j in range(len(dat)):
12+
if src[i + j] != dat[j]:
13+
ret = False
14+
break
15+
if ret:
16+
return i
17+
return -1
18+
19+
def i2bytes(integer):
20+
l = []
21+
_ = integer
22+
for i in range(4):
23+
l.append(_ & 0xFF)
24+
_ = _ >> 8
25+
return bytes(bytearray(l))
26+
27+
if len(sys.argv) == 1:
28+
print('usage:')
29+
print('python npktool.py d filename.apk - unpack the apk file.')
30+
print('python npktool.py c file.py 00ABCDEF - compile file.py and insert it at offset 00ABCDEF in script.npk and build the APK.')
31+
print('python npktool.py b path file.apk - build the APK file and sign.')
32+
sys.exit()
33+
option = sys.argv[1]
34+
35+
if option == 'd':
36+
filename = ''.join(list(sys.argv[2])[:-4])
37+
if not os.path.exists(filename):
38+
os.system('apktool d ' + sys.argv[2])
39+
else:
40+
print('APK already disassembled.')
41+
shutil.copyfile(filename + '/assets/script.npk', 'original_script.npk')
42+
elif option == 'c':
43+
filename = sys.argv[2]
44+
offset, = struct.unpack('>L', bytearray.fromhex(sys.argv[3]))
45+
offset_dat = i2bytes(offset)
46+
47+
name = ''.join(list(filename)[:-3])
48+
os.system('python -m py_compile ' + filename)
49+
os.system('python pyc_encryptor.py ' + name + '.pyc ' + name + '.pycfix')
50+
os.system('python encode.py ' + name + '.pycfix ' + name + '.pyn')
51+
52+
bin_size = os.path.getsize(name + '.pyn')
53+
new_bin = open(name + '.pyn', 'rb')
54+
original_script = open('original_script.npk', 'rb')
55+
original_script.seek(0x14)
56+
table_addr, = struct.unpack('<L', bytes(original_script.read(4)))
57+
original_script.seek(table_addr)
58+
offset_table = original_script.read()
59+
index_in_table = search_bytes(offset_table, offset_dat)
60+
size_addr = []
61+
size_addr.append(index_in_table + table_addr + 4)
62+
size_addr.append(size_addr[0] + 4)
63+
original_script.seek(size_addr[0])
64+
old_size = original_script.read(4)
65+
old_size, = struct.unpack('<L', old_size)
66+
print ('original size: ' + repr(old_size))
67+
print ('new size: ' + repr(bin_size))
68+
original_script.seek(0)
69+
new_script_body = original_script.read(offset)
70+
new_script_body = new_script_body + new_bin.read()
71+
original_script.seek(offset + bin_size)
72+
new_script_body = new_script_body + original_script.read(table_addr - offset - bin_size)
73+
74+
new_bin_size_dat = i2bytes(bin_size)
75+
original_script.seek(table_addr)
76+
new_table = original_script.read(size_addr[0] - table_addr)
77+
new_table = new_table + new_bin_size_dat
78+
new_table = new_table + new_bin_size_dat
79+
original_script.seek(size_addr[1] + 4)
80+
new_table = new_table + original_script.read()
81+
82+
new_script_dat = new_script_body + new_table
83+
new_bin.close()
84+
original_script.close()
85+
new_script_file = open('new_script.npk', 'wb')
86+
new_script_file.write(new_script_dat)
87+
new_script_file.close()
88+
elif option == 'b':
89+
path = sys.argv[2]
90+
shutil.copyfile('new_script.npk', path + '/assets/script.npk')
91+
os.system('apktool b ' + path)
92+
os.system('java -jar signapk.jar testkey.x509.pem testkey.pk8 ' + path + '/dist/' + path + '.apk ' + sys.argv[3])

pyc_decryptor.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import os
2+
import zlib
3+
import rotor
4+
import marshal
5+
import binascii
6+
import argparse
7+
import pymarshal
8+
class PYCEncryptor(object):
9+
def __init__(self):
10+
self.opcode_encrypt_map = {
11+
1: 38, 2: 46, 3: 37, 4: 66, 5: 12, 10: 35, 11: 67, 12: 81, 13: 32, 15: 9, 19: 63, 20: 70,
12+
21: 44, 22: 36, 23: 39, 24: 57, 25: 10, 26: 52, 28: 49, 30: 86, 31: 87, 32: 88, 33: 89,
13+
40: 24, 41: 25, 42: 26, 43: 27, 50: 14, 51: 15, 52: 16, 53: 17, 54: 8, 55: 21, 56: 55,
14+
57: 82, 58: 34, 59: 22, 60: 65, 61: 6, 62: 58, 63: 71, 64: 43, 65: 30, 66: 19, 67: 5,
15+
68: 60, 71: 53, 72: 42, 73: 3, 74: 48, 75: 84, 76: 77, 77: 78, 78: 85, 79: 47, 80: 51,
16+
81: 54, 82: 50, 83: 83, 84: 74, 85: 64, 86: 31, 87: 72, 88: 45, 89: 33, 90: 145, 91: 159,
17+
92: 125, 93: 149, 94: 157, 95: 132, 96: 95, 97: 113, 98: 111, 99: 138, 100: 153, 101: 101,
18+
102: 135, 103: 90, 104: 99, 105: 151, 106: 96, 107: 114, 108: 134, 109: 116, 110: 156,
19+
111: 105, 112: 130, 113: 137, 114: 148, 115: 172, 116: 155, 119: 103, 120: 158, 121: 128,
20+
122: 110, 124: 97, 125: 104, 126: 118, 130: 93, 131: 131, 132: 136, 133: 115, 134: 100, 135: 120,
21+
136: 129, 137: 102, 140: 140, 141: 141, 142: 142, 143: 94, 146: 109, 147: 123
22+
}
23+
self.opcode_decrypt_map = {self.opcode_encrypt_map[key]: key for key in self.opcode_encrypt_map}
24+
self.pyc27_header = "\x03\xf3\x0d\x0a\x00\x00\x00\x00"
25+
def _decrypt_file(self, filename):
26+
os.path.splitext(filename)
27+
content = open(filename).read()
28+
try:
29+
m = pymarshal.loads(content)
30+
except:
31+
try:
32+
m = marshal.loads(content)
33+
except Exception as e:
34+
print("[!] error: %s" % str(e))
35+
return None
36+
return m.co_filename.replace('\\', '/'), pymarshal.dumps(m, self.opcode_decrypt_map)
37+
def decrypt_file(self, input_file, output_file=None):
38+
result = self._decrypt_file(input_file)
39+
if not result:
40+
return
41+
pyc_filename, pyc_content = result
42+
if not output_file:
43+
output_file = os.path.basename(pyc_filename) + '.pyc'
44+
with open(output_file, 'wb') as fd:
45+
fd.write(self.pyc27_header + pyc_content)
46+
def main():
47+
parser = argparse.ArgumentParser(description='onmyoji py decrypt tool')
48+
parser.add_argument("INPUT_NAME", help='input file')
49+
parser.add_argument("OUTPUT_NAME", help='output file')
50+
args = parser.parse_args()
51+
encryptor = PYCEncryptor()
52+
encryptor.decrypt_file(args.INPUT_NAME, args.OUTPUT_NAME)
53+
if __name__ == '__main__':
54+
main()

pyc_encryptor.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import os
2+
import zlib
3+
import rotor
4+
import marshal
5+
import binascii
6+
import argparse
7+
import pymarshal
8+
class PYCEncryptor(object):
9+
def __init__(self):
10+
self.opcode_encrypt_map = {
11+
38: 1, 46: 2, 37: 3, 66: 4, 12: 5, 35: 10, 67: 11, 81: 12, 32: 13,
12+
9: 15, 63: 19, 70: 20, 44: 21, 36: 22, 39: 23, 57: 24, 10: 25,
13+
52: 26, 49: 28, 86: 30, 87: 31, 88: 32, 89: 33, 24: 40, 25: 41,
14+
26: 42, 27: 43, 14: 50, 15: 51, 16: 52, 17: 53, 8: 54, 21: 55,
15+
55: 56, 82: 57, 34: 58, 22: 59, 65: 60, 6: 61, 58: 62, 71: 63,
16+
43: 64, 30: 65, 19: 66, 5: 67, 60: 68, 53: 71, 42: 72, 3: 73,
17+
48: 74, 84: 75, 77: 76, 78: 77, 85: 78, 47: 79, 51: 80, 54: 81,
18+
50: 82, 83: 83, 74: 84, 64: 85, 31: 86, 72: 87, 45: 88, 33: 89,
19+
145: 90, 159: 91, 125: 92, 149: 93, 157: 94, 132: 95, 95: 96, 113: 97,
20+
111: 98, 138: 99, 153: 100, 101: 101, 135: 102, 90: 103, 99: 104, 151: 105,
21+
96: 106, 114: 107, 134: 108, 116: 109, 156: 110, 105: 111, 130: 112, 137: 113,
22+
148: 114, 172: 115, 155: 116, 103: 119, 158: 120, 128: 121, 110: 122, 97: 124,
23+
104: 125, 118: 126, 93: 130, 131: 131, 136: 132, 115: 133, 100: 134, 120: 135,
24+
129: 136, 102: 137, 140: 140, 141: 141, 142: 142, 94: 143, 109: 146, 123: 147
25+
}
26+
self.opcode_decrypt_map = {self.opcode_encrypt_map[key]: key for key in self.opcode_encrypt_map}
27+
def _decrypt_file(self, filename):
28+
os.path.splitext(filename)
29+
file = open(filename)
30+
file.seek(8)
31+
content = file.read()
32+
try:
33+
m = pymarshal.loads(content)
34+
except:
35+
try:
36+
m = marshal.loads(content)
37+
except Exception as e:
38+
print("[!] error: %s" % str(e))
39+
return None
40+
return m.co_filename.replace('\\', '/'), pymarshal.dumps(m, self.opcode_decrypt_map)
41+
def decrypt_file(self, input_file, output_file=None):
42+
result = self._decrypt_file(input_file)
43+
if not result:
44+
return
45+
pyc_filename, pyc_content = result
46+
if not output_file:
47+
output_file = os.path.basename(pyc_filename) + '.pyc'
48+
with open(output_file, 'wb') as fd:
49+
fd.write(pyc_content)
50+
def main():
51+
parser = argparse.ArgumentParser(description='onmyoji py decrypt tool')
52+
parser.add_argument("INPUT_NAME", help='input file')
53+
parser.add_argument("OUTPUT_NAME", help='output file')
54+
args = parser.parse_args()
55+
encryptor = PYCEncryptor()
56+
encryptor.decrypt_file(args.INPUT_NAME, args.OUTPUT_NAME)
57+
if __name__ == '__main__':
58+
main()

0 commit comments

Comments
 (0)