Daniel Plohmann avatar Daniel Plohmann committed bb20612

integrated changes into src

Comments (0)

Files changed (4)


-<p class="version_info">[Release version: 1.0 - Date: 17.09.2012]</p>
+<p class="version_info">[Release version: 1.0a - Date: 18.09.2012]</p>
 <p>This is the guide to IDAscope, a plugin intended to ease reverse engineering with a focus on malware analysis. The idea for the plugin was born at <a href="http://recon.cx">RECON 2012</a> out of some prototype scripts. IDAscope is written in pure Python with a PySide based GUI.</p><p>The authors of IDAscope are:</p>
 <p>The Arit/Log Rating simply allows narrowing down the ratios to be considered. Raising this value will show only blocks with a high calculation portion but may miss some crypto candidates.</p>
 <p>The Basic Block size can be both used to exclude small and large blocks, thus aiming at typical sizes for certain algorithms.</p>
 <p>The number of allowed calls is counted per function instead of per basic block and turned out to be helpful in order to reduce false positives. By limiting this to a few calls (0, 1, 2), more or less self-contained functions can be selected.</p>
-<p>The check boxes next to the bars give additional methods to reduce false positives or increase the accessibility. "Exclude Zeroing" only considers instructions that are not used to clear a register, e.g. "xor eax, eax". "Group By Functions" changes the view from a basic block based view to a function based view.</p>
+<p>The check boxes next to the bars give additional methods to reduce false positives or increase the accessibility.
+<li>"Exclude Zeroing" only considers instructions that are not used to clear a register, e.g. "xor eax, eax".</li>
+<li>"Looped Blocks only" will reduce the display to blocks that are contained in loops.</li>
+<li>"Group By Functions" changes the view from a basic block based view to a function based view.</li>
 <p>A description of this widget is also covered in this <a href="http://pnx-tf.blogspot.de/2012/08/idascope-update-crypto-identification.html">blog post</a>.</p>


 from IdaProxy import IdaProxy
 from PatternManager import PatternManager
+from Tarjan import Tarjan
 from idascope.core.structures.Segment import Segment
 from idascope.core.structures.AritlogBasicBlock import AritlogBasicBlock
         print ("[|] loading CryptoIdentifier")
         self.time = time
         self.re = re
+        self.Tarjan = Tarjan
         self.CryptoSignatureHit = CryptoSignatureHit
         self.AritlogBasicBlock = AritlogBasicBlock
         self.Segment = Segment
             function_chart = self.ida_proxy.FlowChart(self.ida_proxy.get_func(function_ea))
             calls_in_function = 0
             function_blocks = []
+            function_dgraph = {}
+            blocks_in_loops = set()
             for current_block in function_chart:
                 block = self.AritlogBasicBlock(current_block.startEA, current_block.endEA)
                 for instruction in self.ida_proxy.Heads(block.start_ea, block.end_ea):
                         if mnemonic == "call":
                             calls_in_function += 1
+                # prepare graph for Tarjan's algorithm
+                succeeding_blocks = [succ.startEA for succ in current_block.succs()]
+                function_dgraph[current_block.startEA] = succeeding_blocks
+                # add trivial loops
+                if current_block.startEA in succeeding_blocks:
+                    blocks_in_loops.update([current_block.startEA])
+            # perform Tarjan's algorithm to identify strongly connected components (= loops) in the function graph
+            tarjan = self.Tarjan()
+            strongly_connected = tarjan.calculate_strongly_connected_components(function_dgraph)
+            non_trivial_loops = [component for component in strongly_connected if len(component) > 1]
+            for component in non_trivial_loops:
+                blocks_in_loops.update(non_trivial_loops)
             for block in function_blocks:
+                if block.start_ea in blocks_in_loops:
+                    block.is_contained_in_loop = True
                 block.num_calls_in_function = calls_in_function
         print ("[\\] Analysis took %3.2f seconds" % (self.time.time() - time_before))
         return self.get_aritlog_blocks(self.low_rating_threshold, self.high_rating_threshold,
             self.low_instruction_threshold, self.high_instruction_threshold,
             self.low_call_threshold, self.high_call_threshold,
-            False)
+            False, False)
     def update_thresholds(self, min_rating, max_rating, min_instr, max_instr, min_call, max_call):
             self.high_call_threshold = max_call
-    def get_aritlog_blocks(self, min_rating, max_rating, min_instr, max_instr, min_api, max_api, is_nonzero):
+    def get_aritlog_blocks(self, min_rating, max_rating, min_instr, max_instr, min_api, max_api, is_nonzero, \
+        is_looped):
         get all blocks that are within the limits specified by the heuristic parameters.
         parameters are the same as in function "update_thresholds" except
         param is_nonzero: defines whether zeroing instructions (like xor eax, eax) shall be counted or not.
         type is_nonzero: boolean
+        param is_looped: defines whether only basic blocks in loops shall be selected
+        type is_looped: boolean
         @return: a list of AritlogBasicBlock data objects, according to the parameters
         self.update_thresholds(min_rating, max_rating, min_instr, max_instr, min_api, max_api)
         return [block for block in self.aritlog_blocks if
             (self.high_rating_threshold >= block.get_aritlog_rating(is_nonzero) >= self.low_rating_threshold) and
             (self.high_instruction_threshold >= block.num_instructions >= self.low_instruction_threshold) and
-            (self.high_call_threshold >= block.num_calls_in_function >= self.low_call_threshold)]
+            (self.high_call_threshold >= block.num_calls_in_function >= self.low_call_threshold) and
+            (not is_looped or block.is_contained_in_loop)]
     def get_unfiltered_block_count(self):
         segments = self.get_segment_data()
         print ("[|] Segments under analysis: ")
         for segment in segments:
-            print ("      " + segment)
+            print ("      " + str(segment))
         print ("[|] PatternManager initialized, number of signatures: %d" % len(self.pm.signatures))
         keywords = self.pm.get_tokenized_signatures(pattern_size)
         print ("[|] PatternManager tokenized patterns into %d chunks of %d bytes" % (len(keywords.keys()), pattern_size))


         self.self_nullifying_instructions = ["xor", "sbb", "sub"]
         self.start_ea = start_ea
         self.end_ea = end_ea
+        self.is_contained_in_loop = False
         self.num_instructions = 0
         self.num_log_arit_instructions = 0
         self.num_zeroing_instructions = 0
         Convenience function.
         @return: a nice string representation for this object
-        return "0x%x - 0x%x (%d), aritlog: %02.2f%% (%02.2f%%)" % (self.start_ea, self.end_ea, \
-        self.num_instructions, self.aritlog_rating * 100.0, self.nonzeroing_aritlog_rating * 100.0)
+        return "0x%x - 0x%x (%d), aritlog: %02.2f%% (%02.2f%%) [%s]" % (self.start_ea, self.end_ea, \
+        self.num_instructions, self.aritlog_rating * 100.0, self.nonzeroing_aritlog_rating * 100.0,
+        self.is_contained_in_loop and "loop" or "no loop")
     def __lt__(self, other):


         self.aritlog_controls_zeroing_cb = QtGui.QCheckBox("Exclude Zeroing")
+        self.aritlog_controls_looped_cb = QtGui.QCheckBox("Looped Blocks only")
+        self.aritlog_controls_looped_cb.setCheckState(self.QtCore.Qt.Checked)
+        self.aritlog_controls_looped_cb.stateChanged.connect(self.populate_aritlog_table)
         self.aritlog_controls_group_cb = QtGui.QCheckBox("Group by Functions")
+        aritlog_controls_aggregator_layout.addWidget(self.aritlog_controls_looped_cb)
         na = self.aritlog_controls_num_api_editor
         is_grouped = self.aritlog_controls_group_cb.isChecked()
         is_nonzero = self.aritlog_controls_zeroing_cb.isChecked()
+        is_looped = self.aritlog_controls_looped_cb.isChecked()
         aritlog_blocks = self.ci.get_aritlog_blocks(ts.low / 100.0, ts.high / 100.0, bs.low, bs.high, na.low, \
-            na.high, is_nonzero)
+            na.high, is_nonzero, is_looped)
         table_data = self.calculate_aritlog_table_data(aritlog_blocks, is_grouped)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.