├── README.md └── dexconfusion.py /README.md: -------------------------------------------------------------------------------- 1 | # DexConfuse 2 | 简易dex混淆器 3 | 可以混淆类名,方法名和字段名 4 | -------------------------------------------------------------------------------- /dexconfusion.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Feb 17 16:42:59 2015 4 | 5 | @author: burningcodes 6 | """ 7 | 8 | import struct 9 | import binascii 10 | import hashlib 11 | import zlib 12 | import random 13 | 14 | class DexFile: 15 | def __init__(self,filepath): 16 | self.dex = open(filepath,"r+")#不能用rw ,同时读写要用r+ 17 | self.string_table = [] 18 | self.type_table = [] 19 | self.proto_table = [] 20 | self.field_table = [] 21 | self.method_table = [] 22 | self.map_type = {0x0:"header_item", 23 | 0x1:"string_id_item", 24 | 0x2:"type_id_item", 25 | 0x3:"proto_id_item", 26 | 0x4:"field_id_item", 27 | 0x5:"method_id_item", 28 | 0x6:"class_def_item", 29 | 0x1000:"map_list", 30 | 0x1001:"type_list", 31 | 0x1002:"annotation_set_ref_list", 32 | 0x1003:"annotation_set_item", 33 | 0x2000:"class_data_item", 34 | 0x2001:"code_item", 35 | 0x2002:"string_data_item", 36 | 0x2003:"debug_info_item", 37 | 0x2004:"annotation_item", 38 | 0x2005:"encoded_array_item", 39 | 0x2006:"annotations_directory_item"} 40 | 41 | self.randomstr = ["a","b","c","d","e","f","g","h","i","j",\ 42 | "k","l","m","n","o","p","q","r","s","t",\ 43 | "u","v","w","x","y","z",\ 44 | "A","B","C","D","E","F","G","H","I","J",\ 45 | "K","L","M","N","O","P","Q","R","S","T",\ 46 | "U","V","W","X","Y","Z",\ 47 | "0","1","2","3","4","5","6","7","8","9"]# 62 48 | 49 | self.usedstr = {} #混淆的字符串不能重复 用字典来判断 50 | 51 | 52 | def ReadDexHeader(self): 53 | print "----------------read Header-----------------------------" 54 | self.magic = struct.unpack("4s",self.dex.read(4))[0] 55 | print "magic code:",self.magic,#加逗号去掉最后的自动换行 56 | if self.magic != "dex\n": 57 | print "the file is not a dex file" 58 | self.CloseDexFile() 59 | exit(-1) 60 | return 61 | 62 | self.version = struct.unpack("4s",self.dex.read(4))[0] 63 | print "version:",self.version 64 | 65 | #用adler32算法对这个字段之后的所有数据做的检验码,来验证这个dex文件是否损坏 66 | self.checksum = struct.unpack("i",self.dex.read(4))[0] 67 | print "checksum:",self.checksum 68 | 69 | #dex文件的签名值(是对这个字段之后的所有数据做的签名,用来唯一的标识这个dex文件) 70 | self.sig = struct.unpack("20s",self.dex.read(20))[0] 71 | #将字符串以16进制打印出时用 binascii库 72 | print "signature:", str(binascii.b2a_hex(self.sig)) 73 | 74 | 75 | self.filesize = struct.unpack("I",self.dex.read(4))[0] 76 | print "filesize:",self.filesize 77 | 78 | self.headersize = struct.unpack("I",self.dex.read(4))[0] 79 | print "headersize:",self.headersize 80 | 81 | #默认情况下是以小顶端的方式存 小顶端时读出0x12345678,大顶端时读出0x78563412 82 | self.endiantag = struct.unpack("I",self.dex.read(4))[0] 83 | print "endiantag:",hex(self.endiantag) 84 | 85 | self.link_size = struct.unpack("I",self.dex.read(4))[0] 86 | print "link_size:",self.link_size 87 | 88 | self.link_off = struct.unpack("I",self.dex.read(4))[0] 89 | print "link_off:",self.link_off,"(",hex(self.link_off),")" 90 | 91 | self.map_off = struct.unpack("I",self.dex.read(4))[0] 92 | print "map_off:",self.map_off,"(",hex(self.map_off),")" 93 | 94 | self.string_num = struct.unpack("I",self.dex.read(4))[0] 95 | print "string_num:",self.string_num 96 | 97 | self.string_table_off = struct.unpack("I",self.dex.read(4))[0] 98 | print "string_table_off:",self.string_table_off,"(",hex(self.string_table_off),")" 99 | 100 | self.type_num = struct.unpack("I",self.dex.read(4))[0] 101 | print "type_num:",self.type_num 102 | 103 | self.type_table_off = struct.unpack("I",self.dex.read(4))[0] 104 | print "type_table_off:",self.type_table_off,"(",hex(self.type_table_off),")" 105 | 106 | self.proto_num = struct.unpack("I",self.dex.read(4))[0] 107 | print "proto_num:",self.proto_num 108 | 109 | self.proto_off = struct.unpack("I",self.dex.read(4))[0] 110 | print "proto_off:",self.proto_off,"(",hex(self.proto_off),")" 111 | 112 | self.field_num = struct.unpack("I",self.dex.read(4))[0] 113 | print "field_num:",self.field_num 114 | 115 | self.field_off = struct.unpack("I",self.dex.read(4))[0] 116 | print "field_off:",self.field_off,"(",hex(self.field_off),")" 117 | 118 | self.method_num = struct.unpack("I",self.dex.read(4))[0] 119 | print "method_num:",self.method_num 120 | 121 | self.method_off = struct.unpack("I",self.dex.read(4))[0] 122 | print "method_off:",self.method_off,"(",hex(self.method_off),")" 123 | 124 | self.class_def_size = struct.unpack("I",self.dex.read(4))[0] 125 | print "class_def_size:",self.class_def_size 126 | 127 | self.class_def_off = struct.unpack("I",self.dex.read(4))[0] 128 | print "class_def_off:",self.class_def_off,"(",hex(self.class_def_off),")" 129 | 130 | self.data_size = struct.unpack("I",self.dex.read(4))[0] 131 | print "data_size:",self.data_size 132 | 133 | self.data_off = struct.unpack("I",self.dex.read(4))[0] 134 | print "data_off:",self.data_off,"(",hex(self.data_off),")" 135 | 136 | 137 | 138 | 139 | def ReadStringTable(self): 140 | print "----------------read String-----------------------------" 141 | 142 | self.dex.seek(self.string_table_off,0) 143 | 144 | count = 0 145 | while(count < self.string_num): 146 | #unpack返回的是一个元组,所以要用[0]来取出元组中的第一个元素 147 | self.string_table.append(struct.unpack("I",self.dex.read(4))[0]) 148 | count+=1 149 | ############################## 150 | 151 | 152 | 153 | #type table的内容全都指向string table 154 | def ReadTypeTable(self): 155 | print "----------------read Type-----------------------------" 156 | 157 | count = 0 158 | self.dex.seek(self.type_table_off) 159 | while(count < self.type_num): 160 | self.type_table.append(struct.unpack("I",self.dex.read(4))[0]) 161 | count+=1 162 | 163 | 164 | 165 | #变量 166 | def ReadFieldsTable(self): 167 | print "----------------read Fields-----------------------------" 168 | count = 0 169 | self.dex.seek(self.field_off) 170 | print "Fields:",self.field_num 171 | while(count < self.field_num): 172 | class_idx,type_idx,name_idx = struct.unpack("HHI",self.dex.read(8)) 173 | 174 | self.field_table.append([class_idx,type_idx,name_idx]) 175 | count+=1 176 | 177 | #函数 178 | def ReadMethodTable(self): 179 | print "----------------read Method-----------------------------" 180 | count = 0 181 | self.dex.seek(self.method_off) 182 | print "Method:",self.method_num 183 | while(count < self.method_num): 184 | class_idx,proto_idx,name_idx = struct.unpack("HHI",self.dex.read(8)) 185 | 186 | self.method_table.append([class_idx,proto_idx,name_idx]) 187 | count+=1 188 | 189 | 190 | def ConfuseClass(self): 191 | count = 0 192 | 193 | print "----------------read Class-----------------------------" 194 | print "Class:",self.class_def_size 195 | while(count < self.class_def_size): 196 | self.dex.seek(self.class_def_off + count*32) 197 | 198 | class_idx,\ 199 | access_flags,\ 200 | superclass_idx,\ 201 | interfaces_off,\ 202 | source_file_idx,\ 203 | annotations_off,\ 204 | class_data_off,\ 205 | static_values_off = struct.unpack("IIIIIIII",self.dex.read(32)) 206 | #print "Class",count,self.types[class_idx] 207 | 208 | count+=1 209 | 210 | ###################### 211 | # 判断是否能被混淆 212 | ##################################### 213 | print class_idx 214 | print self.type_table[class_idx] 215 | classnameoff = self.string_table[self.type_table[class_idx]] 216 | self.dex.seek(classnameoff,0) 217 | strlen = self.DecUnsignedLEB128(self.dex) 218 | #用tell来获取当前位置 219 | classnameoff = self.dex.tell() 220 | 221 | #print "len:",strlen 222 | #读取不定长字符串 223 | classname = self.dex.read(strlen) 224 | 225 | 226 | if superclass_idx != 0xffffffff: 227 | #有父类 228 | superclassoff = self.string_table[self.type_table[superclass_idx]] 229 | self.dex.seek(superclassoff,0) 230 | strlen2 = self.DecUnsignedLEB128(self.dex) 231 | #print "super len:",strlen2 232 | superclass = self.dex.read(strlen2) 233 | else: 234 | #没有父类 235 | superclass = "" 236 | 237 | 238 | if self.ClassCanBeConfused(classname,superclass) == False: 239 | continue 240 | 241 | #从后面开始找/ 242 | idx = classname.rfind("/") 243 | print "classname",classname,"idx",idx 244 | #没找到说明是这样: Lsumclass; 这时idx指向s 即idx=1 245 | if idx == -1: 246 | idx = 1 247 | #print "ll:",strlen 248 | 249 | #比如 Lclassname/main; 从/之后的m到n 250 | strlen = strlen - idx - 2#包名不能被混淆 去除/和; 251 | #print "len",strlen 252 | #混淆类名 253 | self.DoConfuse(classnameoff + idx + 1,strlen)#/后面一个开始 254 | 255 | ###################################### 256 | 257 | 258 | 259 | 260 | 261 | #class_data_off为0表示此类没有类数据(是接口) 262 | if class_data_off == 0: 263 | continue 264 | #获取类数据,获取该类定义的方法和字段 265 | self.dex.seek(class_data_off) 266 | static_fields_size = self.DecUnsignedLEB128(self.dex) 267 | instance_fields_size = self.DecUnsignedLEB128(self.dex) 268 | direct_methods_size = self.DecUnsignedLEB128(self.dex) 269 | virtual_methods_size = self.DecUnsignedLEB128(self.dex) 270 | 271 | print "static_fields_size:",static_fields_size 272 | field_idx = 0 273 | while(static_fields_size > 0): 274 | field_idx_diff = self.DecUnsignedLEB128(self.dex) 275 | access_flags = self.DecUnsignedLEB128(self.dex) 276 | static_fields_size-=1 277 | savedaddr = self.dex.tell()#保存当前地址 278 | 279 | #取出field 280 | name_idx = self.field_table[field_idx + field_idx_diff][2] 281 | self.dex.seek(self.string_table[name_idx],0) 282 | strlen = self.DecUnsignedLEB128(self.dex) 283 | #用tell来获取当前位置 284 | fieldoff = self.dex.tell() 285 | 286 | #print "filed:",self.dex.read(strlen),"len:",strlen 287 | 288 | #混淆field 289 | self.DoConfuse(fieldoff,strlen) 290 | 291 | 292 | field_idx+=field_idx_diff 293 | 294 | self.dex.seek(savedaddr)#恢复地址 295 | 296 | #do sth 297 | 298 | field_idx = 0 299 | print "instance_fields_size",instance_fields_size 300 | while(instance_fields_size > 0): 301 | field_idx_diff = self.DecUnsignedLEB128(self.dex) 302 | access_flags = self.DecUnsignedLEB128(self.dex) 303 | instance_fields_size-=1 304 | 305 | savedaddr = self.dex.tell()#保存当前地址 306 | 307 | #取出field 308 | name_idx = self.field_table[field_idx + field_idx_diff][2] 309 | self.dex.seek(self.string_table[name_idx],0) 310 | strlen = self.DecUnsignedLEB128(self.dex) 311 | #用tell来获取当前位置 312 | fieldoff = self.dex.tell() 313 | #混淆field 314 | #print "filed:",self.dex.read(strlen),"len:",strlen 315 | self.DoConfuse(fieldoff,strlen) 316 | 317 | 318 | field_idx+=field_idx_diff 319 | 320 | self.dex.seek(savedaddr)#恢复地址 321 | 322 | #do sth 323 | 324 | 325 | method_idx = 0 326 | print "direct_methods_size",direct_methods_size 327 | while(direct_methods_size > 0): 328 | method_idx_diff = self.DecUnsignedLEB128(self.dex) 329 | access_flags = self.DecUnsignedLEB128(self.dex) 330 | code_off = self.DecUnsignedLEB128(self.dex) 331 | direct_methods_size-=1 332 | #第一次循环时method_idx_diff直接指向method table中对应的项 333 | #从第二次开始method_idx_diff就是与前一次的偏移值,方法在这个列表 334 | #中一定是递增的 335 | 336 | savedaddr = self.dex.tell()#保存当前地址 337 | 338 | #取出method 339 | name_idx = self.method_table[method_idx + method_idx_diff][2] 340 | self.dex.seek(self.string_table[name_idx],0) 341 | strlen = self.DecUnsignedLEB128(self.dex) 342 | #用tell来获取当前位置 343 | methodoff = self.dex.tell() 344 | #混淆field 345 | methodname = self.dex.read(strlen) 346 | print "filed:",methodname,"len:",strlen 347 | 348 | 349 | #不能混淆init函数 350 | if methodname != "": 351 | self.DoConfuse(methodoff,strlen) 352 | 353 | 354 | method_idx+=method_idx_diff 355 | 356 | self.dex.seek(savedaddr)#恢复地址 357 | 358 | 359 | 360 | method_idx = 0 361 | print "virtual_methods_size",virtual_methods_size 362 | while(virtual_methods_size > 0): 363 | method_idx_diff = self.DecUnsignedLEB128(self.dex) 364 | access_flags = self.DecUnsignedLEB128(self.dex) 365 | code_off = self.DecUnsignedLEB128(self.dex) 366 | virtual_methods_size-=1 367 | savedaddr = self.dex.tell()#保存当前地址 368 | 369 | #取出field 370 | name_idx = self.method_table[method_idx + method_idx_diff][2] 371 | self.dex.seek(self.string_table[name_idx],0) 372 | strlen = self.DecUnsignedLEB128(self.dex) 373 | #用tell来获取当前位置 374 | methodoff = self.dex.tell() 375 | #混淆field 376 | #print "filed:",self.dex.read(strlen),"len:",strlen 377 | self.DoConfuse(methodoff,strlen) 378 | 379 | 380 | method_idx+=method_idx_diff 381 | 382 | 383 | self.dex.seek(savedaddr)#恢复地址 384 | 385 | 386 | 387 | 388 | #判断这个类是否能被混淆 389 | def ClassCanBeConfused(self,classstr,superclassstr): 390 | #if superclassstr == "": 391 | # return True 392 | if classstr.find("$")!=-1: #不混淆内部类 393 | return False 394 | if classstr.find("Lcom/example/util/Signature")!=-1: 395 | return True 396 | 397 | return False 398 | 399 | 400 | def DoConfuse(self,addr,len): 401 | #print "addr",addr 402 | self.dex.seek(addr) 403 | str = self.GenRandomStr(len) 404 | self.dex.write(str) 405 | 406 | def GenRandomStr(self,len): 407 | 408 | while True: 409 | 410 | str = "" 411 | 412 | for i in range(len): 413 | num = random.randint(0,61) 414 | str = "".join([str,self.randomstr[num]]) 415 | print str 416 | 417 | if self.usedstr.has_key(str): #防止重复 418 | continue 419 | else: 420 | self.usedstr[str] = 1 421 | break 422 | 423 | return str 424 | 425 | def ReCalSignature(self): 426 | #4 + 4 + 4 + 20 427 | self.dex.seek(32) 428 | sigdata = self.dex.read() 429 | sha1 = hashlib.sha1() 430 | sha1.update(sigdata) 431 | print "signature:",sha1.hexdigest(),"\n",sha1.digest() 432 | 433 | self.dex.seek(12) 434 | self.dex.write(struct.pack("20s",sha1.digest())) 435 | 436 | 437 | def ReCalChecksum(self): 438 | self.dex.seek(12) 439 | checkdata = self.dex.read() 440 | checksum = zlib.adler32(checkdata) 441 | print "checksum:",checksum 442 | 443 | self.dex.seek(8) 444 | self.dex.write(struct.pack("i",checksum)) 445 | 446 | 447 | 448 | def DecUnsignedLEB128(self,file): 449 | result = struct.unpack("i", file.read(4))[0] 450 | result = result&0x000000ff 451 | file.seek(-3, 1) # 不能直接从1字节强转为4字节,所以先取4字节,再清空3字节 452 | if(result > 0x7f): 453 | next = struct.unpack("i", file.read(4))[0] 454 | next = next&0x000000ff 455 | file.seek(-3, 1) 456 | result = (result&0x7f) | (next&0x7f)<<7 457 | if(next > 0x7f): 458 | next = struct.unpack("i", file.read(4))[0] 459 | next = next&0x000000ff 460 | file.seek(-3, 1) 461 | result = result | (next&0x7f)<<14 462 | if(next > 0x7f): 463 | next = struct.unpack("i", file.read(4))[0] 464 | next = next&0x000000ff 465 | file.seek(-3, 1) 466 | result = result | (next&0x7f)<<21 467 | if(next > 0x7f): 468 | next = struct.unpack("i", file.read(4))[0] 469 | next = next&0x000000ff 470 | file.seek(-3, 1) 471 | result = result | next<<28 472 | 473 | #print "result:", result 474 | return result 475 | 476 | 477 | def CloseDexFile(self): 478 | self.dex.close() 479 | 480 | 481 | 482 | 483 | if __name__ == "__main__": 484 | df = DexFile("/home/cyg0810/crackdidiclasses.dex") 485 | df.ReadDexHeader() 486 | #df.CalSignature() 487 | #df.CalChecksum() 488 | 489 | df.ReadStringTable() 490 | # print "len :",len(df.strings),df.strings 491 | df.ReadTypeTable() 492 | 493 | df.ReadFieldsTable() 494 | df.ReadMethodTable() 495 | #print "string table:",len(df.string_table),df.string_table 496 | #print "type table",len(df.type_table),df.type_table 497 | #print "field table:",df.field_table 498 | #print "method table:",df.method_table 499 | 500 | #df.GenRandomStr(5) 501 | df.ConfuseClass() 502 | 503 | df.ReCalSignature() 504 | df.ReCalChecksum() 505 | #df.ReadClass() 506 | df.CloseDexFile() 507 | --------------------------------------------------------------------------------