def reversed_move_parameters_test(): print("\nRunning reversed move parameters test") print("\ttest/supportActivity_method.smali") scd = SmaliClassDef.SmaliClassDef("./test/supportActivity_method.smali") scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) scd.instrument() scd.write_to_file("./test/supportActivity_method_result.smali") # the bug here is that v0 was used for the tag propagation on # the initial parameter moves, but v0 was used on the second and # and therefore did not hold the correct value when it was used # later, by the original program fh = open("./test/supportActivity_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/supportActivity_method_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def on_nested_scrolling_parent_helper(): print("\nRunning copy1 v0<-v4") print("\ttest/onNestedScrollAccepted_method.smali") scd = SmaliClassDef.SmaliClassDef( "./test/onNestedScrollAccepted_method.smali") check_arg_method = scd.methods[0] #print("before growing: ", check_arg_method.get_register_meta_data()) scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) #print("after growing: ", check_arg_method.get_register_meta_data()) scd.instrument() scd.write_to_file("./test/onNestedScrollAccepted_method_result.smali") fh = open("./test/onNestedScrollAccepted_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/onNestedScrollAccepted_method_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def wide_register_has_type_long_string(): print("\nRunning wide register has type long/string") print("\ttest/checkArgumentInRange_method.smali") scd = SmaliClassDef.SmaliClassDef( "./test/checkArgumentInRange_method.smali") check_arg_method = scd.methods[0] #print("before growing: ", check_arg_method.get_register_meta_data()) scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) #print("after growing: ", check_arg_method.get_register_meta_data()) scd.instrument() scd.write_to_file("./test/checkArgumentInRange_method_result.smali") fh = open("./test/checkArgumentInRange_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/checkArgumentInRange_method_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def double_move_result_bug(): print( "\nRunning basic wholistic taint tracking instrumentation tests (double move result bug)..." ) print("\ttest/double_move_result_line.smali") scd = SmaliClassDef.SmaliClassDef("./test/double_move_result_line.smali") putExtraData_method = scd.methods[0] #print(str(putExtraData_method)) #print(putExtraData_method.get_locals_directive_line()) assert (str( putExtraData_method.get_locals_directive_line()) == ".locals 2") #print(putExtraData_method.get_register_meta_data()) assert (putExtraData_method.get_register_meta_data() == "['v0', 'v1', 'v2/p0', 'v3/p1']") scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) scd.instrument() scd.write_to_file("./test/double_move_result_line_result.smali") #print(str(putExtraData_method)) #print(putExtraData_method.get_locals_directive_line()) assert (str( putExtraData_method.get_locals_directive_line()) == ".locals 6") #print(putExtraData_method.get_register_meta_data()) assert (putExtraData_method.get_register_meta_data() == "['v0', 'v1', 'v2/p0', 'v3/p1', 'v4', 'v5', 'v6', 'v7']") result = open("./test/double_move_result_line_result.smali", "r").readlines() soln = open("./test/double_move_result_line_soln.smali", "r").readlines() assert (result == soln) print("passed!")
def on_start_intent_sender_from_fragment(): print("\nRunning copy v19<-v0 Imprecise Constant: -128") print("\ttest/onStartIntentSenderFromFragment_method.smali") scd = SmaliClassDef.SmaliClassDef( "./test/onStartIntentSenderFromFragment_method.smali") check_arg_method = scd.methods[0] #print("before growing: ", check_arg_method.get_register_meta_data()) scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) #print("after growing: ", check_arg_method.get_register_meta_data()) scd.instrument() scd.write_to_file( "./test/onStartIntentSenderFromFragment_method_result.smali") fh = open("./test/onStartIntentSenderFromFragment_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/onStartIntentSenderFromFragment_method_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def returning_uninitialized_object(): # returning uninitialized object # Uninitialized Reference: android.support.v4.app.FragmentManagerImpl$AnimationOrAnimator Allocation PC: 462 # there was an invoke-direct method dropped during instrumentation print("\nRunning returning uninitialized object") print("\ttest/loadAnimation.smali") # this method was truncated just after the instructions relevant # to the bug being fixed scd = SmaliClassDef.SmaliClassDef("./test/loadAnimation_method.smali") check_arg_method = scd.methods[0] #print("before growing: ", check_arg_method.get_register_meta_data()) scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) #print("after growing: ", check_arg_method.get_register_meta_data()) scd.instrument() scd.write_to_file("./test/loadAnimation_method_result.smali") fh = open("./test/loadAnimation_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/loadAnimation_method_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def tried_to_get_class_from_non_reference_register_v0(): # tried to get class from non-reference register v0 (type=Float) print( "\nRunning tried to get class from non-reference register v0 (type=Float)" ) print("\ttest/executeOpsTogether_method.smali") # this method was truncated just after the instructions relevant # to the bug being fixed scd = SmaliClassDef.SmaliClassDef( "./test/executeOpsTogether_method_truncated.smali") check_arg_method = scd.methods[0] #print("before growing: ", check_arg_method.get_register_meta_data()) scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) #print("after growing: ", check_arg_method.get_register_meta_data()) scd.instrument() scd.write_to_file("./test/executeOpsTogether_method_result.smali") fh = open("./test/executeOpsTogether_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/executeOpsTogether_method_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def goto_tracking_bug(): # [0xC3] copy1 v16<-v0 type=Reference: android.view.View cat=1 # something wrong with tracking the type of v0 through some # complex goto instructions print("\nRunning goto_tracking_bug") print("\ttest/findReferenceChild_method_minimal.smali") scd = SmaliClassDef.SmaliClassDef( "./test/findReferenceChild_method_minimal.smali") scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) scd.instrument() #scd.methods[0].cfg.show() scd.write_to_file("./test/findReferenceChild_method_minimal_result.smali") fh = open("./test/findReferenceChild_method_minimal_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/findReferenceChild_method_minimal_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def wide_register_index_out_of_range_bug_2(): print("\nRunning wide register index out of range bug 2") print("\ttest/makeOpenCloseAnimation_method.smali") scd = SmaliClassDef.SmaliClassDef( "./test/makeOpenCloseAnimation_method.smali") scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) scd.instrument() scd.write_to_file("./test/makeOpenCloseAnimation_method_result.smali") # the bug here is that v19 was used in the moves_before and moves_after # but it was used to store a wide value and the method does not allow # the user of v20 # .locals is 15 and there are 5 parameter registers (counting this) # so the method uses 20 registers total: v0 - v19 fh = open("./test/makeOpenCloseAnimation_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/makeOpenCloseAnimation_method_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def grow_locals_test_2(): print("\nRunning grow locals 2 test") print("\ttest/Main.smali") scd = SmaliClassDef.SmaliClassDef("./test/Main.smali") scd.grow_locals(3) scd.write_to_file("./test/Main_After.smali") print("passed!")
def type_safety_checker_leaks_test(): print("\nRunning type safety checker leaks test") method_text = open("./test/edge_case_method1.smali", "r").readlines() mock_class = SmaliClassDef.MockSmaliClassDef() smd = SmaliMethodDef.SmaliMethodDef(method_text, mock_class) #print(smd.get_num_registers()) assert (smd.get_num_registers() == 20) #print(Instrumenter.MAX_DESIRED_NUM_REGISTERS) assert (Instrumenter.MAX_DESIRED_NUM_REGISTERS == 4) smd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) smd.instrument() print("passed!")
def strange_insert_lines_at_beginning_placement(): print("\nRunning strange insert line placement test") print("\ttest/constructor_truncated.smali") # for some reason the IFT instructions added by stigma for method start # were displacing the .locals line? very strange scd = SmaliClassDef.SmaliClassDef("./test/constructor_truncated.smali") scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) scd.instrument() assert (scd.methods[0].get_locals_directive_num() == 20) print("passed!")
def runStigma(): print("Running Stigma") start_time = time.time() relevantFilePaths = getFiles() # getting list of all classes in this project class_names = [] for path in relevantFilePaths: class_names.append( SmaliClassDef.SmaliClassDef.extract_class_name(path)) print("...Instrumenting class files") counter = 1 comparison_instruction_count = 0 not_enough_registers_count = 0 total_files = len(class_names) for path in relevantFilePaths: #print("cur file path: " + str(name)) # parse file scd = SmaliClassDef.SmaliClassDef(path) scd.internal_class_names.extend(class_names) # analytics stuff comparison_instruction_count = comparison_instruction_count + scd.get_num_comparison_instructions( ) for every_method in scd.methods: not_enough_registers_count += every_method.not_enough_free_registers_count #Progress bar print(f'...{str(counter)}/{str(total_files)}', end='\r') counter += 1 # actual instrumentation scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) scd.instrument() scd.overwrite_to_file() analytics_path = os.path.join(temp_file.name, getNewAPKName() + "_analytics.dat") fh = open(analytics_path, "w") fh.write("Number of Comparisons: " + str(comparison_instruction_count) + "\n") fh.write( "Number of instructions in which there were not enough registers to properly instrument: " + str(comparison_instruction_count)) fh.close() print("Stigma ran in %.1f seconds" % (time.time() - start_time))
def type_safety_checker_aget2_test(): print("\nRunning aget2 test") fh = open("test/diffPartial_method.smali", "r") method_list = fh.readlines() fh.close() #print("Building SMD") mock_class = SmaliClassDef.MockSmaliClassDef() smd = SmaliMethodDef.SmaliMethodDef(method_list, mock_class) mock_class.methods.append(smd) #print(smd) #print("Instrumenting") assert (Instrumenter.MAX_DESIRED_NUM_REGISTERS == 4) smd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) mock_class.write_to_file("test/diffPartial_method_grown.smali") smd.instrument() print("passed!")
def register_listeners(): print("\nRunning missing move-result bug") print("\ttest/register_listeners_method.smali") # a move-result was dropped scd = SmaliClassDef.SmaliClassDef("./test/register_listeners_method.smali") scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) scd.instrument() scd.write_to_file("./test/register_listeners_method_result.smali") fh = open("./test/register_listeners_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/register_listeners_method_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def get_class_from_non_reference_register_bug(): print("\nRunning get class from non reference register bug") print("\ttest/endAnimatingAwayFragments_method.smali") scd = SmaliClassDef.SmaliClassDef( "./test/endAnimatingAwayFragments_method.smali") endAnimatingMethod = scd.methods[0] #print("before growth") #print(endAnimatingMethod) #print(endAnimatingMethod.get_register_meta_data()) #print("\n\n") scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) #print("after growth") #print(endAnimatingMethod) #print(endAnimatingMethod.get_register_meta_data()) scd.instrument() scd.write_to_file("./test/endAnimatingAwayFragments_method_result.smali") # there was a bug demonstrated by this method. The system was using # the registers as "free_regs" even if they were used on a subsequent # move-result-* instruction (re: invoke-* and filled-new-array operations) fh = open("./test/endAnimatingAwayFragments_method_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/endAnimatingAwayFragments_method_soln.smali", "r") soln = fh.readlines() fh.close() #print(soln) assert (result == soln) print("passed!")
def register_shuffling_test(): print("\nRunning register shuffling test") print("\ttest/custom_class.smali") scd = SmaliClassDef.SmaliClassDef("./test/custom_class.smali") made_up_method = scd.methods[0] #print(made_up_method.get_register_meta_data()) assert (made_up_method.get_register_meta_data() == str([ 'v0', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 'v8', 'v9', 'v10', 'v11', 'v12', 'v13', 'v14', 'v15', 'v16/p0', 'v17/p1', 'v18/p2', 'v19/p3' ])) scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) #print(made_up_method.get_register_meta_data()) assert (made_up_method.get_register_meta_data() == str([ 'v0', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 'v8', 'v9', 'v10', 'v11', 'v12', 'v13', 'v14', 'v15', 'v16/p0', 'v17/p1', 'v18/p2', 'v19/p3', 'v20', 'v21', 'v22', 'v23' ])) scd.instrument() scd.write_to_file("./test/custom_class_result.smali") fh = open("./test/custom_class_result.smali", "r") result = fh.readlines() fh.close() fh = open("./test/custom_class_soln.smali", "r") soln = fh.readlines() fh.close() assert (result == soln) print("passed!")
def stigma_leaks_crash_SupportActivity(): print("\nTesting grow() functionality...") scd = SmaliClassDef.SmaliClassDef("./test/SupportActivity.smali") scd.grow_locals(4) scd.write_to_file("./test/SupportActivity_After.smali") # Make sure locals cannot be grown twice smd = scd.methods[0] try: smd.grow_locals(4) assert (False) except ValueError: pass arbitrary_method = scd.methods[0] assert (arbitrary_method.has_grown == 4) #print(arbitrary_method.top_regs) assert (arbitrary_method.top_regs == ['v2', 'v3', 'v4', 'v5']) #print(arbitrary_method.first_new_free_reg_num) assert (arbitrary_method.first_new_free_reg_num == 2) print("passed!")
def wide_register_index_out_of_range_bug(): print("\nRunning wide register index out of range bug") print("\t(no instrumentation in this test)") # I think the bug is related to the move-wide for p3 (the second part of the last (wide) parameter) scd = SmaliClassDef.SmaliClassDef("./test/binarySearch_method.smali") binarySearchMethod = scd.methods[0] #print(binarySearchMethod.get_register_meta_data()) scd.grow_locals(Instrumenter.MAX_DESIRED_NUM_REGISTERS) #print(binarySearchMethod.get_register_meta_data()) scd.write_to_file("./test/binarySearch_method_result.smali") result_fh = open("./test/binarySearch_method_result.smali") result = result_fh.readlines() result_fh.close() solution_fh = open("./test/binarySearch_method_soln.smali") solution = solution_fh.readlines() solution_fh.close() assert (result == solution) print("passed!")
def type_saftey_checker_test3(): # there is a couple weird strings declared at the start of the A00 method scd = SmaliClassDef.SmaliClassDef("./test/0wH.smali")
def splitSmali(): print("Accounting For Constant Pool Limits") # There are separately enumerated and indexed constant pools for references to strings, types, fields, and methods. # https://source.android.com/devices/tech/dalvik/dalvik-bytecode # Our stigma instrumentation will add many fields and many classes # unfortunately, for a single smali_classesX folder there can only be # so many classes, so many fields in all the classes combined, # and so many methods in all classes combined . We typically break # those limits. So, we can resolve the issue by simply moving # some files to smali_classesX+1 # basic algorithm is to place all smali files into a list # regardless of existing folder location (smali_classesX) # go through the list linearly and count the number of fields # in the classes, methods in the classes, and total number of # classes. If any of those goes above THRESH # break the list at this point and designate all the files # until this point to be in smali_classesY (where Y increments) # each time the list is split up # # resultsLists = [file1, file2, file3] [file4, file5, file6, ...] # ^ # | # split point # (number of fields in file1, 2, 3, and 4 combined is > THRESH) # THRESH = 32768 # probably isn't the correct threshold # max unsigned short: 65535 # middle-ground: 16384 probably not relevant # max signed short: 32767 # max unsigned byte: 255 # max signed byte: 127 smaliFiles = getFiles() # see the instructions that correspond to the 4 different # countable things: type_id, string_id, field_id, # http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html # method_id does not exist in that file. BUT, the totalFieldCount = 0 totalMethodCount = 0 # missing: strings, classes resultLists = [] s = 0 e = 0 for idx, smaliFile in enumerate(smaliFiles): #print("file: " + str(smaliFile)) scd = SmaliClassDef.SmaliClassDef(smaliFile) field_num = scd.get_num_field_declarations( ) + scd.get_num_field_references() method_num = scd.get_num_method_declarations( ) + scd.get_num_method_references() if (method_num > THRESH): # https://github.com/JesusFreke/smali/issues/301 raise RuntimeError("methods in " + str(smaliFile) + " is greater than threshold. " + str(method_num) + ">" + str(THRESH)) if (field_num > THRESH): # https://github.com/JesusFreke/smali/issues/301 raise RuntimeError("methods in " + str(smaliFile) + " is greater than threshold. " + str(method_num) + ">" + str(THRESH)) if ((totalFieldCount + field_num >= THRESH) or ((totalMethodCount + method_num) >= THRESH)): if ((totalMethodCount + method_num >= THRESH)): print( " ...adjusting to avoid method threshold..." ) #at:" + str(scd) + ": " + str(totalMethodCount) + "->" + str(totalMethodCount + method_num)) if ((totalFieldCount + field_num >= THRESH)): print( " ...adjusting to avoid field threshold..." ) #at:" + str(scd) + ": " + str(totalFieldCount) + "->" + str(totalFieldCount + field_num)) # do a break e = idx resultLists.append(smaliFiles[s:e]) s = e # technical detail that's easy to miss / confuse # the file that broke the threshold # goes into the NEXT list totalFieldCount = field_num totalMethodCount = method_num else: totalFieldCount += field_num totalMethodCount += method_num print("...Re-arranging files") #print(str(len(resultLists)) + " groups") for idx, group in enumerate(resultLists): path = os.path.join(temp_file.name, "smali/") if (idx > 0): path = os.path.join(temp_file.name, "smali_classes" + str(idx + 1) + "/") os.makedirs(path, exist_ok=True) for smaliFile in group: newFolderPath = path + extractPathParts(smaliFile, 4, -1) #print("newFolderPath: " + str(newFolderPath)) #print("newFolderPath: " + newFolderPath) os.makedirs(newFolderPath, exist_ok=True) newFileAbsPath = os.path.join(newFolderPath, os.path.basename(smaliFile)) # Some characters need to be # escaped in bash shell. For example # smali_classes2/edu/fandm/enovak/leaks/Main$1.smali # the $1 will be treated like a variable in bash unless # it is escaped or wrapped in quotes # refactored according to https://docs.python.org/3/library/subprocess.html # shell = true means things like "*" and "~" will be expanded in the shell # I DID NOT include shell=true, which means that shell=false. # This means that single-quotes or escaping $ is unnecessary. # sometimes the system determines that a file should result # in the place in which it already resides (smali_classes2 -> smali_classes2) # in those cases we don't want to do any mv at all. Even with -n # mv will exit with an error, status 1 if (smaliFile != newFileAbsPath): os.rename(smaliFile, newFileAbsPath)