|
5 | 5 | import sys, os, string |
6 | 6 | import urllib.request |
7 | 7 | import zipfile |
8 | | -from subprocess import call |
| 8 | +import subprocess |
9 | 9 | from optparse import OptionParser |
10 | 10 |
|
11 | | -apk_file='' |
12 | | -apk_folder='' |
13 | | -project_name='' |
14 | | -sign_file='' |
15 | | -cwd=os.path.dirname(os.path.abspath(__file__)) |
16 | | -home=os.path.dirname(os.path.realpath(sys.argv[0])) |
17 | | -outdir=os.path.dirname(os.path.realpath(sys.argv[1])) |
18 | | -external="https://github.com/TheZ3ro/apk2java-linux/releases/download/tool/tool.zip" |
| 11 | + |
| 12 | +def call(cmd, **kwargs): |
| 13 | + print('Running: {0}'.format(' '.join(cmd))) |
| 14 | + return subprocess.call(cmd, **kwargs) |
| 15 | + |
| 16 | + |
| 17 | +apk_file = '' |
| 18 | +apk_folder = '' |
| 19 | +project_name = '' |
| 20 | +sign_file = '' |
| 21 | +cwd = os.path.dirname(os.path.abspath(__file__)) |
| 22 | +home = os.path.dirname(os.path.realpath(sys.argv[0])) |
| 23 | +outdir = os.path.dirname(os.path.realpath(sys.argv[1])) |
| 24 | +external = "https://github.com/TheZ3ro/apk2java-linux/releases/download/tool/tool.zip" |
| 25 | + |
19 | 26 |
|
20 | 27 | def check_home(path): |
21 | | - return os.path.isdir(path+"/tool") |
| 28 | + return os.path.isdir(path + "/tool") |
| 29 | + |
22 | 30 |
|
23 | 31 | def getunzipped(theurl, thedir, report): |
24 | | - if not os.path.exists(thedir): |
25 | | - os.mkdir(thedir) |
26 | | - print ("Downloading external tool... -> "+thedir+"/tool/") |
27 | | - name = os.path.join(thedir, 'temp.zip') |
28 | | - try: |
29 | | - name, hdrs = urllib.request.urlretrieve(theurl, name, report) |
30 | | - except IOError as e: |
31 | | - print ("Can't retrieve %r to %r: %s" % (theurl, thedir, e)) |
32 | | - return |
33 | | - try: |
34 | | - z = zipfile.ZipFile(name) |
35 | | - except zipfile.error as e: |
36 | | - print ("Bad zipfile (from %r): %s" % (theurl, e)) |
37 | | - return |
38 | | - for n in z.namelist(): |
39 | | - (dirname, filename) = os.path.split(n) |
40 | | - perm = ((z.getinfo(n).external_attr >> 16) & 0x0777) |
41 | | - if filename == '': |
42 | | - # directory |
43 | | - newdir = thedir + '/' + dirname |
44 | | - if not os.path.exists(newdir): |
45 | | - os.mkdir(newdir) |
46 | | - else: |
47 | | - # file |
48 | | - fd = os.open(thedir+"/"+n, os.O_CREAT | os.O_WRONLY, perm) |
49 | | - os.write(fd, z.read(n)) |
50 | | - os.close(fd) |
51 | | - z.close() |
52 | | - os.unlink(name) |
53 | | - print ("") |
| 32 | + if not os.path.exists(thedir): |
| 33 | + os.mkdir(thedir) |
| 34 | + print("Downloading external tool... -> " + thedir + "/tool/") |
| 35 | + name = os.path.join(thedir, 'temp.zip') |
| 36 | + try: |
| 37 | + name, hdrs = urllib.request.urlretrieve(theurl, name, report) |
| 38 | + except IOError as e: |
| 39 | + print("Can't retrieve %r to %r: %s" % (theurl, thedir, e)) |
| 40 | + return |
| 41 | + try: |
| 42 | + z = zipfile.ZipFile(name) |
| 43 | + except zipfile.error as e: |
| 44 | + print("Bad zipfile (from %r): %s" % (theurl, e)) |
| 45 | + return |
| 46 | + for n in z.namelist(): |
| 47 | + (dirname, filename) = os.path.split(n) |
| 48 | + perm = ((z.getinfo(n).external_attr >> 16) & 0x0777) |
| 49 | + if filename == '': |
| 50 | + # directory |
| 51 | + newdir = thedir + '/' + dirname |
| 52 | + if not os.path.exists(newdir): |
| 53 | + os.mkdir(newdir) |
| 54 | + else: |
| 55 | + # file |
| 56 | + fd = os.open(thedir + "/" + n, os.O_CREAT | os.O_WRONLY, perm) |
| 57 | + os.write(fd, z.read(n)) |
| 58 | + os.close(fd) |
| 59 | + z.close() |
| 60 | + os.unlink(name) |
| 61 | + print("") |
| 62 | + |
54 | 63 |
|
55 | 64 | def report(blocknr, blocksize, size): |
56 | | - current = blocknr*blocksize |
57 | | - sys.stdout.write("\rProgress: {0:.2f}%".format(100.0*current/size)+" - {0:.1f} MB".format(current/1024/1024)+"/{0:.1f} MB".format(size/1024/1024)) |
| 65 | + current = blocknr * blocksize |
| 66 | + sys.stdout.write("\rProgress: {0:.2f}%".format(100.0 * current / size) + " - {0:.1f} MB".format( |
| 67 | + current / 1024 / 1024) + "/{0:.1f} MB".format(size / 1024 / 1024)) |
| 68 | + |
| 69 | + |
| 70 | +def print_header(text): |
| 71 | + block = "*********************************************" |
| 72 | + print(block) |
| 73 | + print('**' + text.center(len(block) - 4) + '**') |
| 74 | + print(block) |
| 75 | + |
58 | 76 |
|
59 | 77 | def apktool(smali): |
60 | | - print ("*********************************************") |
61 | | - print ("** Extract, fix resource files **") |
62 | | - print ("*********************************************") |
63 | | - if apk_file != '': |
64 | | - if smali == True: |
65 | | - call(home+'/tool/apktool_200rc3.jar d '+apk_file+' -o '+outdir+project_name+' -f',shell=True) |
66 | | - else: |
67 | | - call(home+'/tool/apktool_200rc3.jar d '+apk_file+' -o '+outdir+project_name+' -sf',shell=True) |
68 | | - os.system('mv %s %s' % (outdir+project_name+'/classes.dex', outdir+project_name+'/original/')) |
69 | | - print ('Done') |
| 78 | + print_header('Extract, fix resource files') |
| 79 | + if apk_file != '': |
| 80 | + cmd = [home + '/tool/apktool_200rc3.jar', 'd', apk_file, '-o', outdir + project_name, '-f'] |
| 81 | + |
| 82 | + if smali: |
| 83 | + cmd[-1] = '-sf' |
| 84 | + |
| 85 | + call(cmd) |
| 86 | + call(['mv', outdir + project_name + '/classes.dex', outdir + project_name + '/original/']) |
| 87 | + print('Done') |
| 88 | + |
70 | 89 |
|
71 | 90 | def dex2jar(): |
72 | | - print ("*********************************************") |
73 | | - print ("** Convert 'apk' to 'jar' **") |
74 | | - print ("*********************************************") |
75 | | - if apk_file != '': |
76 | | - call(home+'/tool/dex2jar-0.0.9.15/d2j-dex2jar.sh -f -o '+outdir+project_name+'.jar '+apk_file, shell=True) |
77 | | - call(home+'/tool/dex2jar-0.0.9.15/d2j-asm-verify.sh '+outdir+project_name+'.jar',shell=True) |
78 | | - print ('Done') |
| 91 | + print_header("Convert 'apk' to 'jar'") |
| 92 | + if apk_file != '': |
| 93 | + call([home + '/tool/dex2jar-0.0.9.15/d2j-dex2jar.sh', '-f', '-o', outdir + project_name + '.jar', apk_file]) |
| 94 | + call([home + '/tool/dex2jar-0.0.9.15/d2j-asm-verify.sh', outdir + project_name + '.jar']) |
| 95 | + print('Done') |
| 96 | + |
79 | 97 |
|
80 | 98 | def procyon(): |
81 | | - print ("*********************************************") |
82 | | - print ("** Decompiling class files **") |
83 | | - print ("*********************************************") |
84 | | - if apk_file != '': |
85 | | - call(home+'/tool/procyon-decompiler-0528.jar -jar '+outdir+project_name+'.jar -o '+outdir+project_name+'/src/',shell=True) |
86 | | - print ('Done') |
| 99 | + print_header('Decompiling class files') |
| 100 | + if apk_file != '': |
| 101 | + call([home + '/tool/procyon-decompiler-0528.jar', '-jar', outdir + project_name + '.jar', |
| 102 | + '-o', outdir + project_name + '/src/']) |
| 103 | + print('Done') |
| 104 | + |
87 | 105 |
|
88 | 106 | def apktool_build(): |
89 | | - print ("*********************************************") |
90 | | - print ("** Building apk from smali **") |
91 | | - print ("*********************************************") |
92 | | - if apk_folder != '': |
93 | | - call(home+'/tool/apktool_200rc3.jar b '+apk_folder+' -o '+outdir+project_name+'-rebuild.apk',shell=True) |
94 | | - global sign_file |
95 | | - sign_file = outdir+project_name+'-rebuild.apk' |
96 | | - print ('Done') |
| 107 | + print_header('Building apk from smali') |
| 108 | + if apk_folder != '': |
| 109 | + call([home + '/tool/apktool_200rc3.jar', 'b', apk_folder, '-o', outdir + project_name + '-rebuild.apk']) |
| 110 | + global sign_file |
| 111 | + sign_file = outdir + project_name + '-rebuild.apk' |
| 112 | + print('Done') |
| 113 | + |
97 | 114 |
|
98 | 115 | def jar2jasmin(): |
99 | | - print ("*********************************************") |
100 | | - print ("** Convert 'jar' to 'jasmin' **") |
101 | | - print ("*********************************************") |
102 | | - if apk_file != '': |
103 | | - call(home+'/tool/dex2jar-0.0.9.15/d2j-jar2jasmin.sh -f -o '+outdir+project_name+'/jasmin '+outdir+project_name+'.jar',shell=True) |
104 | | - print ('Done') |
| 116 | + print_header("Convert 'jar' to 'jasmin'") |
| 117 | + if apk_file != '': |
| 118 | + call([home + '/tool/dex2jar-0.0.9.15/d2j-jar2jasmin.sh', '-f', '-o', outdir + project_name + '/jasmin', |
| 119 | + outdir + project_name + '.jar']) |
| 120 | + print('Done') |
| 121 | + |
105 | 122 |
|
106 | 123 | def jasmin_build(): |
107 | | - print ("*********************************************") |
108 | | - print ("** Build apk from jasmin **") |
109 | | - print ("*********************************************") |
110 | | - if apk_folder != '': |
111 | | - call(home+'/tool/dex2jar-0.0.9.15/d2j-jasmin2jar.sh -f -o '+outdir+project_name+'-new.jar '+outdir+project_name+'/jasmin',shell=True) |
112 | | - call(home+'/tool/dex2jar-0.0.9.15/d2j-asm-verify.sh '+outdir+project_name+'-new.jar',shell=True) |
113 | | - call(home+'/tool/dex2jar-0.0.9.15/d2j-jar2dex.sh -f -o '+outdir+project_name+'/classes.dex '+outdir+project_name+'-new.jar',shell=True) |
114 | | - call('zip -r '+outdir+project_name+'-new.apk -j '+outdir+project_name+'/classes.dex',shell=True) |
115 | | - global sign_file |
116 | | - sign_file = outdir+project_name+'-new.apk' |
117 | | - print ('Done') |
| 124 | + print_header('Build apk from jasmin') |
| 125 | + if apk_folder != '': |
| 126 | + call([home + '/tool/dex2jar-0.0.9.15/d2j-jasmin2jar.sh', '-f', '-o', outdir + project_name + '-new.jar', |
| 127 | + outdir + project_name + '/jasmin']) |
| 128 | + call([home + '/tool/dex2jar-0.0.9.15/d2j-asm-verify.sh', outdir + project_name + '-new.jar']) |
| 129 | + call([home + '/tool/dex2jar-0.0.9.15/d2j-jar2dex.sh', '-f', '-o', outdir + project_name + '/classes.dex', |
| 130 | + outdir + project_name + '-new.jar']) |
| 131 | + call(['zip', '-r ' + outdir + project_name + '-new.apk', '-j', outdir + project_name + '/classes.dex']) |
| 132 | + global sign_file |
| 133 | + sign_file = outdir + project_name + '-new.apk' |
| 134 | + print('Done') |
| 135 | + |
118 | 136 |
|
119 | 137 | def sign(): |
120 | | - print ("*********************************************") |
121 | | - print ("** Sign apk **") |
122 | | - print ("*********************************************") |
123 | | - call(home+'/tool/dex2jar-0.0.9.15/d2j-apk-sign.sh -f -o '+outdir+project_name+'-signed.apk '+sign_file,shell=True) |
124 | | - print ('Done') |
| 138 | + print_header('Sign apk') |
| 139 | + call([home + '/tool/dex2jar-0.0.9.15/d2j-apk-sign.sh', '-f', '-o', outdir + project_name + '-signed.apk', |
| 140 | + sign_file]) |
| 141 | + print('Done') |
| 142 | + |
125 | 143 |
|
126 | 144 | def main(): |
127 | | - global apk_folder,apk_file,project_name,home,outdir |
128 | | - usage = "usage: %prog action file [options]" |
129 | | - parser = OptionParser(usage=usage) |
130 | | - parser.add_option("--java",action="store_true", dest="java", default=True, help="select java source format [DEFAULT]") |
131 | | - parser.add_option("--smali",action="store_true", dest="smali", default=False, help="select smali source format") |
132 | | - parser.add_option("--jasmin",action="store_true", dest="jasmin", default=False, help="select jasmin source format") |
133 | | - parser.add_option("--no-source",action="store_true", dest="nosc", default=False, help="no source code generation") |
134 | | - parser.add_option("-o", dest="outdir", default=cwd+"/", help="specify the output directory " |
135 | | - +"(if not specified the decomipled version will be store in a folder in the script directory)") |
136 | | - (options, args) = parser.parse_args() |
137 | | - |
138 | | - if home == cwd+"/apk2java": |
139 | | - if check_home(home) == False: |
140 | | - getunzipped(external, home, report) |
141 | | - else: |
142 | | - if check_home(home) == False: |
143 | | - if check_home(cwd+"/apk2java") == False: |
144 | | - getunzipped(external, cwd+"/apk2java", report) |
145 | | - home = cwd+"/apk2java" |
146 | | - else: |
147 | | - home = cwd+"/apk2java" |
148 | | - outdir = options.outdir |
149 | | - |
150 | | - if (options.smali+options.jasmin+options.nosc) > 1: |
151 | | - print ("[ ERROR ] You can only select 1 source format --[smali/jasmin/java/no-source]") |
152 | | - exit(1) |
153 | | - if len(args)==2: |
154 | | - if args[0] == 'd': |
155 | | - if os.path.isfile(args[1]) and os.path.splitext(args[1])[-1].lower() == '.apk': |
156 | | - apk_file = args[1] |
157 | | - project_name = os.path.splitext(os.path.basename(args[1]))[0].lower() |
158 | | - if not os.path.exists(outdir): |
159 | | - os.makedirs(outdir) |
160 | | - call("cp "+apk_file+" "+outdir+project_name+"-new.apk",shell=True) |
161 | | - if options.jasmin == True: |
162 | | - dex2jar() |
163 | | - jar2jasmin() |
164 | | - else: |
165 | | - apktool(options.smali) |
166 | | - if options.smali == False and options.nosc == False: |
167 | | - dex2jar() |
168 | | - procyon() |
169 | | - call("cp "+tmp+project_name+"/ ./ -R",shell=True) |
170 | | - else: |
171 | | - print ("[ ERROR ] You must select a valid APK file!") |
| 145 | + global apk_folder, apk_file, project_name, home, outdir |
| 146 | + usage = "usage: %prog action file [options]" |
| 147 | + parser = OptionParser(usage=usage) |
| 148 | + parser.add_option("--java", action="store_true", dest="java", default=True, |
| 149 | + help="select java source format [DEFAULT]") |
| 150 | + parser.add_option("--smali", action="store_true", dest="smali", default=False, help="select smali source format") |
| 151 | + parser.add_option("--jasmin", action="store_true", dest="jasmin", default=False, help="select jasmin source format") |
| 152 | + parser.add_option("--no-source", action="store_true", dest="nosc", default=False, help="no source code generation") |
| 153 | + parser.add_option("-o", dest="outdir", default=cwd + "/", help="specify the output directory " |
| 154 | + + "(if not specified the decomipled version will be store in a folder in the script directory)") |
| 155 | + (options, args) = parser.parse_args() |
| 156 | + |
| 157 | + if home == cwd + "/apk2java": |
| 158 | + if check_home(home) == False: |
| 159 | + getunzipped(external, home, report) |
| 160 | + else: |
| 161 | + if check_home(home) == False: |
| 162 | + if check_home(cwd + "/apk2java") == False: |
| 163 | + getunzipped(external, cwd + "/apk2java", report) |
| 164 | + home = cwd + "/apk2java" |
| 165 | + else: |
| 166 | + home = cwd + "/apk2java" |
| 167 | + outdir = options.outdir |
| 168 | + |
| 169 | + if (options.smali + options.jasmin + options.nosc) > 1: |
| 170 | + print("[ ERROR ] You can only select 1 source format --[smali/jasmin/java/no-source]") |
172 | 171 | exit(1) |
173 | | - elif args[0] == 'b': |
174 | | - if os.path.isdir(args[1]): |
175 | | - apk_folder = args[1] |
176 | | - project_name = os.path.basename(os.path.dirname(args[1]).lower()) |
177 | | - if options.jasmin == True: |
178 | | - jasmin_build() |
179 | | - elif options.smali == True: |
180 | | - apktool_build() |
| 172 | + if len(args) == 2: |
| 173 | + if args[0] == 'd': |
| 174 | + if os.path.isfile(args[1]) and os.path.splitext(args[1])[-1].lower() == '.apk': |
| 175 | + apk_file = args[1] |
| 176 | + project_name = os.path.splitext(os.path.basename(args[1]))[0].lower() |
| 177 | + if not os.path.exists(outdir): |
| 178 | + os.makedirs(outdir) |
| 179 | + |
| 180 | + call(["cp", apk_file, outdir + project_name + "-new.apk"]) |
| 181 | + if options.jasmin == True: |
| 182 | + dex2jar() |
| 183 | + jar2jasmin() |
| 184 | + else: |
| 185 | + apktool(options.smali) |
| 186 | + if options.smali == False and options.nosc == False: |
| 187 | + dex2jar() |
| 188 | + procyon() |
| 189 | + call(["cp", tmp + project_name + "/", "./", "-R"]) |
| 190 | + else: |
| 191 | + print("[ ERROR ] You must select a valid APK file!") |
| 192 | + exit(1) |
| 193 | + elif args[0] == 'b': |
| 194 | + if os.path.isdir(args[1]): |
| 195 | + apk_folder = args[1] |
| 196 | + project_name = os.path.basename(os.path.dirname(args[1]).lower()) |
| 197 | + if options.jasmin == True: |
| 198 | + jasmin_build() |
| 199 | + elif options.smali == True: |
| 200 | + apktool_build() |
| 201 | + else: |
| 202 | + print("[ ERROR ] Can't build apk with that source format. Only Jasmin or Smali supported") |
| 203 | + sign() |
| 204 | + call(["cp", sign_file, "./", ]) |
181 | 205 | else: |
182 | | - print ("[ ERROR ] Can't build apk with that source format. Only Jasmin or Smali supported") |
183 | | - sign() |
184 | | - call("cp "+sign_file+" ./",shell=True) |
| 206 | + parser.error("action can be only 'b' (build) or 'd' (decompile)") |
185 | 207 | else: |
186 | | - parser.error("action can be only 'b' (build) or 'd' (decompile)") |
187 | | - else: |
188 | | - parser.print_help() |
| 208 | + parser.print_help() |
| 209 | + |
189 | 210 |
|
190 | 211 | # Script start Here |
191 | | -if __name__=="__main__": |
192 | | - main() |
| 212 | +if __name__ == "__main__": |
| 213 | + main() |
0 commit comments