Commits

Daniel Plohmann committed 341bfdf

Added scan support for PKCS keys+certificates. Did some major refactoring.

  • Participants
  • Parent commits 080962b

Comments (0)

Files changed (27)

 
     def __init__(self):
         super(IDAscopeForm, self).__init__()
-        banner =   "#############################################\n" \
-                 + "  ___ ____    _                             \n" \
-                 + " |_ _|  _ \  / \   ___  ___ ___  _ __   ___ \n" \
-                 + "  | || | | |/ _ \ / __|/ __/ _ \\| '_ \\ / _ \\\n" \
-                 + "  | || |_| / ___ \\\\__ \\ (_| (_) | |_) |  __/\n" \
-                 + " |___|____/_/   \\_\\___/\\___\\___/| .__/ \\___|\n" \
-                 + "                                |_|         \n" \
-                 + "#############################################\n" \
-                 + " by Daniel Plohmann and Alexander Hanel      \n" \
-                 + "#############################################\n"
+        banner = "#############################################\n" \
+               + "  ___ ____    _                             \n" \
+               + " |_ _|  _ \  / \   ___  ___ ___  _ __   ___ \n" \
+               + "  | || | | |/ _ \ / __|/ __/ _ \\| '_ \\ / _ \\\n" \
+               + "  | || |_| / ___ \\\\__ \\ (_| (_) | |_) |  __/\n" \
+               + " |___|____/_/   \\_\\___/\\___\\___/| .__/ \\___|\n" \
+               + "                                |_|         \n" \
+               + "#############################################\n" \
+               + " by Daniel Plohmann and Alexander Hanel      \n" \
+               + "#############################################\n"
         print banner
         print ("[+] Loading simpliFiRE.IDAscope")
         global HOTKEYS
         time_before = time.time()
         print ("[/] setting up shared modules...")
         self.semantic_identifier = SemanticIdentifier(self.config)
-        self.semantic_identifier.scan()
+        self.semantic_identifier.scanByReferences()
+        self.crypto_identifier = CryptoIdentifier()
         self.documentation_helper = DocumentationHelper(self.config)
         self.winapi_provider = WinApiProvider(self.config)
         self.ida_proxy = IdaProxy()
-        self.crypto_identifier = CryptoIdentifier()
         print ("[\\] this took %3.2f seconds.\n" % (time.time() - time_before))
 
     def setup_widgets(self):

idascope/core/AnnotationsProvider.py

 
 import json
 
-import idc
-import idaapi
-import idautils
-
 from helpers import JsonHelper
 
 annotations = {
 class AnnotationsProvider():
 
     def __init__(self):
-        # when calling from a PyQt object, the reference to the IDA python modules are not available.
-        # We add references to idc, idaapi, and idautils to our class to overcome this.
-        # 2012-07-01 TODO Figure out why the reference is broken and fix this.
-        self.idc = idc
-        self.idaapi = idaapi
-        self.idautils = idautils
         return
 
-    def load_config(self, config_filename):
+    def _loadConfig(self, config_filename):
         # TODO adapt implementation for this module
         config_file = open(config_filename, "r")
         config = config_file.read()
         self.semantic_definitions = parsed_config["semantic_definitions"]
         return
 
-    def get_annotations(self):
+    def getAnnotations(self):
         # return: function:
 
         # FLAGs that help to identify names:

idascope/core/CryptoIdentifier.py

 #  <http://www.gnu.org/licenses/>.
 #
 ########################################################################
-
+# Credits:
+# - Scanning algorithm for certificates is based on work by
+#   kyprizel's dump_certs.py
+#     (http://www.kyprizel.net/work/ida/getkeys.py)
+#   which is in turn based on work by Tobias Klein
+#     (http://www.trapkit.de/research/sslkeyfinder/)
+########################################################################
 
 import time
 import re
 
 from IdaProxy import IdaProxy
-from PatternManager import PatternManager
-from helpers.Tarjan import Tarjan
+from helpers.PatternManager import PatternManager
+from helpers.GraphHelper import GraphHelper
 
 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.GraphHelper = GraphHelper
         self.CryptoSignatureHit = CryptoSignatureHit
         self.AritlogBasicBlock = AritlogBasicBlock
         self.Segment = Segment
         """
         Scan the whole IDB with all available techniques.
         """
-        self.scan_aritlog()
-        self.scan_crypto_patterns()
+        self.scanAritlog()
+        self.scanCryptoPatterns()
 
 ################################################################################
 # Aritlog scanning
 ################################################################################
 
-    def scan_aritlog(self):
+    def scanAritlog(self):
         """
         scan with the arithmetic/logic heuristic
         @return: a list of AritLogBasicBlock data objects that fulfill the parameters as specified
         """
-        print ("[/] Starting aritlog heuristic analysis.")
+        print ("  [/] CryptoIdentifier: Starting aritlog heuristic analysis.")
         self.aritlog_blocks = []
         time_before = self.time.time()
         for function_ea in self.ida_proxy.Functions():
                         mnemonic = self.ida_proxy.GetMnem(instruction)
                         has_identical_operands = self.ida_proxy.GetOperandValue(instruction, 0) == \
                             self.ida_proxy.GetOperandValue(instruction, 1)
-                        block.update_instruction_count(mnemonic, has_identical_operands)
+                        block.updateInstructionCount(mnemonic, has_identical_operands)
                         if mnemonic == "call":
                             calls_in_function += 1
                 function_blocks.append(block)
                 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)
+            graph_helper = self.GraphHelper()
+            strongly_connected = graph_helper.calculateStronglyConnectedComponents(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)
                     block.is_contained_in_loop = True
                 block.num_calls_in_function = calls_in_function
             self.aritlog_blocks.extend(function_blocks)
-        print ("[\\] Analysis took %3.2f seconds" % (self.time.time() - time_before))
+        print ("  [\\] Analysis took %3.2f seconds." % (self.time.time() - time_before))
 
-        return self.get_aritlog_blocks(self.low_rating_threshold, self.high_rating_threshold,
+        return self.getAritlogBlocks(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)
 
-    def update_thresholds(self, min_rating, max_rating, min_instr, max_instr, min_call, max_call):
+    def _updateThresholds(self, min_rating, max_rating, min_instr, max_instr, min_call, max_call):
         """
         update all six threshold bounds
         @param min_rating: the minimum arit/log ratio a basic block must have
         else:
             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 getAritlogBlocks(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
+        parameters are the same as in function "_updateThresholds" 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)
+        self._updateThresholds(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_rating_threshold >= block.getAritlogRating(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) and
             (not is_looped or block.is_contained_in_loop)]
 
-    def get_unfiltered_block_count(self):
+    def getUnfilteredBlockCount(self):
         """
         returns the number of basic blocks that have been analyzed.
         @return: (int) number of basic blocks
 # Signature scanning
 ################################################################################
 
-    def get_segment_data(self):
+    def getSegmentData(self):
         """
         returns the raw bytes of the segments as stored by IDA
         @return: a list of Segment data objects.
                 print ("[!] Tried to access invalid segment data. An error has occurred while address conversion")
         return segments
 
-    def scan_crypto_patterns(self, pattern_size=32):
+    def scanCryptoPatterns(self, pattern_size=32):
         """
         perform a scan ofr signatures. For matching, the standard python re module is used.
         @return: A list of CryptoSignatureHit data objects
         """
         crypt_results = []
-        print ("[/] Starting aritlog function enumeration.")
+        print ("  [/] CryptoIdentifier: Starting crypto signature scanning.")
         time_before_matching = self.time.time()
-        segments = self.get_segment_data()
-        print ("[|] Segments under analysis: ")
+        segments = self.getSegmentData()
+        print ("  [|] Segments under analysis: ")
         for segment in segments:
             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))
+        print ("  [|] PatternManager initialized, number of signatures: %d" % len(self.pm.signatures))
+        keywords = self.pm.getTokenizedSignatures(pattern_size)
+        print ("  [|] PatternManager tokenized patterns into %d chunks of %d bytes" % \
+            (len(keywords.keys()), pattern_size))
         for keyword in keywords.keys():
             for segment in segments:
                 crypt_results.extend([self.CryptoSignatureHit(segment.start_ea + match.start(), \
                     keywords[keyword], keyword) for match in self.re.finditer(self.re.escape(keyword), segment.data)])
-        print ("[\\] Full matching took %3.2f seconds and resulted in %d hits" % (self.time.time() - time_before_matching, \
+        print ("  [|] PatternManager now scanning variable signatures")
+        variable_matches = self.scanVariablePatterns()
+        crypt_results.extend(variable_matches)
+        print ("  [\\] Full matching took %3.2f seconds and resulted in %d hits." % \
+            (self.time.time() - time_before_matching, \
             len(crypt_results)))
         self.signature_hits = crypt_results
         return crypt_results
 
-    def get_signature_length(self, signature_name):
+    def scanVariablePatterns(self):
+        # the scanning code is roughly based on kyprizel's signature scan, see credtis above for more information
+        crypt_results = []
+        decoded_base64 = self.getDecodedBase64Strings()
+        temporary_segment = self.mapBase64ToTemporarySegment(decoded_base64)
+        variable_signatures = self.pm.getVariableSignatures()
+        for var_sig in variable_signatures.keys():
+            current_seg = self.ida_proxy.FirstSeg()
+            seg_end = self.ida_proxy.SegEnd(current_seg)
+            while current_seg != self.ida_proxy.BAD_ADDR:
+                signature_hit = self.ida_proxy.find_binary(current_seg, seg_end, variable_signatures[var_sig], 16, 1)
+                if signature_hit != self.ida_proxy.BAD_ADDR:
+                    crypt_results.append(self.CryptoSignatureHit(signature_hit, \
+                        [var_sig], variable_signatures[var_sig]))
+                    current_seg = signature_hit + variable_signatures[var_sig].count(" ") + 1
+                else:
+                    current_seg = self.ida_proxy.NextSeg(seg_end)
+                    if not current_seg == self.ida_proxy.BAD_ADDR:
+                        seg_end = self.ida_proxy.SegEnd(current_seg)
+            if seg_end == temporary_segment:
+                current_seg = temporary_segment
+                seg_end = self.ida_proxy.SegEnd(current_seg)
+                while current_seg != self.ida_proxy.BAD_ADDR:
+                    signature_hit = self.ida_proxy.find_binary(current_seg, seg_end, variable_signatures[var_sig], 16, 1)
+                    if signature_hit != self.ida_proxy.BAD_ADDR:
+                        string_addr = self.extractAddr(signature_hit - temporary_segment, decoded_base64)
+                        crypt_results.append(self.CryptoSignatureHit(string_addr, \
+                            [var_sig], variable_signatures[var_sig]))
+                        current_seg = signature_hit + variable_signatures[var_sig].count(" ") + 1
+                    else:
+                        break
+        self.ida_proxy.DelSeg(temporary_segment, self.ida_proxy.SEGMOD_KILL)
+        return crypt_results
+
+    def extractAddr(self, signature_hit, decoded_base64):
+        string_addr = 0
+        for base in decoded_base64:
+            if base[1] <= signature_hit:
+                string_addr = base[0]
+            else:
+                break
+        return string_addr
+
+    def getDecodedBase64Strings(self):
+        decoded_names = []
+        byte_count = 0
+        for name in self.ida_proxy.Names():
+            flags = self.ida_proxy.GetFlags(name[0])
+            if not self.ida_proxy.isASCII(flags):
+                continue
+            ascii = self.ida_proxy.GetString(name[0])
+            try:
+                b64 = ascii.decode("base64")
+                decoded_names.append((name[0], byte_count, b64))
+                byte_count += len(b64)
+            except:
+                continue
+        return decoded_names
+
+    def mapBase64ToTemporarySegment(self, decoded_base64):
+        byte_count = decoded_base64[-1][1] + len(decoded_base64[-1][2])
+        # get end of final segment to spawn a new one at that location, write decoded bytes there, search
+        current_seg = self.ida_proxy.FirstSeg()
+        seg_end = 0
+        while current_seg != self.ida_proxy.BAD_ADDR:
+            current_seg = self.ida_proxy.NextSeg(seg_end)
+            if not current_seg == self.ida_proxy.BAD_ADDR:
+                seg_end = self.ida_proxy.SegEnd(current_seg)
+        print ("[|] PatternManager is creating a temporary segment to allow scanning of decoded base64 strings.")
+        self.ida_proxy.AddSeg(seg_end, seg_end + byte_count, 0, True, self.ida_proxy.SA_REL_PARA, self.ida_proxy.SC_PUB)
+        self.ida_proxy.SegRename(seg_end, "scopetmp")
+        offset = seg_end
+        for b64 in decoded_base64:
+            for byte in b64[2]:
+                self.ida_proxy.PatchByte(offset, ord(byte))
+                offset += 1
+        return seg_end
+
+    def getSignatureLength(self, signature_name):
         """
         returns the length for a signature, identified by its name
         @param signature_name: name for a signature, e.g. "ADLER 32"
                 return len(item[0])
         return 0
 
-    def get_xrefs_to_address(self, address):
+    def getXrefsToAddress(self, address):
         """
         get all references to a certain address.
         These are no xrefs in IDA sense but references to the crypto signatures.
                 xrefs.append((x.frm, False))
         return xrefs
 
-    def get_signature_hits(self):
+    def getSignatureHits(self):
         """
         Get all signature hits that have a length of at least match_filter_factor percent
         of the signature they triggered.
         filtered_hits = []
         for hit in unified_hits:
             if len(hit.matched_signature) >= max([self.match_filter_factor * \
-                self.get_signature_length(name) for name in hit.signature_names]):
-                hit.code_refs_to = self.get_xrefs_to_address(hit.start_address)
+                self.getSignatureLength(name) for name in hit.signature_names]):
+                hit.code_refs_to = self.getXrefsToAddress(hit.start_address)
                 filtered_hits.append(hit)
 
         grouped_hits = {}

idascope/core/DocumentationHelper.py

         self.default_highlight_color = 0x3333FF
         self.color_state = "unknown"
         self.idascope_config = idascope_config
-        self._load_config(self.idascope_config.semantics_file)
+        self._loadConfig(self.idascope_config.semantics_file)
         return
 
-    def _load_config(self, config_filename):
+    def _loadConfig(self, config_filename):
         """
         Loads a semantic configuration file and generates a color map from the contained information.
         @param config_filename: filename of a semantic configuration file
         self.default_neutral_color = int(parsed_config["default_neutral_color"], 16)
         self.default_base_color = int(parsed_config["default_base_color"], 16)
         self.default_highlight_color = int(parsed_config["default_highlight_color"], 16)
-        self.color_map = self._generate_color_map_from_definitions(parsed_config["semantic_definitions"])
+        self.color_map = self._generateColorMapFromDefinitions(parsed_config["semantic_definitions"])
         return
 
-    def _generate_color_map_from_definitions(self, definitions):
+    def _generateColorMapFromDefinitions(self, definitions):
         """
         Internal function to generate a color map from a semantic definitions config file.
         @param definitions: the defintions part of a semantic definitions config file.
                 "highlight_color": int(definition["highlight_color"], 16)}
         return color_map
 
-    def uncolor_all(self):
+    def uncolorAll(self):
         """
         Uncolors all instructions of all segments by changing their color to white.
         """
                 self.ida_proxy.SegEnd(seg_ea)):
                 for block in self.ida_proxy.FlowChart(self.ida_proxy.get_func(function_address)):
                     for head in self.ida_proxy.Heads(block.startEA, block.endEA):
-                        self.color_instruction(head, 0xFFFFFF, refresh=False)
+                        self.colorInstruction(head, 0xFFFFFF, refresh=False)
         self.ida_proxy.refresh_idaview_anyway()
 
-    def color_instruction(self, address, color, refresh=True):
+    def colorInstruction(self, address, color, refresh=True):
         """
         Colors the instruction at an address with the given color code.
         @param address: address of the instruction to color
         if refresh:
             self.ida_proxy.refresh_idaview_anyway()
 
-    def color_basic_block(self, address, color, refresh=True):
+    def colorBasicBlock(self, address, color, refresh=True):
         """
         Colors the basic block containing a target address with the given color code.
         @param address: address an instruction in the basic block to color
         for block in function_chart:
             if block.startEA <= address < block.endEA:
                 for head in self.ida_proxy.Heads(block.startEA, block.endEA):
-                    self.color_instruction(head, color, refresh)
+                    self.colorInstruction(head, color, refresh)
 
-    def get_next_color_scheme(self):
+    def getNextColorScheme(self):
         """
         get the next color scheme in the three-cycle "individual/mono/uncolored", where individual is semantic coloring
         @return: next state
         else:
             return "individual"
 
-    def select_highlight_color(self, tag):
+    def selectHighlightColor(self, tag):
         """
         automatically chooses the highlight color for a tag based on the current color scheme
         @return: (int) a color code
         """
-        if self.get_next_color_scheme() == "uncolored":
+        if self.getNextColorScheme() == "uncolored":
             return 0xFFFFFF
-        elif self.get_next_color_scheme() == "mono":
+        elif self.getNextColorScheme() == "mono":
             return self.default_highlight_color
         else:
             return self.color_map[tag]["highlight_color"]
 
-    def select_base_color(self, tagged_addresses_in_block):
+    def selectBaseColor(self, tagged_addresses_in_block):
         """
         automatically chooses the base color for a block based on the current color scheme
         @param tagged_addresses_in_block: all tagged addresses in a basic block for which the color shall be chosen
         @type tagged_addresses_in_block: a list of tuples (int, str) containing pairs of instruction addresses and tags
         @return: (int) a color code
         """
-        if self.get_next_color_scheme() == "uncolored":
+        if self.getNextColorScheme() == "uncolored":
             return 0xFFFFFF
-        elif self.get_next_color_scheme() == "mono":
+        elif self.getNextColorScheme() == "mono":
             return self.default_base_color
         else:
             tags_in_block = [item[1] for item in tagged_addresses_in_block]
         @type scan_result: a dictionary with key/value entries of the following form: (address, [FunctionContext])
         """
         for function_address in scan_result.keys():
-            tagged_addresses_in_function = scan_result[function_address].get_all_tagged_addresses()
+            tagged_addresses_in_function = scan_result[function_address].getAllTaggedAddresses()
             function_chart = self.ida_proxy.FlowChart(self.ida_proxy.get_func(function_address))
             for basic_block in function_chart:
                 tagged_addresses_in_block = [(addr, tagged_addresses_in_function[addr]) for addr in \
                     tagged_addresses_in_function.keys() if addr in xrange(basic_block.startEA, basic_block.endEA)]
                 if len(tagged_addresses_in_block) > 0:
-                    base_color = self.select_base_color(tagged_addresses_in_block)
-                    self.color_basic_block(basic_block.startEA, base_color, refresh=False)
+                    base_color = self.selectBaseColor(tagged_addresses_in_block)
+                    self.colorBasicBlock(basic_block.startEA, base_color, refresh=False)
                     for tagged_address in tagged_addresses_in_block:
-                        highlight_color = self.select_highlight_color(tagged_address[1])
-                        self.color_instruction(tagged_address[0], highlight_color, refresh=False)
-        self.color_state = self.get_next_color_scheme()
+                        highlight_color = self.selectHighlightColor(tagged_address[1])
+                        self.colorInstruction(tagged_address[0], highlight_color, refresh=False)
+        self.color_state = self.getNextColorScheme()
         self.ida_proxy.refresh_idaview_anyway()
 
-    def get_next_non_func_instruction(self, addr):
+    def getNextNonFuncInstruction(self, addr):
         next_instruction = addr
         while next_instruction != self.ida_proxy.BAD_ADDR:
             next_instruction = self.ida_proxy.find_not_func(next_instruction, self.ida_proxy.SEARCH_DOWN)
                 return next_instruction
         return self.ida_proxy.BAD_ADDR
 
-    def convert_non_function_code(self):
-        # do a first run to define those areas that have a function prologue
+    def convertNonFunctionCode(self):
+        self.convertNonFunctionCodeWithPrologues()
+        # do a second run to define the rest
+        next_instruction = self.ida_proxy.minEA()
+        while next_instruction != self.ida_proxy.BAD_ADDR:
+            next_instruction = self.getNextNonFuncInstruction(next_instruction)
+            print("[+] Fixed undefined code to function @ [%08x]" % \
+                (next_instruction))
+            self.ida_proxy.MakeFunction(next_instruction)
+        return
+
+    def convertNonFunctionCodeWithPrologues(self):
         next_instruction = self.ida_proxy.minEA()
         while next_instruction != self.ida_proxy.BAD_ADDR:
-            next_instruction = self.get_next_non_func_instruction(next_instruction)
+            next_instruction = self.getNextNonFuncInstruction(next_instruction)
             if self.ida_proxy.GetMnem(next_instruction).startswith("push") and \
                 self.ida_proxy.GetOpType(next_instruction, 0) == 1 and \
                 self.ida_proxy.GetOperandValue(next_instruction, 0) == 5:
-                instruction_after_push = self.get_next_non_func_instruction(next_instruction)
+                instruction_after_push = self.getNextNonFuncInstruction(next_instruction)
                 if self.ida_proxy.GetMnem(instruction_after_push).startswith("mov") and \
                     self.ida_proxy.GetOpType(instruction_after_push, 0) == 1 and \
                     self.ida_proxy.GetOperandValue(instruction_after_push, 0) == 5 and \
                         print("[+] Fixed undefined code with function prologue (push ebp; mov ebp, esp) to function " \
                             + "@ [%08x]" % (next_instruction))
                         self.ida_proxy.MakeFunction(next_instruction)
-        # do a second run to define the rest
-        next_instruction = self.ida_proxy.minEA()
-        while next_instruction != self.ida_proxy.BAD_ADDR:
-            next_instruction = self.get_next_non_func_instruction(next_instruction)
-            print("[+] Fixed undefined code to function @ [%08x]" % \
-                (next_instruction))
-            self.ida_proxy.MakeFunction(next_instruction)
-        return
+

idascope/core/IdaProxy.py

         self.BAD_ADDR = 0xffffffff
         self.CIC_ITEM = self.idc.CIC_ITEM
         self.FF_LABL = self.idc.FF_LABL
+        self.FL_CN = self.idc.fl_CN
+        self.FL_CN = self.idc.fl_CN
         self.FUNC_LIB = self.idaapi.FUNC_LIB
         self.FUNCATTR_END = self.idc.FUNCATTR_END
         self.INF_SHORT_DN = self.idc.INF_SHORT_DN
         self.SN_NOWARN = self.idc.SN_NOWARN
         self.SN_NOCHECK = self.idc.SN_NOCHECK
+        self.SA_REL_BYTE = self.idc.saRelByte
+        self.SA_REL_PARA = self.idc.saRelPara
+        self.SC_PRIV = self.idc.scPriv
+        self.SC_PUB = self.idc.scPub
+        self.SEGMOD_KILL = self.idaapi.SEGMOD_KILL
         self.SEARCH_DOWN = 1
+        self.MFF_FAST = self.idaapi.MFF_FAST
 
 ###############################################################################
 # From idc.py
     def AddHotkey(self, hotkey, function):
         return self.idc.AddHotkey(hotkey, function)
 
+    def AddSeg(self, start_ea, end_ea, base, use32, align, comb):
+        return self.idc.AddSeg(start_ea, end_ea, base, use32, align, comb)
+
+    def Byte(self, byte):
+        return self.idc.Byte(byte)
+
+    def DelSeg(self, address, flags):
+        return self.idc.DelSeg(address, flags)
+
     def Demangle(self, name, disable_mask):
         return self.idc.Demangle(name, disable_mask)
 
+    def FirstSeg(self):
+        return self.idc.FirstSeg()
+
     def get_byte(self, address):
         return self.idaapi.get_byte(address)
 
     def GetOperandValue(self, address, index):
         return self.idc.GetOperandValue(address, index)
 
+    def GetString(self, address):
+        return self.idc.GetString(address)
+
     def GetType(self, address):
         type_at_address = self.idc.GetType(address)
         if type_at_address is not None:
     def Name(self, address):
         return self.idc.Name(address)
 
+    def NextSeg(self, address):
+        return self.idc.NextSeg(address)
+
+    def PatchByte(self, address, byte):
+        self.idc.PatchByte(address, byte)
+
     def PrevHead(self, ea, minea=0):
         return self.idc.PrevHead(ea, minea)
 
     def SegName(self, address):
         return self.idc.SegName(address)
 
+    def SegRename(self, address, name):
+        return self.idc.SegRename(address, name)
+
     def SegStart(self, address):
         return self.idc.SegStart(address)
 
     def find_not_func(self, *args):
         return self.idaapi.find_not_func(*args)
 
+    def find_binary(self, *args):
+        return self.idaapi.find_binary(*args)
+
     def FlowChart(self, function_address):
         function_chart = []
         try:
     def get_highlighted_identifier(self):
         return self.idaapi.get_highlighted_identifier()
 
+    def isASCII(self, flags):
+        return self.idaapi.isASCII(flags)
+
     def minEA(self):
         return self.idaapi.cvar.inf.minEA
 
     def CodeRefsTo(self, destination, flow):
         return self.idautils.CodeRefsTo(destination, flow)
 
+    def DataRefsFrom(self, source):
+        return self.idautils.DataRefsFrom(source)
+
     def DataRefsTo(self, destination):
         return self.idautils.DataRefsTo(destination)
 
+    def execute_sync(self, *args):
+        return self.idaapi.execute_sync(*args)
+
     def FuncItems(self, function_address):
         return self.idautils.FuncItems(function_address)
 
     def Heads(self, start_address=None, end_address=None):
         return self.idautils.Heads(start_address, end_address)
 
+    def Names(self):
+        return self.idautils.Names()
+
     def Segments(self):
         return self.idautils.Segments()
 

idascope/core/SemanticIdentifier.py

 
 import json
 import re
+import time
 
 from helpers import JsonHelper
 
     def __init__(self, idascope_config):
         print ("[|] loading SemanticIdentifier")
         self.re = re
+        self.time = time
         self.ida_proxy = IdaProxy()
         self.FunctionContext = FunctionContext
         self.CallContext = CallContext
         self.ParameterContext = ParameterContext
         self.renaming_seperator = "_"
         self.semantic_definitions = []
-        self.last_result = {}
+        self.last_scan_result = {}
         self.idascope_config = idascope_config
-        self._load_config(self.idascope_config.semantics_file)
+        self._loadConfig(self.idascope_config.semantics_file)
         return
 
-    def _load_config(self, config_filename):
+    def _loadConfig(self, config_filename):
         """
         Loads a semantic configuration file and collects all definitions from it.
         @param config_filename: filename of a semantic configuration file
         self.semantic_definitions = parsed_config["semantic_definitions"]
         return
 
-    def calculate_number_of_basic_blocks_for_function_address(self, function_address):
+    def calculateNumberOfBasicBlocksForFunctionAddress(self, function_address):
         """
         Calculates the number of basic blocks for a given function by walking its FlowChart.
         @param function_address: function address to calculate the block count for
             pass
         return number_of_blocks
 
-    def get_number_of_basic_blocks_for_function_address(self, address):
+    def getNumberOfBasicBlocksForFunctionAddress(self, address):
         """
         returns the number of basic blocks for the function containing the queried address,
         based on the value stored in the last scan result.
         @return: (int) The number of blocks in th e function
         """
         number_of_blocks = 0
-        function_address = self.get_function_address_for_address(address)
-        if function_address in self.last_result.keys():
-            number_of_blocks = self.last_result[function_address].number_of_basic_blocks
+        function_address = self.getFunctionAddressForAddress(address)
+        if function_address in self.last_scan_result.keys():
+            number_of_blocks = self.last_scan_result[function_address].number_of_basic_blocks
         return number_of_blocks
 
     def scan(self):
         """
         Scan the whole IDB with all available techniques.
         """
-        self.scan_by_references()
-        self.scan_all_code()
+        self.scanByReferences()
+        self.scanDeep()
 
-    def scan_by_references(self):
+    def scanByReferences(self):
         """
         Scan by references to API names, based on the definitions loaded from the config file.
         This is highly efficient because we only touch places in the IDB that actually have references
         to our API names of interest.
         """
-        scan_result = {}
+        print ("  [/] SemanticIdentifier: Starting (fast) scan by references of function semantics.")
+        time_before = self.time.time()
+        self.last_scan_result = {}
         for semantic_group in self.semantic_definitions:
             semantic_group_tag = semantic_group["tag"]
             for api_name in semantic_group["api_names"]:
                 api_address = self.ida_proxy.LocByName(api_name)
-                code_ref_addrs = [ref for ref in self.ida_proxy.CodeRefsTo(api_address, 0)]
-                data_ref_addrs = [ref for ref in self.ida_proxy.DataRefsTo(api_address)]
-                ref_addrs = iter(set(code_ref_addrs).union(set(data_ref_addrs)))
-                for ref in ref_addrs:
-                    function_ctx = self.FunctionContext()
-                    function_ctx.function_address = self.ida_proxy.LocByName(self.ida_proxy.GetFunctionName(ref))
-                    function_ctx.function_name = self.ida_proxy.GetFunctionName(ref)
-                    function_ctx.has_dummy_name = (self.ida_proxy.GetFlags(function_ctx.function_address) & \
-                        self.ida_proxy.FF_LABL) > 0
-                    if function_ctx.function_address not in scan_result.keys():
-                        scan_result[function_ctx.function_address] = function_ctx
-                    else:
-                        function_ctx = scan_result[function_ctx.function_address]
+                for ref in self._getAllRefsTo(api_address):
+                    function_ctx = self._createFunctionContext(ref)
+                    function_ctx.has_tags = True
                     call_ctx = self.CallContext()
                     call_ctx.called_function_name = api_name
                     call_ctx.address_of_call = ref
                     call_ctx.called_address = api_address
                     call_ctx.tag = semantic_group_tag
-                    call_ctx.parameter_contexts = self._resolve_api_call(call_ctx)
+                    call_ctx.parameter_contexts = self._resolveApiCall(call_ctx)
                     function_ctx.call_contexts.append(call_ctx)
-        self.last_result = scan_result
+        print ("  [\\] Analysis took %3.2f seconds." % (self.time.time() - time_before))
+
+    def _getAllRefsTo(self, addr):
+        code_ref_addrs = [ref for ref in self.ida_proxy.CodeRefsTo(addr, 0)]
+        data_ref_addrs = [ref for ref in self.ida_proxy.DataRefsTo(addr)]
+        return iter(set(code_ref_addrs).union(set(data_ref_addrs)))
+
+    def _getNumRefsTo(self, addr):
+        return sum([1 for ref in self._getAllRefsTo(addr)])
+
+    def _getAllRefsFrom(self, addr, code_only=False):
+        code_ref_addrs = [ref for ref in self.ida_proxy.CodeRefsFrom(addr, 0)]
+        data_ref_addrs = []
+        if code_only:
+            # only consider data references that lead to a call near/far (likely imports)
+            data_ref_addrs = [ref for ref in self.ida_proxy.DataRefsFrom(addr) if \
+                self.ida_proxy.GetFlags(ref) & (self.ida_proxy.FL_CN | self.ida_proxy.FL_CF)]
+        else:
+            data_ref_addrs = [ref for ref in self.ida_proxy.DataRefsFrom(addr)]
+        return iter(set(code_ref_addrs).union(set(data_ref_addrs)))
+
+    def _createFunctionContext(self, func_addr):
+        """
+        Create a FunctionContext for the given start address in the current scan result.
+        @param func_addr: address to create a FunctionContext for
+        @type func_addr: int
+        @return: (FunctionContext) A reference to the corresponding function context
+        """
+        function_ctx = None
+        if func_addr not in self.last_scan_result.keys():
+            function_ctx = self.FunctionContext()
+            function_ctx.function_address = self.ida_proxy.LocByName(self.ida_proxy.GetFunctionName(func_addr))
+            function_ctx.function_name = self.ida_proxy.GetFunctionName(func_addr)
+            function_ctx.has_dummy_name = (self.ida_proxy.GetFlags(function_ctx.function_address) & \
+                self.ida_proxy.FF_LABL) > 0
+            self.last_scan_result[function_ctx.function_address] = function_ctx
+        else:
+            function_ctx = self.last_scan_result[func_addr]
+        return function_ctx
 
-    def scan_all_code(self):
+    def scanDeep(self):
         """
         Not implemented yet. In the long run, this function shall perform a full enumeration of all instructions,
         gathering information like number of instructions, number of basic blocks,
         references to and from functions etc.
         """
-        # for all functions, accumulate data for the following fields:
-        #   number_of_basic_blocks = 0
-        #   number_of_instructions = 0
-        #   number_of_xrefs_from = 0
-        #   number_of_xrefs_to = 0
-        pass
-
-    def get_function_address_for_address(self, address):
+        print ("  [/] SemanticIdentifier: Starting deep scan of function semantics.")
+        time_before = self.time.time()
+        for function_ea in self.ida_proxy.Functions():
+            function_chart = self.ida_proxy.FlowChart(self.ida_proxy.get_func(function_ea))
+            num_blocks = 0
+            num_instructions = 0
+            xrefs_from = []
+            calls_from = []
+            function_ctx = self._createFunctionContext(function_ea)
+            for block in function_chart:
+                num_blocks += 1
+                for instruction in self.ida_proxy.Heads(block.startEA, block.endEA):
+                    num_instructions += 1
+                    if self.ida_proxy.isCode(self.ida_proxy.GetFlags(instruction)):
+                        for ref in self._getAllRefsFrom(instruction):
+                            if self.ida_proxy.GetMnem(instruction) == "call":
+                                calls_from.append(ref)
+                            xrefs_from.append(ref)
+            function_ctx.calls_from.update(calls_from)
+            function_ctx.number_of_xrefs_to = self._getNumRefsTo(function_ea)
+            function_ctx.xrefs_from.update(xrefs_from)
+            function_ctx.number_of_xrefs_from = len(xrefs_from)
+            function_ctx.number_of_basic_blocks = num_blocks
+            function_ctx.number_of_instructions = num_instructions
+        print ("  [\\] Analysis took %3.2f seconds." % (self.time.time() - time_before))
+
+    def getFunctionAddressForAddress(self, address):
         """
         Get a function address containing the queried address.
         @param address: address to check the function address for
         """
         return self.ida_proxy.LocByName(self.ida_proxy.GetFunctionName(address))
 
-    def calculate_number_of_functions(self):
+    def calculateNumberOfFunctions(self):
         """
         Calculate the number of functions in all segments.
         @return: (int) the number of functions found.
                 number_of_functions += 1
         return number_of_functions
 
-    def get_identified_function_addresses(self):
+    def getFunctionAddresses(self, dummy=False, tag=False):
         """
         Get all function address that have been covered by the last scanning.
+        @param dummy_only: only return functions with dummy names
+        @type dummy_only: bool
+        @param tag_only: only return tag functions
+        @type tag_only: bool
         @return: (list of int) The addresses of covered functions.
         """
-        return self.last_result.keys()
-
-    def get_identified_dummy_function_addresses(self):
-        """
-        Get all function address with a dummy name that have been covered by the last scanning.
-        @return: (list of int) The addresses of covered functions.
-        """
-        return [addr for addr in self.last_result.keys() if self.last_result[addr].has_dummy_name]
+        if tag and dummy:
+            return [addr for addr in self.last_scan_result.keys() if self.last_scan_result[addr].has_dummy_name and \
+                self.last_scan_result[addr].has_tags]
+        elif tag:
+            return [addr for addr in self.last_scan_result.keys() if self.last_scan_result[addr].has_tags]
+        elif dummy:
+            return [addr for addr in self.last_scan_result.keys() if self.last_scan_result[addr].has_dummy_name]
+        else:
+            return self.last_scan_result.keys()
+        return []
 
-    def get_tags(self):
+    def getTags(self):
         """
         Get all the tags that have been covered by the last scanning.
         @return (list of str) The tags found.
         """
         tags = []
-        for function_address in self.last_result.keys():
-            for call_ctx in self.last_result[function_address].call_contexts:
+        for function_address in self.last_scan_result.keys():
+            for call_ctx in self.last_scan_result[function_address].call_contexts:
                 if call_ctx.tag not in tags:
                     tags.append(call_ctx.tag)
         return tags
 
-    def get_tags_for_function_address(self, address):
+    def getTagsForFunctionAddress(self, address):
         """
         Get all tags found for the function containing the queried address.
         @param address: address in the target function
         @return: (list of str) The tags for the function containing the queried address
         """
         tags = []
-        function_address = self.get_function_address_for_address(address)
-        if function_address in self.last_result.keys():
-            for call_ctx in self.last_result[function_address].call_contexts:
+        function_address = self.getFunctionAddressForAddress(address)
+        if function_address in self.last_scan_result.keys():
+            for call_ctx in self.last_scan_result[function_address].call_contexts:
                 if call_ctx.tag not in tags:
                     tags.append(call_ctx.tag)
         return tags
 
-    def get_tag_count_for_function_address(self, tag, address):
+    def getTagCountForFunctionAddress(self, tag, address):
         """
         Get the number of occurrences for a certain tag for the function containing the queried address.
         @param tag: a tag as included in semantic definitions
         @type address: int
         @return: (int) The number of occurrences for this tag in the function
         """
-        function_address = self.get_function_address_for_address(address)
+        function_address = self.getFunctionAddressForAddress(address)
         tag_count = 0
-        if tag in self.get_tags_for_function_address(function_address):
-            for call_ctx in self.last_result[function_address].call_contexts:
+        if tag in self.getTagsForFunctionAddress(function_address):
+            for call_ctx in self.last_scan_result[function_address].call_contexts:
                 if call_ctx.tag == tag:
                     tag_count += 1
         return tag_count
 
-    def get_tagged_apis_for_function_address(self, address):
+    def getTaggedApisForFunctionAddress(self, address):
         """
         Get all call contexts for the function containing the queried address.
         @param address: address in the target function
         @type address: int
         @return: (list of CallContext data objects) The call contexts identified by the scanning of this function
         """
-        function_address = self.get_function_address_for_address(address)
-        if function_address in self.last_result.keys():
-            all_call_ctx = self.last_result[function_address].call_contexts
+        function_address = self.getFunctionAddressForAddress(address)
+        if function_address in self.last_scan_result.keys():
+            all_call_ctx = self.last_scan_result[function_address].call_contexts
             return [call_ctx for call_ctx in all_call_ctx if call_ctx.tag != ""]
 
-    def get_address_tag_pairs_ordered_by_function(self):
+    def getAddressTagPairsOrderedByFunction(self):
         """
         Get all call contexts for all functions
         @return: a dictionary with key/value entries of the following form: (function_address,
                  dict((call_address, tag)))
         """
         functions_and_tags = {}
-        for function in self.get_identified_function_addresses():
-            call_contexts = self.get_tagged_apis_for_function_address(function)
+        for function in self.getIdentifiedFunctionAddresses():
+            call_contexts = self.getTaggedApisForFunctionAddress(function)
             if function not in functions_and_tags.keys():
                 functions_and_tags[function] = {}
             for call_ctx in call_contexts:
                 functions_and_tags[function][call_ctx.address_of_call] = call_ctx.tag
         return functions_and_tags
 
-    def get_functions_to_rename(self):
+    def getFunctionsToRename(self):
         """
         Get all functions that can be renamed according to the last scan result. Only functions with the standard
         IDA name I{sub_[0-9A-F]+} will be considered for renaming.
                  ("new_function_name", str), ("function_address", int)
         """
         functions_to_rename = []
-        for function_address_to_tag in self.last_result.keys():
-            new_function_name = self.last_result[function_address_to_tag].function_name
+        for function_address_to_tag in self.last_scan_result.keys():
+            new_function_name = self.last_scan_result[function_address_to_tag].function_name
             # has the function still a dummy name?
             if self.ida_proxy.GetFlags(function_address_to_tag) & self.ida_proxy.FF_LABL > 0:
-                tags_for_function = self.get_tags_for_function_address(function_address_to_tag)
+                tags_for_function = self.getTagsForFunctionAddress(function_address_to_tag)
                 for tag in sorted(tags_for_function, reverse=True):
                     if tag not in new_function_name:
                         new_function_name = tag + self.renaming_seperator + new_function_name
                 functions_to_rename.append({"old_function_name": \
-                    self.last_result[function_address_to_tag].function_name, "new_function_name": \
+                    self.last_scan_result[function_address_to_tag].function_name, "new_function_name": \
                     new_function_name, "function_address": function_address_to_tag})
         return functions_to_rename
 
-    def rename_functions(self):
+    def renameFunctions(self):
         """
         Perform the renaming of functions according to the last scan result.
         """
-        for function in self.get_functions_to_rename():
+        for function in self.getFunctionsToRename():
             if function["old_function_name"] == self.ida_proxy.GetFunctionName(function["function_address"]):
                 self.ida_proxy.MakeNameEx(function["function_address"], function["new_function_name"], \
                     self.ida_proxy.SN_NOWARN)
 
-    def rename_potential_wrapper_functions(self):
+    def renamePotentialWrapperFunctions(self):
+        num_wrappers_renamed = 0
         for seg_ea in self.ida_proxy.Segments():
             for func_ea in self.ida_proxy.Functions(self.ida_proxy.SegStart(seg_ea), self.ida_proxy.SegEnd(seg_ea)):
                 if (self.ida_proxy.GetFlags(func_ea) & 0x8000) != 0:
                             while rval == False:
                                 if name_suffix > 40:
                                     print("[!] Potentially more than 50 wrappers for function %s, " \
-                                        "please report IDB" % w_name)
+                                        "please report this IDB ;)" % w_name)
                                     break
                                 if self.ida_proxy.Demangle(w_name, \
                                     self.ida_proxy.GetLongPrm(self.ida_proxy.INF_SHORT_DN)) != w_name:
                                 rval = self.ida_proxy.MakeNameEx(func_ea, f_name, \
                                     self.ida_proxy.SN_NOCHECK | self.ida_proxy.SN_NOWARN)
                             if rval == True:
-                                print("[+] Identified and renamed potential wrapper @ [%08x] to [%s]" % (func_ea, f_name))
+                                print("[+] Identified and renamed potential wrapper @ [%08x] to [%s]" % \
+                                    (func_ea, f_name))
+                                num_wrappers_renamed += 1
+        print("[+] Renamed %d functions with their potentially wrapped name." % num_wrappers_renamed)
 
-    def get_parameters_for_call_address(self, call_address):
+    def getParametersForCallAddress(self, call_address):
         """
         Get the parameters for the given address of a function call.
         @param call_address: address of the target call to inspect
         @return: a list of ParameterContext data objects.
         """
         target_function_address = self.ida_proxy.LocByName(self.ida_proxy.GetFunctionName(call_address))
-        all_tagged_apis_in_function = self.get_tagged_apis_for_function_address(target_function_address)
+        all_tagged_apis_in_function = self.getTaggedApisForFunctionAddress(target_function_address)
         for api in all_tagged_apis_in_function:
             if api.address_of_call == call_address:
-                return self._resolve_api_call(api)
+                return self._resolveApiCall(api)
         return []
 
-    def _resolve_api_call(self, call_context):
+    def _resolveApiCall(self, call_context):
         """
         Resolve the parameters for an API calls based on a call context for this API call.
         @param call_context: the call context to get the parameter information for
         @return: a list of ParameterContext data objects.
         """
         resolved_api_parameters = []
-        api_signature = self._get_api_signature(call_context.called_function_name)
-        push_addresses = self._get_push_addresses_before_target_address(call_context.address_of_call)
-        resolved_api_parameters = self._match_push_addresses_to_signature(push_addresses, api_signature)
+        api_signature = self._getApiSignature(call_context.called_function_name)
+        push_addresses = self._getPushAddressesBeforeTargetAddress(call_context.address_of_call)
+        resolved_api_parameters = self._matchPushAddressesToSignature(push_addresses, api_signature)
         return resolved_api_parameters
 
-    def _match_push_addresses_to_signature(self, push_addresses, api_signature):
+    def _matchPushAddressesToSignature(self, push_addresses, api_signature):
         """
-        Combine the results of I{_get_push_addresses_before_target_address} and I{_get_api_signature} in order to
+        Combine the results of I{_getPushAddressesBeforeTargetAddress} and I{_getApiSignature} in order to
         produce a list of ParameterContext data objects.
         @param push_addresses: the identified push addresses before a function call that shall be matched to a function
                                signature
         @type push_addresses: a list of int
         @param api_signature: information about a function definition with
                               parameter names, types, and so on.
-        @type api_signature: a dictionary with the layout as returned by I{_get_api_signature}
+        @type api_signature: a dictionary with the layout as returned by I{_getApiSignature}
         @return: a list of ParameterContext data objects.
         """
         matched_parameters = []
             matched_parameters.append(param_ctx)
         return matched_parameters
 
-    def _get_api_signature(self, api_name):
+    def _getApiSignature(self, api_name):
         """
         Get the signature for a function by using IDA's I{GetType()}. The string is then parsed with a Regex and
         returned as a dictionary.
                     type_and_name["name"] = parameter[parameter.rfind(" "):].strip()
                     api_signature["parameters"].append(type_and_name)
         else:
-            print ("[-] SemanticIdentifier._get_api_signature: No API/function signature for \"%s\" @ 0x%x available.") \
-                % (api_name, api_location)
+            print ("[-] SemanticIdentifier._getApiSignature: No API/function signature for \"%s\" @ 0x%x available. " \
+            + "(non-critical)") % (api_name, api_location)
         # TODO:
         # here should be a check for the calling convention
         # currently, list list is simply reversed to match the order parameters are pushed to the stack
         api_signature["parameters"].reverse()
         return api_signature
 
-    def _get_push_addresses_before_target_address(self, address):
+    def _getPushAddressesBeforeTargetAddress(self, address):
         """
         Get the addresses of all push instructions in the basic block preceding the given address.
         @param address: address to get the push addresses for.
                         break
         return push_addresses
 
-    def get_last_result(self):
-        """
-        Get the last scan result as retrieved by I{scan_by_references}.
+    def createFunctionGraph(self, func_address):
+        graph = {"root": func_address, "nodes": {}}
+        unexplored = set()
+        if func_address in self.last_scan_result.keys():
+            graph["nodes"][func_address] = self.last_scan_result[func_address].calls_from
+            unexplored = set(self.last_scan_result[func_address].calls_from)
+            while len(unexplored) > 0:
+                current_function = unexplored.pop()
+                if current_function in graph["nodes"].keys() or current_function not in self.last_scan_result.keys():
+                    continue
+                else:
+                    graph["nodes"][current_function] = self.last_scan_result[current_function].calls_from
+                    new_functions = \
+                        set(self.last_scan_result[current_function].calls_from).difference(set(graph["nodes"].keys()))
+                    unexplored.update(new_functions)
+        return graph
+
+    def getLastScanResult(self):
+        """
+        Get the last scan result as retrieved by I{scanByReferences}.
         @return: a dictionary with key/value entries of the following form: (function_address, FunctionContext)
         """
-        return self.last_result
+        return self.last_scan_result
 
-    def print_last_result(self):
+    def printLastScanResult(self):
         """
         nicely print the last scan result (mostly used for debugging)
         """
-        for function_address in self.last_result.keys():
+        for function_address in self.last_scan_result.keys():
             print ("0x%x - %s -> ") % (function_address, self.ida_proxy.GetFunctionName(function_address)) \
-                + ", ".join(self.get_tags_for_function_address(function_address))
-            for call_ctx in self.last_result[function_address].call_contexts:
+                + ", ".join(self.getTagsForFunctionAddress(function_address))
+            for call_ctx in self.last_scan_result[function_address].call_contexts:
                 print ("    0x%x - %s (%s)") % (call_ctx.address_of_call, call_ctx.called_function_name, call_ctx.tag)

idascope/core/WinApiProvider.py

 
 from helpers import JsonHelper
 from helpers.Downloader import Downloader
+from IdaProxy import IdaProxy
 
 
 class WinApiProvider():
         print ("[|] Loading WinApiProvider")
         self.os = os
         self.string = string
+        self.ida_proxy = IdaProxy()
         self.downloader = Downloader()
         self.downloader.downloadFinished.connect(self.onDownloadFinished)
         self.idascope_config = idascope_config
         self.winapi_data = {}
         if self.idascope_config.winapi_load_keyword_database:
-            self._load_keywords()
+            self._loadKeywords()
         self.online_msdn_enabled = self.idascope_config.winapi_online_enabled
         self.last_delivered_filepath = self.idascope_config.winapi_rootdir
         self.backward_history = []
         self.is_appending_to_history = True
         self.download_receivers = []
 
-    def _load_keywords(self):
+    def _loadKeywords(self):
         """
         Loads the keywords database from the file specified in the config.
         """
         keywords_file = open(self.idascope_config.winapi_keywords_file, "r")
         self.winapi_data = json.loads(keywords_file.read(), object_hook=JsonHelper.decode_dict)
 
-    def register_data_receiver(self, receiving_function):
+    def registerDataReceiver(self, receiving_function):
         """
         (Observer Pattern) Register a data receiver for downloaded data. Each time an
         online lookup is performed, the data receiver is provided with the downloaded content.
         """
         When a download of MSDN data is finished, notice all receivers.
         """
+        print "WinApiProvider.onDownloadFinished(): DOWNLOAD FINISHED"
         data = self.downloader.get_data()
         if not data:
             data = "Download failed! Try again or check your Internet connection."
         for receiver in self.download_receivers:
             receiver(data)
 
-    def has_offline_msdn_available(self):
+    def hasOfflineMsdnAvailable(self):
         """
         Determines whether the offline MSDN database is available or not.
         This is evaluated based on whether the keywords database has been loaded or not.
             return True
         return False
 
-    def has_online_msdn_available(self):
+    def hasOnlineMsdnAvailable(self):
         """
         Determines whether the online MSDN database is available or not.
         @return: (bool) setting of the online lookup flag
         """
         return self.online_msdn_enabled
 
-    def set_online_msdn_enabled(self, enabled):
+    def setOnlineMsdnEnabled(self, enabled):
         """
         Change the state of the online lookup availability.
         """
         self.online_msdn_enabled = enabled
 
-    def get_keywords_for_initial(self, keyword_initial):
+    def getKeywordsForInitial(self, keyword_initial):
         """
         Get all keywords that start with the given initial character.
         @param keyword_initial: an initial character
         else:
             return []
 
-    def get_keyword_content(self, keyword):
+    def getKeywordContent(self, keyword):
         """
         Get the content for this keyword.
         @type keyword: str
         @return: (str) HTML content.
         """
-        api_filenames = self._get_api_filenames(keyword)
+        api_filenames = self._getApiFilenames(keyword)
         if len(api_filenames) == 1:
             api_filenames = [self.idascope_config.winapi_rootdir + api_filenames[0]]
         elif self.online_msdn_enabled and len(api_filenames) == 0:
-            return self._get_online_msdn_content(keyword)
-        return self._get_document_content(api_filenames)
+            return self._getOnlineMsdnContent(keyword)
+        return self._getDocumentContent(api_filenames)
 
-    def get_linked_document_content(self, url):
+    def getLinkedDocumentContent(self, url):
         """
         Get the content for a requested linked document
         @param url: URL of the requested file
                 url_str = url_str[:url_str.rfind("#")]
             if url_str != "":
                 filename = self.os.path.join(str(self.last_delivered_filepath), str(url_str))
-                document_content = self._get_single_document_content(filename)
+                document_content = self._getSingleDocumentContent(filename)
             return document_content, anchor
         else:
-            return self._get_single_document_content(url.toString()), anchor
+            return self._getSingleDocumentContent(url.toString()), anchor
 
-    def has_backward_history(self):
+    def hasBackwardHistory(self):
         """
         Get information about whether backward history stepping is available or not.
         @return: (boolean) telling about availability of history stepping.
         """
         return len(self.backward_history) > 1
 
-    def has_forward_history(self):
+    def hasForwardHistory(self):
         """
         Get information about whether forward history stepping is available or not.
         @return: (boolean) telling about availability of history stepping.
         """
         return len(self.forward_history) > 1
 
-    def get_previous_document_content(self):
+    def getPreviousDocumentContent(self):
         """
         Get the content of the previously accessed document. This implements the well-known "back"-button
         functionality.
         @return: a tuple (str, str) with content and anchor within the content
         """
-        self._cleanup_histories()
+        self._cleanupHistories()
         # first move latest visited document to forward queue.
         if len(self.backward_history) > 0:
             history_entry = self.backward_history.pop()
             if len(self.backward_history) > 0:
                 self.is_appending_to_history = False
                 history_entry = self.backward_history[-1]
-                document_content = self._get_document_content(history_entry[0]), history_entry[1]
+                document_content = self._getDocumentContent(history_entry[0]), history_entry[1]
                 self.is_appending_to_history = True
                 return document_content
         return ("", "")
 
-    def get_next_document_content(self):
+    def getNextDocumentContent(self):
         """
         Get the content of the previously accessed document. This implements the well-known "back"-button
         functionality.
         @return: a tuple (str, str) with content and anchor within the content
         """
         # first move latest visited document again to backward queue.
-        self._cleanup_histories()
+        self._cleanupHistories()
         if len(self.forward_history) > 0:
             history_entry = self.forward_history.pop()
             self.backward_history.append(history_entry)
             self.is_appending_to_history = False
-            document_content = self._get_document_content(history_entry[0]), history_entry[1]
+            document_content = self._getDocumentContent(history_entry[0]), history_entry[1]
             self.is_appending_to_history = True
             return document_content
         return ("", "")
 
-    def _cleanup_histories(self):
+    def _cleanupHistories(self):
         """
         Eliminate subsequent similar items from history lists
         """
-        self.backward_history = self._cleanup_list(self.backward_history)
-        self.forward_history = self._cleanup_list(self.forward_history)
+        self.backward_history = self._cleanupList(self.backward_history)
+        self.forward_history = self._cleanupList(self.forward_history)
 
-    def _cleanup_list(self, input_list):
+    def _cleanupList(self, input_list):
         """
         Eliminate subsequent similar items from a list
         @param input_list: A list of arbitrary items
             last_entry = entry
         return cleaned_list
 
-    def _get_api_filenames(self, keyword):
+    def _getApiFilenames(self, keyword):
         """
         Get filenames that are associated with the given keyword.
         @param keyword: keyword to get the filenames for
                     return self.winapi_data[keyword_initial][keyword]
         return []
 
-    def _get_document_content(self, filenames):
+    def _getDocumentContent(self, filenames):
         """
         Produce the document content for a given list of filenames.
         If there are multiple filenames, no document content is returned but a rendered list fo the filenames,
         """
         document_content = "<p>No entries for your query.</p>"
         if len(filenames) > 1:
-            document_content = self._generate_html_list_of_filenames(filenames)
+            document_content = self._generateHtmlListOfFilenames(filenames)
             if self.is_appending_to_history:
                 self.forward_history = []
                 self.backward_history.append((filenames, ""))
-                self._cleanup_histories()
+                self._cleanupHistories()
         elif len(filenames) == 1:
-            document_content = self._get_single_document_content(filenames[0])
+            document_content = self._getSingleDocumentContent(filenames[0])
         return document_content
 
-    def _generate_html_list_of_filenames(self, filenames):
+    def _generateHtmlListOfFilenames(self, filenames):
         """
         Convert a list of filenames as string into a mini-HTML document with the list entries as links to the files
         in a bullet list.
                 filename, filename)
         return document_content
 
-    def _get_single_document_content(self, filename):
+    def _getSingleDocumentContent(self, filename):
         """
         Load a single document by filename and return its content.
         @param filename: the filename to load
             if self.is_appending_to_history:
                 self.forward_history = []
                 self.backward_history.append(([filename], ""))
-                self._cleanup_histories()
+                self._cleanupHistories()
         except Exception as exc:
             document_content = "<html><head /><body>Well, something has gone wrong here. Try again with some" \
                 + " proper API name.<hr /><p>Exception: %s</p></body></html>" % exc
         return document_content
 
-    def _get_online_msdn_content(self, keyword):
+    def _getOnlineMsdnContent(self, keyword):
         """
         This functions downloads content from the MSDN website. It does not return the
         information instantly but provides it through a callback that can be registered
-        with the function I{register_data_receiver}.
+        with the function I{registerDataReceiver}.
         @param keyword: the keyword to look up in MSDN
         @type keyword: str
         @return: (str) a waiting message if the keyword has been queried or a negative answer if
         feed_content = self.downloader.download(feed_url)
         if not feed_content:
             return "<p>Could not access the MSDN feed. Check your Internet connection.</p>"
-        msdn_url = self._get_first_msdn_link(feed_content)
+        msdn_url = self._getFirstMsdnLink(feed_content)
         if msdn_url != "":
-            return self.cleanup_downloaded_html(self.downloader.download(msdn_url))
+            return self._cleanupDownloadedHtml(self.downloader.download(msdn_url))
             # FIXME: should threading in IDA ever work, use this...
-            # self.downloader.download_threaded(msdn_url)
+            # self.downloader.setDownloadUrl(msdn_url)
+            # self.downloader.downloadStoredUrl
+            # self.ida_proxy.execute_sync(self.downloader.downloadStoredUrl, self.ida_proxy.MFF_FAST)
             # return "<p>Fetching information from online MSDN, please wait...</p>"
         else:
             return "<p>Even MSDN can't help you on this one.</p>"
 
-    def _get_first_msdn_link(self, feed_content):
+    def _getFirstMsdnLink(self, feed_content):
         """
         Parses the first MSDN URL from a RSS feed.
         @param feed_content: a rss feed output
                 feed_content = feed_content[feed_content.find("</link>") + 7:]
             return ""
 
-    def cleanup_downloaded_html(self, content):
+    def _cleanupDownloadedHtml(self, content):
         content = content[content.find("<div class=\"topic\""):content.find("<div id=\"contentFeedback\"")]
         content = "".join(s for s in content if s in self.string.printable)
         return content

idascope/core/helpers/Downloader.py

 #
 ########################################################################
 
+# TODO: Make this finally multi-threaded.
+
 from PySide import QtCore
 import httplib
 
         self.ThreadedDownloader = ThreadedDownloader
         self._data = None
         self.download_thread = None
+        self.download_url = ""
 
-    def download_threaded(self, url):
+    def downloadThreaded(self, url):
         """
         Start a new download thread. Will notify via signal "downloadFinished" when done.
         @param url: The URL to download from.
         # clean up thread object
         self.download_worker.shutdown()
 
+    def setDownloadUrl(self, url):
+        # print "Downloader.setDownloadUrl(): called, setting download_url to: %s" % self.download_url
+        self.download_url = url
+
+    def downloadStoredUrl(self):
+        # print "Downloader.downloadStoredUrl(): called, download_url is: %s" % self.download_url
+        self.downloadSignalled(self.download_url)
+
     def download(self, url):
         """
         Start a blocking download. Will return the downloaded content when done.
         @type url: str
         @return: (str) the downloaded content.
         """
+        # print "Downloader.download(): type of received parameter: ", type(url)
         host = url[7:url.find("/", 7)]
         path = url[url.find("/", 7):]
         try:
             self._data = None
         return self._data
 
-    def download_signalled(self, url):
+    def downloadSignalled(self, url):
+        # print "Downloader.downloadSignalled(): called"
         self.download(url)
         self.downloadFinished.emit()
 
-    def get_data(self):
+    def getData(self):
         """
         Returns the previously downloaded data.
         """
 
     def _onThreadFinished(self):
         if self.download_thread:
-            self._data = self.download_thread.get_data()
+            self._data = self.download_thread.getData()
             self.download_thread = None
         self.downloadFinished.emit()

idascope/core/helpers/GraphHelper.py

+#!/usr/bin/python
+
+from idascope.core.IdaProxy import IdaProxy
+
+
+class GraphHelper():
+
+    def __init__(self):
+        self.ida_proxy = IdaProxy()
+
+    def handleGraphRecursions(self, graph):
+        """
+        Analyze an arbitrary graph structure for strongly connected components. If such are found, break the
+        loops and return the graph with an additional key "recursions" that can be used to indicate that these
+        loops have been broken.
+        @param graph: a dictionary describing a directed graph, with keys as nodes and values as successors.
+        @type graph: (dict)
+        @return: (dict) the modified graph with an additional key "recursions", indicating the broken recursions.
+        """
+        strongly_connected = self.calculateStronglyConnectedComponents(graph["nodes"])
+        non_trivial_loops = [component for component in strongly_connected if len(component) > 1]
+        if len(non_trivial_loops) > 0:
+            print "here are loops: 0x%x >> %s" % \
+                (graph["root"], ", ".join(["0x%x" % addr for addr in non_trivial_loops[0]]))
+            self.renderGraph(graph)
+
+    def renderGraph(self, graph):
+        for function_addr in graph["nodes"].keys():
+            refs = graph["nodes"][function_addr]
+            print "0x%x (%s)" % (function_addr, self.ida_proxy.GetFunctionName(function_addr))
+            # for ref in refs:
+            #     print "  > 0x%x (%s)" % (ref, self.ida_proxy.GetFunctionName(ref))
+
+    def calcAvgOutDegree(self, graph):
+        out_refs = 0
+        for function_addr in graph["nodes"].keys():
+            out_refs += len(graph["nodes"][function_addr])
+        print "0x%x -> %2.2f edges per node" % (graph["root"], 1.0 * out_refs / len(graph["nodes"].keys()))
+
+    def calculateStronglyConnectedComponents(self, graph):
+        """
+        Tarjan's Algorithm (named for its discoverer, Robert Tarjan) is a graph theory algorithm
+        for finding the strongly connected components of a graph.
+        This can be used to find loops.
+        Based on: http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
+
+        Implementation by Dries Verdegem:
+        http://www.logarithmic.net/pfh-files/blog/01208083168/tarjan.py
+        Taken from Dr. Paul Harrison Blog:
+        http://www.logarithmic.net/pfh/blog/01208083168
+
+        @param graph: a dictionary describing a directed graph, with keys as nodes and values as successors.
+        @type graph: (dict)
+        @return: (a list of tuples) describing the SCCs
+        """
+
+        index_counter = [0]
+        stack = []
+        lowlinks = {}
+        index = {}
+        result = []
+
+        def calculateSccForNode(node):
+            # set the depth index for this node to the smallest unused index
+            index[node] = index_counter[0]
+            lowlinks[node] = index_counter[0]
+            index_counter[0] += 1
+            stack.append(node)
+            # Consider successors of `node`
+            try:
+                successors = graph[node]
+            except:
+                successors = []
+            for successor in successors:
+                if successor not in lowlinks:
+                    # Successor has not yet been visited; recurse on it
+                    calculateSccForNode(successor)
+                    lowlinks[node] = min(lowlinks[node], lowlinks[successor])
+                elif successor in stack:
+                    # the successor is in the stack and hence in the current strongly connected component (SCC)
+                    lowlinks[node] = min(lowlinks[node], index[successor])
+            # If `node` is a root node, pop the stack and generate an SCC
+            if lowlinks[node] == index[node]:
+                connected_component = []
+                while True:
+                    successor = stack.pop()
+                    connected_component.append(successor)
+                    if successor == node:
+                        break
+                component = tuple(connected_component)
+                # storing the result
+                result.append(component)
+        for node in graph:
+            if node not in lowlinks:
+                calculateSccForNode(node)
+        return result

idascope/core/helpers/PatternManager.py

+#!/usr/bin/python
+########################################################################
+# Copyright (c) 2012
+# Daniel Plohmann <daniel.plohmann<at>gmail<dot>com>
+# Alexander Hanel <alexander.hanel<at>gmail<dot>com>
+# All rights reserved.
+########################################################################
+#
+#  This file is part of IDAscope
+#
+#  IDAscope is free software: you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see
+#  <http://www.gnu.org/licenses/>.
+#
+########################################################################
+
+import struct
+
+
+class MutablePattern(str):
+    """
+    simple wrapper class used in Felix' original implementation to identify such patterns that can be mutated / padded
+    to unsigned int ( 2byte) or long (4 byte).
+    """
+    pass
+
+
+class VariablePattern(str):
+    """
+    simple wrapper class to indicate that this signature may contain the placeholder character "?" to indicate
+    skip bytes.
+    """
+    pass
+
+
+class PatternManager:
+    """
+    This class stores patterns for crypto detection and allows manipulations on them, such as tokenizing into
+    certain bytes lengths in order to increase detection robustness.
+    """
+    # THe huge dictionary, storing the signature information in key/value form: ("byte-signature", "signature_name").
+    signatures = {
+
+        ################ extracted from PyQemu, kerckhoffr
+
+        MutablePattern("\x63\x7c\x77\x7b\xf2\x6b\x6f\xc5\x30\x01\x67\x2b\xfe\xd7\xab\x76"): "AES forward box",
+        MutablePattern("\xca\x82\xc9\x7d\xfa\x59\x47\xf0\xad\xd4\xa2\xaf\x9c\xa4\x72\xc0"): "AES forward box",
+        MutablePattern("\xb7\xfd\x93\x26\x36\x3f\xf7\xcc\x34\xa5\xe5\xf1\x71\xd8\x31\x15"): "AES forward box",
+        MutablePattern("\x04\xc7\x23\xc3\x18\x96\x05\x9a\x07\x12\x80\xe2\xeb\x27\xb2\x75"): "AES forward box",
+        MutablePattern("\x09\x83\x2c\x1a\x1b\x6e\x5a\xa0\x52\x3b\xd6\xb3\x29\xe3\x2f\x84"): "AES forward box",
+        MutablePattern("\x53\xd1\x00\xed\x20\xfc\xb1\x5b\x6a\xcb\xbe\x39\x4a\x4c\x58\xcf"): "AES forward box",
+        MutablePattern("\xd0\xef\xaa\xfb\x43\x4d\x33\x85\x45\xf9\x02\x7f\x50\x3c\x9f\xa8"): "AES forward box",
+        MutablePattern("\x51\xa3\x40\x8f\x92\x9d\x38\xf5\xbc\xb6\xda\x21\x10\xff\xf3\xd2"): "AES forward box",
+        MutablePattern("\xcd\x0c\x13\xec\x5f\x97\x44\x17\xc4\xa7\x7e\x3d\x64\x5d\x19\x73"): "AES forward box",
+        MutablePattern("\x60\x81\x4f\xdc\x22\x2a\x90\x88\x46\xee\xb8\x14\xde\x5e\x0b\xdb"): "AES forward box",
+        MutablePattern("\xe0\x32\x3a\x0a\x49\x06\x24\x5c\xc2\xd3\xac\x62\x91\x95\xe4\x79"): "AES forward box",
+        MutablePattern("\xe7\xc8\x37\x6d\x8d\xd5\x4e\xa9\x6c\x56\xf4\xea\x65\x7a\xae\x08"): "AES forward box",
+        MutablePattern("\xba\x78\x25\x2e\x1c\xa6\xb4\xc6\xe8\xdd\x74\x1f\x4b\xbd\x8b\x8a"): "AES forward box",
+        MutablePattern("\x70\x3e\xb5\x66\x48\x03\xf6\x0e\x61\x35\x57\xb9\x86\xc1\x1d\x9e"): "AES forward box",
+        MutablePattern("\xe1\xf8\x98\x11\x69\xd9\x8e\x94\x9b\x1e\x87\xe9\xce\x55\x28\xdf"): "AES forward box",
+        MutablePattern("\x8c\xa1\x89\x0d\xbf\xe6\x42\x68\x41\x99\x2d\x0f\xb0\x54\xbb\x16"): "AES forward box",
+        MutablePattern("\x52\x09\x6a\xd5\x30\x36\xa5\x38\xbf\x40\xa3\x9e\x81\xf3\xd7\xfb"): "AES inverse box",
+        MutablePattern("\x7c\xe3\x39\x82\x9b\x2f\xff\x87\x34\x8e\x43\x44\xc4\xde\xe9\xcb"): "AES inverse box",
+        MutablePattern("\x54\x7b\x94\x32\xa6\xc2\x23\x3d\xee\x4c\x95\x0b\x42\xfa\xc3\x4e"): "AES inverse box",
+        MutablePattern("\x08\x2e\xa1\x66\x28\xd9\x24\xb2\x76\x5b\xa2\x49\x6d\x8b\xd1\x25"): "AES inverse box",
+        MutablePattern("\x72\xf8\xf6\x64\x86\x68\x98\x16\xd4\xa4\x5c\xcc\x5d\x65\xb6\x92"): "AES inverse box",
+        MutablePattern("\x6c\x70\x48\x50\xfd\xed\xb9\xda\x5e\x15\x46\x57\xa7\x8d\x9d\x84"): "AES inverse box",
+        MutablePattern("\x90\xd8\xab\x00\x8c\xbc\xd3\x0a\xf7\xe4\x58\x05\xb8\xb3\x45\x06"): "AES inverse box",
+        MutablePattern("\xd0\x2c\x1e\x8f\xca\x3f\x0f\x02\xc1\xaf\xbd\x03\x01\x13\x8a\x6b"): "AES inverse box",
+        MutablePattern("\x3a\x91\x11\x41\x4f\x67\xdc\xea\x97\xf2\xcf\xce\xf0\xb4\xe6\x73"): "AES inverse box",
+        MutablePattern("\x96\xac\x74\x22\xe7\xad\x35\x85\xe2\xf9\x37\xe8\x1c\x75\xdf\x6e"): "AES inverse box",
+        MutablePattern("\x47\xf1\x1a\x71\x1d\x29\xc5\x89\x6f\xb7\x62\x0e\xaa\x18\xbe\x1b"): "AES inverse box",
+        MutablePattern("\xfc\x56\x3e\x4b\xc6\xd2\x79\x20\x9a\xdb\xc0\xfe\x78\xcd\x5a\xf4"): "AES inverse box",
+        MutablePattern("\x1f\xdd\xa8\x33\x88\x07\xc7\x31\xb1\x12\x10\x59\x27\x80\xec\x5f"): "AES inverse box",
+        MutablePattern("\x60\x51\x7f\xa9\x19\xb5\x4a\x0d\x2d\xe5\x7a\x9f\x93\xc9\x9c\xef"): "AES inverse box",
+        MutablePattern("\xa0\xe0\x3b\x4d\xae\x2a\xf5\xb0\xc8\xeb\xbb\x3c\x83\x53\x99\x61"): "AES inverse box",
+        MutablePattern("\x17\x2b\x04\x7e\xba\x77\xd6\x26\xe1\x69\x14\x63\x55\x21\x0c\x7d"): "AES inverse box",
+
+        MutablePattern("\x0e\x04\x0d\x01\x02\x0f\x0b\x08\x03\x0a\x06\x0c\x05\x09\x00\x07"): "Serpent S-Box",
+        MutablePattern("\x00\x0f\x07\x04\x0e\x02\x0d\x01\x0a\x06\x0c\x0b\x09\x05\x03\x08"): "Serpent S-Box",
+        MutablePattern("\x04\x01\x0e\x08\x0d\x06\x02\x0b\x0f\x0c\x09\x07\x03\x0a\x05\x00"): "Serpent S-Box",
+        MutablePattern("\x0f\x0c\x08\x02\x04\x09\x01\x07\x05\x0b\x03\x0e\x0a\x00\x06\x0d"): "Serpent S-Box",
+        MutablePattern("\x0f\x01\x08\x0e\x06\x0b\x03\x04\x09\x07\x02\x0d\x0c\x00\x05\x0a"): "Serpent S-Box",
+        MutablePattern("\x03\x0d\x04\x07\x0f\x02\x08\x0e\x0c\x00\x01\x0a\x06\x09\x0b\x05"): "Serpent S-Box",
+        MutablePattern("\x00\x0e\x07\x0b\x0a\x04\x0d\x01\x05\x08\x0c\x06\x09\x03\x02\x0f"): "Serpent S-Box",
+        MutablePattern("\x0d\x08\x0a\x01\x03\x0f\x04\x02\x0b\x06\x07\x0c\x00\x05\x0e\x09"): "Serpent S-Box",
+        MutablePattern("\x0a\x00\x09\x0e\x06\x03\x0f\x05\x01\x0d\x0c\x07\x0b\x04\x02\x08"): "Serpent S-Box",
+        MutablePattern("\x0d\x07\x00\x09\x03\x04\x06\x0a\x02\x08\x05\x0e\x0c\x0b\x0f\x01"): "Serpent S-Box",
+        MutablePattern("\x0d\x06\x04\x09\x08\x0f\x03\x00\x0b\x01\x02\x0c\x05\x0a\x0e\x07"): "Serpent S-Box",
+        MutablePattern("\x01\x0a\x0d\x00\x06\x09\x08\x07\x04\x0f\x0e\x03\x0b\x05\x02\x0c"): "Serpent S-Box",
+        MutablePattern("\x07\x0d\x0e\x03\x00\x06\x09\x0a\x01\x02\x08\x05\x0b\x0c\x04\x0f"): "Serpent S-Box",
+        MutablePattern("\x0d\x08\x0b\x05\x06\x0f\x00\x03\x04\x07\x02\x0c\x01\x0a\x0e\x09"): "Serpent S-Box",
+        MutablePattern("\x0a\x06\x09\x00\x0c\x0b\x07\x0d\x0f\x01\x03\x0e\x05\x02\x08\x04"): "Serpent S-Box",
+        MutablePattern("\x03\x0f\x00\x06\x0a\x01\x0d\x08\x09\x04\x05\x0b\x0c\x07\x02\x0e"): "Serpent S-Box",
+        MutablePattern("\x02\x0c\x04\x01\x07\x0a\x0b\x06\x08\x05\x03\x0f\x0d\x00\x0e\x09"): "Serpent S-Box",
+        MutablePattern("\x0e\x0b\x02\x0c\x04\x07\x0d\x01\x05\x00\x0f\x0a\x03\x09\x08\x06"): "Serpent S-Box",
+        MutablePattern("\x04\x02\x01\x0b\x0a\x0d\x07\x08\x0f\x09\x0c\x05\x06\x03\x00\x0e"): "Serpent S-Box",
+        MutablePattern("\x0b\x08\x0c\x07\x01\x0e\x02\x0d\x06\x0f\x00\x09\x0a\x04\x05\x03"): "Serpent S-Box",
+        MutablePattern("\x0c\x01\x0a\x0f\x09\x02\x06\x08\x00\x0d\x03\x04\x0e\x07\x05\x0b"): "Serpent S-Box",
+        MutablePattern("\x0a\x0f\x04\x02\x07\x0c\x09\x05\x06\x01\x0d\x0e\x00\x0b\x03\x08"): "Serpent S-Box",
+        MutablePattern("\x09\x0e\x0f\x05\x02\x08\x0c\x03\x07\x00\x04\x0a\x01\x0d\x0b\x06"): "Serpent S-Box",
+        MutablePattern("\x04\x03\x02\x0c\x09\x05\x0f\x0a\x0b\x0e\x01\x07\x06\x00\x08\x0d"): "Serpent S-Box",
+        MutablePattern("\x04\x0b\x02\x0e\x0f\x00\x08\x0d\x03\x0c\x09\x07\x05\x0a\x06\x01"): "Serpent S-Box",
+        MutablePattern("\x0d\x00\x0b\x07\x04\x09\x01\x0a\x0e\x03\x05\x0c\x02\x0f\x08\x06"): "Serpent S-Box",
+        MutablePattern("\x01\x04\x0b\x0d\x0c\x03\x07\x0e\x0a\x0f\x06\x08\x00\x05\x09\x02"): "Serpent S-Box",
+        MutablePattern("\x06\x0b\x0d\x08\x01\x04\x0a\x07\x09\x05\x00\x0f\x0e\x02\x03\x0c"): "Serpent S-Box",
+        MutablePattern("\x0d\x02\x08\x04\x06\x0f\x0b\x01\x0a\x09\x03\x0e\x05\x00\x0c\x07"): "Serpent S-Box",
+        MutablePattern("\x01\x0f\x0d\x08\x0a\x03\x07\x04\x0c\x05\x06\x0b\x00\x0e\x09\x02"): "Serpent S-Box",
+        MutablePattern("\x07\x0b\x04\x01\x09\x0c\x0e\x02\x00\x06\x0a\x0d\x0f\x03\x05\x08"): "Serpent S-Box",
+        MutablePattern("\x02\x01\x0e\x07\x04\x0a\x08\x0d\x0f\x0c\x09\x00\x03\x05\x06\x0b"): "Serpent S-Box",
+
+        MutablePattern("\x0e\x03\x04\x08\x01\x0c\x0a\x0f\x07\x0d\x09\x06\x0b\x02\x00\x05"): "Serpent inverse S-Box",
+        MutablePattern("\x00\x07\x05\x0e\x03\x0d\x09\x02\x0f\x0c\x08\x0b\x0a\x06\x04\x01"): "Serpent inverse S-Box",
+        MutablePattern("\x0f\x01\x06\x0c\x00\x0e\x05\x0b\x03\x0a\x0d\x07\x09\x04\x02\x08"): "Serpent inverse S-Box",
+        MutablePattern("\x0d\x06\x03\x0a\x04\x08\x0e\x07\x02\x05\x0c\x09\x01\x0f\x0b\x00"): "Serpent inverse S-Box",
+        MutablePattern("\x0d\x01\x0a\x06\x07\x0e\x04\x09\x02\x08\x0f\x05\x0c\x0b\x03\x00"): "Serpent inverse S-Box",
+        MutablePattern("\x09\x0a\x05\x00\x02\x0f\x0c\x03\x06\x0d\x0b\x0e\x08\x01\x07\x04"): "Serpent inverse S-Box",
+        MutablePattern("\x00\x07\x0e\x0d\x05\x08\x0b\x02\x09\x0c\x04\x03\x0a\x06\x01\x0f"): "Serpent inverse S-Box",
+        MutablePattern("\x0c\x03\x07\x04\x06\x0d\x09\x0a\x01\x0f\x02\x08\x0b\x00\x0e\x05"): "Serpent inverse S-Box",
+        MutablePattern("\x01\x08\x0e\x05\x0d\x07\x04\x0b\x0f\x02\x00\x0c\x0a\x09\x03\x06"): "Serpent inverse S-Box",
+        MutablePattern("\x02\x0f\x08\x04\x05\x0a\x06\x01\x09\x03\x07\x0d\x0c\x00\x0b\x0e"): "Serpent inverse S-Box",
+        MutablePattern("\x07\x09\x0a\x06\x02\x0c\x01\x0f\x04\x03\x0d\x08\x0b\x00\x0e\x05"): "Serpent inverse S-Box",
+        MutablePattern("\x03\x00\x0e\x0b\x08\x0d\x04\x07\x06\x05\x01\x0c\x0f\x02\x0a\x09"): "Serpent inverse S-Box",
+        MutablePattern("\x04\x08\x09\x03\x0e\x0b\x05\x00\x0a\x06\x07\x0c\x0d\x01\x02\x0f"): "Serpent inverse S-Box",
+        MutablePattern("\x06\x0c\x0a\x07\x08\x03\x04\x09\x01\x0f\x0d\x02\x0b\x00\x0e\x05"): "Serpent inverse S-Box",
+        MutablePattern("\x03\x09\x0d\x0a\x0f\x0c\x01\x06\x0e\x02\x00\x05\x04\x07\x0b\x08"): "Serpent inverse S-Box",
+        MutablePattern("\x02\x05\x0e\x00\x09\x0a\x03\x0d\x07\x08\x04\x0b\x0c\x06\x0f\x01"): "Serpent inverse S-Box",
+        MutablePattern("\x0d\x03\x00\x0a\x02\x09\x07\x04\x08\x0f\x05\x06\x01\x0c\x0e\x0b"): "Serpent inverse S-Box",
+        MutablePattern("\x09\x07\x02\x0c\x04\x08\x0f\x05\x0e\x0d\x0b\x01\x03\x06\x00\x0a"): "Serpent inverse S-Box",
+        MutablePattern("\x0e\x02\x01\x0d\x00\x0b\x0c\x06\x07\x09\x04\x03\x0a\x05\x0f\x08"): "Serpent inverse S-Box",
+        MutablePattern("\x0a\x04\x06\x0f\x0d\x0e\x08\x03\x01\x0b\x0c\x00\x02\x07\x05\x09"): "Serpent inverse S-Box",
+        MutablePattern("\x08\x01\x05\x0a\x0b\x0e\x06\x0d\x07\x04\x02\x0f\x00\x09\x0c\x03"): "Serpent inverse S-Box",
+        MutablePattern("\x0c\x09\x03\x0e\x02\x07\x08\x04\x0f\x06\x00\x0d\x05\x0a\x0b\x01"): "Serpent inverse S-Box",
+        MutablePattern("\x09\x0c\x04\x07\x0a\x03\x0f\x08\x05\x00\x0b\x0e\x06\x0d\x01\x02"): "Serpent inverse S-Box",
+        MutablePattern("\x0d\x0a\x02\x01\x00\x05\x0c\x0b\x0e\x04\x07\x08\x03\x0f\x09\x06"): "Serpent inverse S-Box",
+        MutablePattern("\x05\x0f\x02\x08\x00\x0c\x0e\x0b\x06\x0a\x0d\x01\x09\x07\x03\x04"): "Serpent inverse S-Box",
+        MutablePattern("\x01\x06\x0c\x09\x04\x0a\x0f\x03\x0e\x05\x07\x02\x0b\x00\x08\x0d"): "Serpent inverse S-Box",
+        MutablePattern("\x0c\x00\x0f\x05\x01\x0d\x0a\x06\x0b\x0e\x08\x02\x04\x03\x07\x09"): "Serpent inverse S-Box",
+        MutablePattern("\x0a\x04\x0d\x0e\x05\x09\x00\x07\x03\x08\x06\x01\x0f\x02\x0c\x0b"): "Serpent inverse S-Box",
+        MutablePattern("\x0d\x07\x01\x0a\x03\x0c\x04\x0f\x02\x09\x08\x06\x0e\x00\x0b\x05"): "Serpent inverse S-Box",
+        MutablePattern("\x0c\x00\x0f\x05\x07\x09\x0a\x06\x03\x0e\x04\x0b\x08\x02\x0d\x01"): "Serpent inverse S-Box",
+        MutablePattern("\x08\x03\x07\x0d\x02\x0e\x09\x00\x0f\x04\x0a\x01\x05\x0b\x06\x0c"): "Serpent inverse S-Box",
+        MutablePattern("\x0b\x01\x00\x0c\x04\x0d\x0e\x03\x06\x0a\x05\x0f\x09\x07\x02\x08"): "Serpent inverse S-Box",
+
+        struct.pack("<I", 0x67452301): "md5/sha1 initial values",
+        struct.pack("<I", 0xefcdab89): "md5/sha1 initial values",
+        struct.pack("<I", 0x98badcfe): "md5/sha1 initial values",
+        struct.pack("<I", 0x10325476): "md5/sha1 initial values",
+        struct.pack("<I", 0xc3d2e1f0): "md5/sha1 initial values",
+
+        struct.pack("<Q", 0xA5A5A5A5A5A5A5A5): "TIGER Initialization",
+        struct.pack("<Q", 0x0123456789ABCDEF): "TIGER Initialization",
+        struct.pack("<Q", 0xFEDCBA9876543210): "TIGER Initialization",
+        struct.pack("<Q", 0xF096A5B4C3B2E187): "TIGER Initialization",
+
+        struct.pack("<I", 0xb7e15163): "RC5 Box",
+        struct.pack("<I", 0x5618cb1c): "RC5 Box",
+        struct.pack("<I", 0xf45044d5): "RC5 Box",
+        struct.pack("<I", 0x9287be8e): "RC5 Box",
+        struct.pack("<I", 0x30bf3847): "RC5 Box",
+        struct.pack("<I", 0xcef6b200): "RC5 Box",
+        struct.pack("<I", 0x6d2e2bb9): "RC5 Box",
+        struct.pack("<I", 0x0b65a572): "RC5 Box",
+        struct.pack("<I", 0xa99d1f2b): "RC5 Box",
+        struct.pack("<I", 0x47d498e4): "RC5 Box",
+        struct.pack("<I", 0xe60c129d): "RC5 Box",
+        struct.pack("<I", 0x84438c56): "RC5 Box",
+        struct.pack("<I", 0x227b060f): "RC5 Box",
+        struct.pack("<I", 0xc0b27fc8): "RC5 Box",
+        struct.pack("<I", 0x5ee9f981): "RC5 Box",
+        struct.pack("<I", 0xfd21733a): "RC5 Box",
+        struct.pack("<I", 0x9b58ecf3): "RC5 Box",
+        struct.pack("<I", 0x399066ac): "RC5 Box",
+        struct.pack("<I", 0xd7c7e065): "RC5 Box",
+        struct.pack("<I", 0x75ff5a1e): "RC5 Box",
+        struct.pack("<I", 0x1436d3d7): "RC5 Box",
+        struct.pack("<I", 0xb26e4d90): "RC5 Box",
+        struct.pack("<I", 0x50a5c749): "RC5 Box",
+        struct.pack("<I", 0xeedd4102): "RC5 Box",
+        struct.pack("<I", 0x8d14babb): "RC5 Box",
+        struct.pack("<I", 0x2b4c3474): "RC5 Box",
+        struct.pack("<I", 0xc983ae2d): "RC5 Box",
+        struct.pack("<I", 0x67bb27e6): "RC5 Box",
+        struct.pack("<I", 0x05f2a19f): "RC5 Box",
+        struct.pack("<I", 0xa42a1b58): "RC5 Box",
+        struct.pack("<I", 0x42619511): "RC5 Box",
+        struct.pack("<I", 0xe0990eca): "RC5 Box",
+
+        MutablePattern("\xb7\xe1\x51\x62\x8a\xed\x2a\x6a\xbf\x71\x58\x80\x9c\xf4\xf3\xc7\x62\xe7\x16\x0f\x38\xb4\xda\x56\xa7\x84\xd9\x04\x51\x90\xcf\xef"):"IDEA Box",
+
+        MutablePattern("\x08\x01\x07\x0d\x06\x0f\x03\x02\x00\x0b\x05\x09\x0e\x0c\x0a\x04"):"Twofish S-Box",
+        MutablePattern("\x02\x08\x0b\x0d\x0f\x07\x06\x0e\x03\x01\x09\x04\x00\x0a\x0c\x05"):"Twofish S-Box",
+        MutablePattern("\x0e\x0c\x0b\x08\x01\x02\x03\x05\x0f\x04\x0a\x06\x07\x00\x09\x0d"):"Twofish S-Box",
+        MutablePattern("\x01\x0e\x02\x0b\x04\x0c\x03\x07\x06\x0d\x0a\x05\x0f\x09\x00\x08"):"Twofish S-Box",
+        MutablePattern("\x0b\x0a\x05\x0e\x06\x0d\x09\x00\x0c\x08\x0f\x03\x02\x04\x07\x01"):"Twofish S-Box",
+        MutablePattern("\x04\x0c\x07\x05\x01\x06\x09\x0a\x00\x0e\x0d\x08\x02\x0b\x03\x0f"):"Twofish S-Box",
+        MutablePattern("\x0d\x07\x0f\x04\x01\x02\x06\x0e\x09\x0b\x03\x00\x08\x05\x0c\x0a"):"Twofish S-Box",
+        MutablePattern("\x0b\x09\x05\x01\x0c\x03\x0d\x0e\x06\x04\x07\x0f\x02\x00\x08\x0a"):"Twofish S-Box",
+
+        ################ by DP - extracted from findcrypt
+
+        MutablePattern("\x35\xbe\x07\x2e\x53\x69\xdb\x28\x6f\xb7\x76\x6b\x0c\x7d\x36\x8b\x92\xbc\xa9\x32\xac\x38\x9c\x42\x63\xc8\x1e\x4f\x24\xe5\xf7\xc9\x61\x8d\x2f\x3f\xb3\x65\x7f\x70\xaf\x9a\xea\xf5\x5b\x98\x90\xb1\x87\x71\x72\xed\x37\x45\x68\xa3\xe3\xef\x5c\xc5\x50\xc1\xd6\xca\x5a\x62\x5f\x26\x09\x5d\x14\x41\xe8\x9d\xce\x40\xfd\x08\x17\x4a\x0f\xc7\xb4\x3e\x12\xfc\x25\x4b\x81\x2c\x04\x78\xcb\xbb\x20\xbd\xf9\x29\x99\xa8\xd3\x60\xdf\x11\x97\x89\x7e\xfa\xe0\x9b\x1f\xd2\x67\xe2\x64\x77\x84\x2b\x9e\x8a\xf1\x6d\x88\x79\x74\x57\xdd\xe6\x39\x7b\xee\x83\xe1\x58\xf2\x0d\x34\xf8\x30\xe9\xb9\x23\x54\x15\x44\x0b\x4d\x66\x3a\x03\xa2\x91\x94\x52\x4c\xc3\x82\xe7\x80\xc0\xb6\x0e\xc2\x6c\x93\xec\xab\x43\x95\xf6\xd8\x46\x86\x05\x8c\xb0\x75\x00\xcc\x85\xd7\x3d\x73\x7a\x48\xe4\xd1\x59\xad\xb8\xc6\xd0\xdc\xa1\xaa\x02\x1d\xbf\xb5\x9f\x51\xc4\xa5\x10\x22\xcf\x01\xba\x8f\x31\x7c\xae\x96\xda\xf0\x56\x47\xd4\xeb\x4e\xd9\x13\x8e\x49\x55\x16\xff\x3b\xf4\xa4\xb2\x06\xa0\xa7\xfb\x1b\x6e\x3c\x33\xcd\x18\x5e\x6a\xd5\xa6\x21\xde\xfe\x2a\x1c\xf3\x0a\x1a\x19\x27\x2d"): "SQUARE_SHARK_dec",
+        MutablePattern("\x75\x32\xbc\xbc\xf3\x21\xec\xec\xc6\x43\x20\x20\xf4\xc9\xb3\xb3\xdb\x03\xda\xda\x7b\x8b\x02\x02\xfb\x2b\xe2\xe2\xc8\xfa\x9e\x9e\x4a\xec\xc9\xc9\xd3\x09\xd4\xd4\xe6\x6b\x18\x18\x6b\x9f\x1e\x1e\x45\x0e\x98\x98\x7d\x38\xb2\xb2\xe8\xd2\xa6\xa6\x4b\xb7\x26\x26\xd6\x57\x3c\x3c\x32\x8a\x93\x93\xd8\xee\x82\x82\xfd\x98\x52\x52\x37\xd4\x7b\x7b\x71\x37\xbb\xbb\xf1\x97\x5b\x5b\xe1\x83\x47\x47\x30\x3c\x24\x24\x0f\xe2\x51\x51\xf8\xc6\xba\xba\x1b\xf3\x4a\x4a\x87\x48\xbf\xbf\xfa\x70\x0d\x0d\x06\xb3\xb0\xb0\x3f\xde\x75\x75\x5e\xfd\xd2\xd2\xba\x20\x7d\x7d\xae\x31\x66\x66\x5b\xa3\x3a\x3a\x8a\x1c\x59\x59\x00\x00\x00\x00\xbc\x93\xcd\xcd\x9d\xe0\x1a\x1a\x6d\x2c\xae\xae\xc1\xab\x7f\x7f\xb1\xc7\x2b\x2b\x0e\xb9\xbe\xbe\x80\xa0\xe0\xe0\x5d\x10\x8a\x8a\xd2\x52\x3b\x3b\xd5\xba\x64\x64\xa0\x88\xd8\xd8\x84\xa5\xe7\xe7\x07\xe8\x5f\x5f\x14\x11\x1b\x1b\xb5\xc2\x2c\x2c\x90\xb4\xfc\xfc\x2c\x27\x31\x31\xa3\x65\x80\x80\xb2\x2a\x73\x73\x73\x81\x0c\x0c\x4c\x5f\x79\x79\x54\x41\x6b\x6b\x92\x02\x4b\x4b\x74\x69\x53\x53\x36\x8f\x94\x94\x51\x1f\x83\x83\x38\x36\x2a\x2a\xb0\x9c\xc4\xc4\xbd\xc8\x22\x22\x5a\xf8\xd5\xd5\xfc\xc3\xbd\xbd\x60\x78\x48\x48\x62\xce\xff\xff\x96\x07\x4c\x4c\x6c\x77\x41\x41\x42\xe6\xc7\xc7\xf7\x24\xeb\xeb\x10\x14\x1c\x1c\x7c\x63\x5d\x5d\x28\x22\x36\x36\x27\xc0\x67\x67\x8c\xaf\xe9\xe9\x13\xf9\x44\x44\x95\xea\x14\x14\x9c\xbb\xf5\xf5\xc7\x18\xcf\xcf\x24\x2d\x3f\x3f\x46\xe3\xc0\xc0\x3b\xdb\x72\x72\x70\x6c\x54\x54\xca\x4c\x29\x29\xe3\x35\xf0\xf0\x85\xfe\x08\x08\xcb\x17\xc6\xc6\x11\x4f\xf3\xf3\xd0\xe4\x8c\x8c\x93\x59\xa4\xa4\xb8\x96\xca\xca\xa6\x3b\x68\x68\x83\x4d\xb8\xb8\x20\x28\x38\x38\xff\x2e\xe5\xe5\x9f\x56\xad\xad\x77\x84\x0b\x0b\xc3\x1d\xc8\xc8\xcc\xff\x99\x99\x03\xed\x58\x58\x6f\x9a\x19\x19\x08\x0a\x0e\x0e\xbf\x7e\x95\x95\x40\x50\x70\x70\xe7\x30\xf7\xf7\x2b\xcf\x6e\x6e\xe2\x6e\x1f\x1f\x79\x3d\xb5\xb5\x0c\x0f\x09\x09\xaa\x34\x61\x61\x82\x16\x57\x57\x41\x0b\x9f\x9f\x3a\x80\x9d\x9d\xea\x64\x11\x11\xb9\xcd\x25\x25\xe4\xdd\xaf\xaf\x9a\x08\x45\x45\xa4\x8d\xdf\xdf\x97\x5c\xa3\xa3\x7e\xd5\xea\xea\xda\x58\x35\x35\x7a\xd0\xed\xed\x17\xfc\x43\x43\x66\xcb\xf8\xf8\x94\xb1\xfb\xfb\xa1\xd3\x37\x37\x1d\x40\xfa\xfa\x3d\x68\xc2\xc2\xf0\xcc\xb4\xb4\xde\x5d\x32\x32\xb3\x71\x9c\x9c\x0b\xe7\x56\x56\x72\xda\xe3\xe3\xa7\x60\x87\x87\x1c\x1b\x15\x15\xef\x3a\xf9\xf9\xd1\xbf\x63\x63\x53\xa9\x34\x34\x3e\x85\x9a\x9a\x8f\x42\xb1\xb1\x33\xd1\x7c\x7c\x26\x9b\x88\x88\x5f\xa6\x3d\x3d\xec\xd7\xa1\xa1\x76\xdf\xe4\xe4\x2a\x94\x81\x81\x49\x01\x91\x91\x81\xfb\x0f\x0f\x88\xaa\xee\xee\xee\x61\x16\x16\x21\x73\xd7\xd7\xc4\xf5\x97\x97\x1a\xa8\xa5\xa5\xeb\x3f\xfe\xfe\xd9\xb5\x6d\x6d\xc5\xae\x78\x78\x39\x6d\xc5\xc5\x99\xe5\x1d\x1d\xcd\xa4\x76\x76\xad\xdc\x3e\x3e\x31\x67\xcb\xcb\x8b\x47\xb6\xb6\x01\x5b\xef\xef\x18\x1e\x12\x12\x23\xc5\x60\x60\xdd\xb0\x6a\x6a\x1f\xf6\x4d\x4d\x4e\xe9\xce\xce\x2d\x7c\xde\xde\xf9\x9d\x55\x55\x48\x5a\x7e\x7e\x4f\xb2\x21\x21\xf2\x7a\x03\x03\x65\x26\xa0\xa0\x8e\x19\x5e\x5e\x78\x66\x5a\x5a\x5c\x4b\x65\x65\x58\x4e\x62\x62\x19\x45\xfd\xfd\x8d\xf4\x06\x06\xe5\x86\x40\x40\x98\xbe\xf2\xf2\x57\xac\x33\x33\x67\x90\x17\x17\x7f\x8e\x05\x05\x05\x5e\xe8\xe8\x64\x7d\x4f\x4f\xaf\x6a\x89\x89\x63\x95\x10\x10\xb6\x2f\x74\x74\xfe\x75\x0a\x0a\xf5\x92\x5c\x5c\xb7\x74\x9b\x9b\x3c\x33\x2d\x2d\xa5\xd6\x30\x30\xce\x49\x2e\x2e\xe9\x89\x49\x49\x68\x72\x46\x46\x44\x55\x77\x77\xe0\xd8\xa8\xa8\x4d\x04\x96\x96\x43\xbd\x28\x28\x69\x29\xa9\xa9\x29\x79\xd9\xd9\x2e\x91\x86\x86\xac\x87\xd1\xd1\x15\x4a\xf4\xf4\x59\x15\x8d\x8d\xa8\x82\xd6\xd6\x0a\xbc\xb9\xb9\x9e\x0d\x42\x42\x6e\xc1\xf6\xf6\x47\xb8\x2f\x2f\xdf\x06\xdd\xdd\x34\x39\x23\x23\x35\x62\xcc\xcc\x6a\xc4\xf1\xf1\xcf\x12\xc1\xc1\xdc\xeb\x85\x85\x22\x9e\x8f\x8f\xc9\xa1\x71\x71\xc0\xf0\x90\x90\x9b\x53\xaa\xaa\x89\xf1\x01\x01\xd4\xe1\x8b\x8b\xed\x8c\x4e\x4e\xab\x6f\x8e\x8e\x12\xa2\xab\xab\xa2\x3e\x6f\x6f\x0d\x54\xe6\xe6\x52\xf2\xdb\xdb\xbb\x7b\x92\x92\x02\xb6\xb7\xb7\x2f\xca\x69\x69\xa9\xd9\x39\x39\xd7\x0c\xd3\xd3\x61\x23\xa7\xa7\x1e\xad\xa2\xa2\xb4\x99\xc3\xc3\x50\x44\x6c\x6c\x04\x05\x07\x07\xf6\x7f\x04\x04\xc2\x46\x27\x27\x16\xa7\xac\xac\x25\x76\xd0\xd0\x86\x13\x50\x50\x56\xf7\xdc\xdc\x55\x1a\x84\x84\x09\x51\xe1\xe1\xbe\x25\x7a\x7a\x91\xef\x13\x13\x39\x39\xd9\xa9\x17\x17\x90\x67\x9c\x9c\x71\xb3\xa6\xa6\xd2\xe8\x07\x07\x05\x04\x52\x52\x98\xfd\x80\x80\x65\xa3\xe4\xe4\xdf\x76\x45\x45\x08\x9a\x4b\x4b\x02\x92\xe0\xe0\xa0\x80\x5a\x5a\x66\x78\xaf\xaf\xdd\xe4\x6a\x6a\xb0\xdd\x63\x63\xbf\xd1\x2a\x2a\x36\x38\xe6\xe6\x54\x0d\x20\x20\x43\xc6\xcc\xcc\x62\x35\xf2\xf2\xbe\x98\x12\x12\x1e\x18\xeb\xeb\x24\xf7\xa1\xa1\xd7\xec\x41\x41\x77\x6c\x28\x28\xbd\x43\xbc\xbc\x32\x75\x7b\x7b\xd4\x37\x88\x88\x9b\x26\x0d\x0d\x70\xfa\x44\x44\xf9\x13\xfb\xfb\xb1\x94\x7e\x7e\x5a\x48\x03\x03\x7a\xf2\x8c\x8c\xe4\xd0\xb6\xb6\x47\x8b\x24\x24\x3c\x30\xe7\xe7\xa5\x84\x6b\x6b\x41\x54\xdd\xdd\x06\xdf\x60\x60\xc5\x23\xfd\xfd\x45\x19\x3a\x3a\xa3\x5b\xc2\xc2\x68\x3d\x8d\x8d\x15\x59\xec\xec\x21\xf3\x66\x66\x31\xae\x6f\x6f\x3e\xa2\x57\x57\x16\x82\x10\x10\x95\x63\xef\xef\x5b\x01\xb8\xb8\x4d\x83\x86\x86\x91\x2e\x6d\x6d\xb5\xd9\x83\x83\x1f\x51\xaa\xaa\x53\x9b\x5d\x5d\x63\x7c\x68\x68\x3b\xa6\xfe\xfe\x3f\xeb\x30\x30\xd6\xa5\x7a\x7a\x25\xbe\xac\xac\xa7\x16\x09\x09\x0f\x0c\xf0\xf0\x35\xe3\xa7\xa7\x23\x61\x90\x90\xf0\xc0\xe9\xe9\xaf\x8c\x9d\x9d\x80\x3a\x5c\x5c\x92\xf5\x0c\x0c\x81\x73\x31\x31\x27\x2c\xd0\xd0\x76\x25\x56\x56\xe7\x0b\x92\x92\x7b\xbb\xce\xce\xe9\x4e\x01\x01\xf1\x89\x1e\x1e\x9f\x6b\x34\x34\xa9\x53\xf1\xf1\xc4\x6a\xc3\xc3\x99\xb4\x5b\x5b\x97\xf1\x47\x47\x83\xe1\x18\x18\x6b\xe6\x22\x22\xc8\xbd\x98\x98\x0e\x45\x1f\x1f\x6e\xe2\xb3\xb3\xc9\xf4\x74\x74\x2f\xb6\xf8\xf8\xcb\x66\x99\x99\xff\xcc\x14\x14\xea\x95\x58\x58\xed\x03\xdc\xdc\xf7\x56\x8b\x8b\xe1\xd4\x15\x15\x1b\x1c\xa2\xa2\xad\x1e\xd3\xd3\x0c\xd7\xe2\xe2\x2b\xfb\xc8\xc8\x1d\xc3\x5e\x5e\x19\x8e\x2c\x2c\xc2\xb5\x49\x49\x89\xe9\xc1\xc1\x12\xcf\x95\x95\x7e\xbf\x7d\x7d\x20\xba\x11\x11\x64\xea\x0b\x0b\x84\x77\xc5\xc5\x6d\x39\x89\x89\x6a\xaf\x7c\x7c\xd1\x33\x71\x71\xa1\xc9\xff\xff\xce\x62\xbb\xbb\x37\x71\x0f\x0f\xfb\x81\xb5\xb5\x3d\x79\xe1\xe1\x51\x09\x3e\x3e\xdc\xad\x3f\x3f\x2d\x24\x76\x76\xa4\xcd\x55\x55\x9d\xf9\x82\x82\xee\xd8\x40\x40\x86\xe5\x78\x78\xae\xc5\x25\x25\xcd\xb9\x96\x96\x04\x4d\x77\x77\x55\x44\x0e\x0e\x0a\x08\x50\x50\x13\x86\xf7\xf7\x30\xe7\x37\x37\xd3\xa1\xfa\xfa\x40\x1d\x61\x61\x34\xaa\x4e\x4e\x8c\xed\xb0\xb0\xb3\x06\x54\x54\x6c\x70\x73\x73\x2a\xb2\x3b\x3b\x52\xd2\x9f\x9f\x0b\x41\x02\x02\x8b\x7b\xd8\xd8\x88\xa0\xf3\xf3\x4f\x11\xcb\xcb\x67\x31\x27\x27\x46\xc2\x67\x67\xc0\x27\xfc\xfc\xb4\x90\x38\x38\x28\x20\x04\x04\x7f\xf6\x48\x48\x78\x60\xe5\xe5\x2e\xff\x4c\x4c\x07\x96\x65\x65\x4b\x5c\x2b\x2b\xc7\xb1\x8e\x8e\x6f\xab\x42\x42\x0d\x9e\xf5\xf5\xbb\x9c\xdb\xdb\xf2\x52\x4a\x4a\xf3\x1b\x3d\x3d\xa6\x5f\xa4\xa4\x59\x93\xb9\xb9\xbc\x0a\xf9\xf9\x3a\xef\x13\x13\xef\x91\x08\x08\xfe\x85\x91\x91\x01\x49\x16\x16\x61\xee\xde\xde\x7c\x2d\x21\x21\xb2\x4f\xb1\xb1\x42\x8f\x72\x72\xdb\x3b\x2f\x2f\xb8\x47\xbf\xbf\x48\x87\xae\xae\x2c\x6d\xc0\xc0\xe3\x46\x3c\x3c\x57\xd6\x9a\x9a\x85\x3e\xa9\xa9\x29\x69\x4f\x4f\x7d\x64\x81\x81\x94\x2a\x2e\x2e\x49\xce\xc6\xc6\x17\xcb\x69\x69\xca\x2f\xbd\xbd\xc3\xfc\xa3\xa3\x5c\x97\xe8\xe8\x5e\x05\xed\xed\xd0\x7a\xd1\xd1\x87\xac\x05\x05\x8e\x7f\x64\x64\xba\xd5\xa5\xa5\xa8\x1a\x26\x26\xb7\x4b\xbe\xbe\xb9\x0e\x87\x87\x60\xa7\xd5\xd5\xf8\x5a\x36\x36\x22\x28\x1b\x1b\x11\x14\x75\x75\xde\x3f\xd9\xd9\x79\x29\xee\xee\xaa\x88\x2d\x2d\x33\x3c\x79\x79\x5f\x4c\xb7\xb7\xb6\x02\xca\xca\x96\xb8\x35\x35\x58\xda\xc4\xc4\x9c\xb0\x43\x43\xfc\x17\x84\x84\x1a\x55\x4d\x4d\xf6\x1f\x59\x59\x1c\x8a\xb2\xb2\x38\x7d\x33\x33\xac\x57\xcf\xcf\x18\xc7\x06\x06\xf4\x8d\x53\x53\x69\x74\x9b\x9b\x74\xb7\x97\x97\xf5\xc4\xad\xad\x56\x9f\xe3\xe3\xda\x72\xea\xea\xd5\x7e\xf4\xf4\x4a\x15\x8f\x8f\x9e\x22\xab\xab\xa2\x12\x62\x62\x4e\x58\x5f\x5f\xe8\x07\x1d\x1d\xe5\x99\x23\x23\x39\x34\xf6\xf6\xc1\x6e\x6c\x6c\x44\x50\x32\x32\x5d\xde\x46\x46\x72\x68\xa0\xa0\x26\x65\xcd\xcd\x93\xbc\xda\xda\x03\xdb\xba\xba\xc6\xf8\x9e\x9e\xfa\xc8\xd6\xd6\x82\xa8\x6e\x6e\xcf\x2b\x70\x70\x50\x40\x85\x85\xeb\xdc\x0a\x0a\x75\xfe\x93\x93\x8a\x32\xdf\xdf\x8d\xa4\x29\x29\x4c\xca\x1c\x1c\x14\x10\xd7\xd7\x73\x21\xb4\xb4\xcc\xf0\xd4\xd4\x09\xd3\x8a\x8a\x10\x5d\x51\x51\xe2\x0f\x00\x00\x00\x00\x19\x19\x9a\x6f\x1a\x1a\xe0\x9d\x94\x94\x8f\x36\xc7\xc7\xe6\x42\xc9\xc9\xec\x4a\xd2\xd2\xfd\x5e\x7f\x7f\xab\xc1\xa8\xa8\xd8\xe0\x32\xbc\x75\xbc\x21\xec\xf3\xec\x43\x20\xc6\x20\xc9\xb3\xf4\xb3\x03\xda\xdb\xda\x8b\x02\x7b\x02\x2b\xe2\xfb\xe2\xfa\x9e\xc8\x9e\xec\xc9\x4a\xc9\x09\xd4\xd3\xd4\x6b\x18\xe6\x18\x9f\x1e\x6b\x1e\x0e\x98\x45\x98\x38\xb2\x7d\xb2\xd2\xa6\xe8\xa6\xb7\x26\x4b\x26\x57\x3c\xd6\x3c\x8a\x93\x32\x93\xee\x82\xd8\x82\x98\x52\xfd\x52\xd4\x7b\x37\x7b\x37\xbb\x71\xbb\x97\x5b\xf1\x5b\x83\x47\xe1\x47\x3c\x24\x30\x24\xe2\x51\x0f\x51\xc6\xba\xf8\xba\xf3\x4a\x1b\x4a\x48\xbf\x87\xbf\x70\x0d\xfa\x0d\xb3\xb0\x06\xb0\xde\x75\x3f\x75\xfd\xd2\x5e\xd2\x20\x7d\xba\x7d\x31\x66\xae\x66\xa3\x3a\x5b\x3a\x1c\x59\x8a\x59\x00\x00\x00\x00\x93\xcd\xbc\xcd\xe0\x1a\x9d\x1a\x2c\xae\x6d\xae\xab\x7f\xc1\x7f\xc7\x2b\xb1\x2b\xb9\xbe\x0e\xbe\xa0\xe0\x80\xe0\x10\x8a\x5d\x8a\x52\x3b\xd2\x3b\xba\x64\xd5\x64\x88\xd8\xa0\xd8\xa5\xe7\x84\xe7\xe8\x5f\x07\x5f\x11\x1b\x14\x1b\xc2\x2c\xb5\x2c\xb4\xfc\x90\xfc\x27\x31\x2c\x31\x65\x80\xa3\x80\x2a\x73\xb2\x73\x81\x0c\x73\x0c\x5f\x79\x4c\x79\x41\x6b\x54\x6b\x02\x4b\x92\x4b\x69\x53\x74\x53\x8f\x94\x36\x94\x1f\x83\x51\x83\x36\x2a\x38\x2a\x9c\xc4\xb0\xc4\xc8\x22\xbd\x22\xf8\xd5\x5a\xd5\xc3\xbd\xfc\xbd\x78\x48\x60\x48\xce\xff\x62\xff\x07\x4c\x96\x4c\x77\x41\x6c\x41\xe6\xc7\x42\xc7\x24\xeb\xf7\xeb\x14\x1c\x10\x1c\x63\x5d\x7c\x5d\x22\x36\x28\x36\xc0\x67\x27\x67\xaf\xe9\x8c\xe9\xf9\x44\x13\x44\xea\x14\x95\x14\xbb\xf5\x9c\xf5\x18\xcf\xc7\xcf\x2d\x3f\x24\x3f\xe3\xc0\x46\xc0\xdb\x72\x3b\x72\x6c\x54\x70\x54\x4c\x29\xca\x29\x35\xf0\xe3\xf0\xfe\x08\x85\x08\x17\xc6\xcb\xc6\x4f\xf3\x11\xf3\xe4\x8c\xd0\x8c\x59\xa4\x93\xa4\x96\xca\xb8\xca\x3b\x68\xa6\x68\x4d\xb8\x83\xb8\x28\x38\x20\x38\x2e\xe5\xff\xe5\x56\xad\x9f\xad\x84\x0b\x77\x0b\x1d\xc8\xc3\xc8\xff\x99\xcc\x99\xed\x58\x03\x58\x9a\x19\x6f\x19\x0a\x0e\x08\x0e\x7e\x95\xbf\x95\x50\x70\x40\x70\x30\xf7\xe7\xf7\xcf\x6e\x2b\x6e\x6e\x1f\xe2\x1f\x3d\xb5\x79\xb5\x0f\x09\x0c\x09\x34\x61\xaa\x61\x16\x57\x82\x57\x0b\x9f\x41\x9f\x80\x9d\x3a\x9d\x64\x11\xea\x11\xcd\x25\xb9\x25\xdd\xaf\xe4\xaf\x08\x45\x9a\x45\x8d\xdf\xa4\xdf\x5c\xa3\x97\xa3\xd5\xea\x7e\xea\x58\x35\xda\x35\xd0\xed\x7a\xed\xfc\x43\x17\x43\xcb\xf8\x66\xf8\xb1\xfb\x94\xfb\xd3\x37\xa1\x37\x40\xfa\x1d\xfa\x68\xc2\x3d\xc2\xcc\xb4\xf0\xb4\x5d\x32\xde\x32\x71\x9c\xb3\x9c\xe7\x56\x0b\x56\xda\xe3\x72\xe3\x60\x87\xa7\x87\x1b\x15\x1c\x15\x3a\xf9\xef\xf9\xbf\x63\xd1\x63\xa9\x34\x53\x34\x85\x9a\x3e\x9a\x42\xb1\x8f\xb1\xd1\x7c\x33\x7c\x9b\x88\x26\x88\xa6\x3d\x5f\x3d\xd7\xa1\xec\xa1\xdf\xe4\x76\xe4\x94\x81\x2a\x81\x01\x91\x49\x91\xfb\x0f\x81\x0f\xaa\xee\x88\xee\x61\x16\xee\x16\x73\xd7\x21\xd7\xf5\x97\xc4\x97\xa8\xa5\x1a\xa5\x3f\xfe\xeb\xfe\xb5\x6d\xd9\x6d\xae\x78\xc5\x78\x6d\xc5\x39\xc5\xe5\x1d\x99\x1d\xa4\x76\xcd\x76\xdc\x3e\xad\x3e\x67\xcb\x31\xcb\x47\xb6\x8b\xb6\x5b\xef\x01\xef\x1e\x12\x18\x12\xc5\x60\x23\x60\xb0\x6a\xdd\x6a\xf6\x4d\x1f\x4d\xe9\xce\x4e\xce\x7c\xde\x2d\xde\x9d\x55\xf9\x55\x5a\x7e\x48\x7e\xb2\x21\x4f\x21\x7a\x03\xf2\x03\x26\xa0\x65\xa0\x19\x5e\x8e\x5e\x66\x5a\x78\x5a\x4b\x65\x5c\x65\x4e\x62\x58\x62\x45\xfd\x19\xfd\xf4\x06\x8d\x06\x86\x40\xe5\x40\xbe\xf2\x98\xf2\xac\x33\x57\x33\x90\x17\x67\x17\x8e\x05\x7f\x05\x5e\xe8\x05\xe8\x7d\x4f\x64\x4f\x6a\x89\xaf\x89\x95\x10\x63\x10\x2f\x74\xb6\x74\x75\x0a\xfe\x0a\x92\x5c\xf5\x5c\x74\x9b\xb7\x9b\x33\x2d\x3c\x2d\xd6\x30\xa5\x30\x49\x2e\xce\x2e\x89\x49\xe9\x49\x72\x46\x68\x46\x55\x77\x44\x77\xd8\xa8\xe0\xa8\x04\x96\x4d\x96\xbd\x28\x43\x28\x29\xa9\x69\xa9\x79\xd9\x29\xd9\x91\x86\x2e\x86\x87\xd1\xac\xd1\x4a\xf4\x15\xf4\x15\x8d\x59\x8d\x82\xd6\xa8\xd6\xbc\xb9\x0a\xb9\x0d\x42\x9e\x42\xc1\xf6\x6e\xf6\xb8\x2f\x47\x2f\x06\xdd\xdf\xdd\x39\x23\x34\x23\x62\xcc\x35\xcc\xc4\xf1\x6a\xf1\x12\xc1\xcf\xc1\xeb\x85\xdc\x85\x9e\x8f\x22\x8f\xa1\x71\xc9\x71\xf0\x90\xc0\x90\x53\xaa\x9b\xaa\xf1\x01\x89\x01\xe1\x8b\xd4\x8b\x8c\x4e\xed\x4e\x6f\x8e\xab\x8e\xa2\xab\x12\xab\x3e\x6f\xa2\x6f\x54\xe6\x0d\xe6\xf2\xdb\x52\xdb\x7b\x92\xbb\x92\xb6\xb7\x02\xb7\xca\x69\x2f\x69\xd9\x39\xa9\x39\x0c\xd3\xd7\xd3\x23\xa7\x61\xa7\xad\xa2\x1e\xa2\x99\xc3\xb4\xc3\x44\x6c\x50\x6c\x05\x07\x04\x07\x7f\x04\xf6\x04\x46\x27\xc2\x27\xa7\xac\x16\xac\x76\xd0\x25\xd0\x13\x50\x86\x50\xf7\xdc\x56\xdc\x1a\x84\x55\x84\x51\xe1\x09\xe1\x25\x7a\xbe\x7a\xef\x13\x91\x13\xd9\xa9\x39\xd9\x90\x67\x17\x90\x71\xb3\x9c\x71\xd2\xe8\xa6\xd2\x05\x04\x07\x05\x98\xfd\x52\x98\x65\xa3\x80\x65\xdf\x76\xe4\xdf\x08\x9a\x45\x08\x02\x92\x4b\x02\xa0\x80\xe0\xa0\x66\x78\x5a\x66\xdd\xe4\xaf\xdd\xb0\xdd\x6a\xb0\xbf\xd1\x63\xbf\x36\x38\x2a\x36\x54\x0d\xe6\x54\x43\xc6\x20\x43\x62\x35\xcc\x62\xbe\x98\xf2\xbe\x1e\x18\x12\x1e\x24\xf7\xeb\x24\xd7\xec\xa1\xd7\x77\x6c\x41\x77\xbd\x43\x28\xbd\x32\x75\xbc\x32\xd4\x37\x7b\xd4\x9b\x26\x88\x9b\x70\xfa\x0d\x70\xf9\x13\x44\xf9\xb1\x94\xfb\xb1\x5a\x48\x7e\x5a\x7a\xf2\x03\x7a\xe4\xd0\x8c\xe4\x47\x8b\xb6\x47\x3c\x30\x24\x3c\xa5\x84\xe7\xa5\x41\x54\x6b\x41\x06\xdf\xdd\x06\xc5\x23\x60\xc5\x45\x19\xfd\x45\xa3\x5b\x3a\xa3\x68\x3d\xc2\x68\x15\x59\x8d\x15\x21\xf3\xec\x21\x31\xae\x66\x31\x3e\xa2\x6f\x3e\x16\x82\x57\x16\x95\x63\x10\x95\x5b\x01\xef\x5b\x4d\x83\xb8\x4d\x91\x2e\x86\x91\xb5\xd9\x6d\xb5\x1f\x51\x83\x1f\x53\x9b\xaa\x53\x63\x7c\x5d\x63\x3b\xa6\x68\x3b\x3f\xeb\xfe\x3f\xd6\xa5\x30\xd6\x25\xbe\x7a\x25\xa7\x16\xac\xa7\x0f\x0c\x09\x0f\x35\xe3\xf0\x35\x23\x61\xa7\x23\xf0\xc0\x90\xf0\xaf\x8c\xe9\xaf\x80\x3a\x9d\x80\x92\xf5\x5c\x92\x81\x73\x0c\x81\x27\x2c\x31\x27\x76\x25\xd0\x76\xe7\x0b\x56\xe7\x7b\xbb\x92\x7b\xe9\x4e\xce\xe9\xf1\x89\x01\xf1\x9f\x6b\x1e\x9f\xa9\x53\x34\xa9\xc4\x6a\xf1\xc4\x99\xb4\xc3\x99\x97\xf1\x5b\x97\x83\xe1\x47\x83\x6b\xe6\x18\x6b\xc8\xbd\x22\xc8\x0e\x45\x98\x0e\x6e\xe2\x1f\x6e\xc9\xf4\xb3\xc9\x2f\xb6\x74\x2f\xcb\x66\xf8\xcb\xff\xcc\x99\xff\xea\x95\x14\xea\xed\x03\x58\xed\xf7\x56\xdc\xf7\xe1\xd4\x8b\xe1\x1b\x1c\x15\x1b\xad\x1e\xa2\xad\x0c\xd7\xd3\x0c\x2b\xfb\xe2\x2b\x1d\xc3\xc8\x1d\x19\x8e\x5e\x19\xc2\xb5\x2c\xc2\x89\xe9\x49\x89\x12\xcf\xc1\x12\x7e\xbf\x95\x7e\x20\xba\x7d\x20\x64\xea\x11\x64\x84\x77\x0b\x84\x6d\x39\xc5\x6d\x6a\xaf\x89\x6a\xd1\x33\x7c\xd1\xa1\xc9\x71\xa1\xce\x62\xff\xce\x37\x71\xbb\x37\xfb\x81\x0f\xfb\x3d\x79\xb5\x3d\x51\x09\xe1\x51\xdc\xad\x3e\xdc\x2d\x24\x3f\x2d\xa4\xcd\x76\xa4\x9d\xf9\x55\x9d\xee\xd8\x82\xee\x86\xe5\x40\x86\xae\xc5\x78\xae\xcd\xb9\x25\xcd\x04\x4d\x96\x04\x55\x44\x77\x55\x0a\x08\x0e\x0a\x13\x86\x50\x13\x30\xe7\xf7\x30\xd3\xa1\x37\xd3\x40\x1d\xfa\x40\x34\xaa\x61\x34\x8c\xed\x4e\x8c\xb3\x06\xb0\xb3\x6c\x70\x54\x6c\x2a\xb2\x73\x2a\x52\xd2\x3b\x52\x0b\x41\x9f\x0b\x8b\x7b\x02\x8b\x88\xa0\xd8\x88\x4f\x11\xf3\x4f\x67\x31\xcb\x67\x46\xc2\x27\x46\xc0\x27\x67\xc0\xb4\x90\xfc\xb4\x28\x20\x38\x28\x7f\xf6\x04\x7f\x78\x60\x48\x78\x2e\xff\xe5\x2e\x07\x96\x4c\x07\x4b\x5c\x65\x4b\xc7\xb1\x2b\xc7\x6f\xab\x8e\x6f\x0d\x9e\x42\x0d\xbb\x9c\xf5\xbb\xf2\x52\xdb\xf2\xf3\x1b\x4a\xf3\xa6\x5f\x3d\xa6\x59\x93\xa4\x59\xbc\x0a\xb9\xbc\x3a\xef\xf9\x3a\xef\x91\x13\xef\xfe\x85\x08\xfe\x01\x49\x91\x01\x61\xee\x16\x61\x7c\x2d\xde\x7c\xb2\x4f\x21\xb2\x42\x8f\xb1\x42\xdb\x3b\x72\xdb\xb8\x47\x2f\xb8\x48\x87\xbf\x48\x2c\x6d\xae\x2c\xe3\x46\xc0\xe3\x57\xd6\x3c\x57\x85\x3e\x9a\x85\x29\x69\xa9\x29\x7d\x64\x4f\x7d\x94\x2a\x81\x94\x49\xce\x2e\x49\x17\xcb\xc6\x17\xca\x2f\x69\xca\xc3\xfc\xbd\xc3\x5c\x97\xa3\x5c\x5e\x05\xe8\x5e\xd0\x7a\xed\xd0\x87\xac\xd1\x87\x8e\x7f\x05\x8e\xba\xd5\x64\xba\xa8\x1a\xa5\xa8\xb7\x4b\x26\xb7\xb9\x0e\xbe\xb9\x60\xa7\x87\x60\xf8\x5a\xd5\xf8\x22\x28\x36\x22\x11\x14\x1b\x11\xde\x3f\x75\xde\x79\x29\xd9\x79\xaa\x88\xee\xaa\x33\x3c\x2d\x33\x5f\x4c\x79\x5f\xb6\x02\xb7\xb6\x96\xb8\xca\x96\x58\xda\x35\x58\x9c\xb0\xc4\x9c\xfc\x17\x43\xfc\x1a\x55\x84\x1a\xf6\x1f\x4d\xf6\x1c\x8a\x59\x1c\x38\x7d\xb2\x38\xac\x57\x33\xac\x18\xc7\xcf\x18\xf4\x8d\x06\xf4\x69\x74\x53\x69\x74\xb7\x9b\x74\xf5\xc4\x97\xf5\x56\x9f\xad\x56\xda\x72\xe3\xda\xd5\x7e\xea\xd5\x4a\x15\xf4\x4a\x9e\x22\x8f\x9e\xa2\x12\xab\xa2\x4e\x58\x62\x4e\xe8\x07\x5f\xe8\xe5\x99\x1d\xe5\x39\x34\x23\x39\xc1\x6e\xf6\xc1\x44\x50\x6c\x44\x5d\xde\x32\x5d\x72\x68\x46\x72\x26\x65\xa0\x26\x93\xbc\xcd\x93\x03\xdb\xda\x03\xc6\xf8\xba\xc6\xfa\xc8\x9e\xfa\x82\xa8\xd6\x82\xcf\x2b\x6e\xcf\x50\x40\x70\x50\xeb\xdc\x85\xeb\x75\xfe\x0a\x75\x8a\x32\x93\x8a\x8d\xa4\xdf\x8d\x4c\xca\x29\x4c\x14\x10\x1c\x14\x73\x21\xd7\x73\xcc\xf0\xb4\xcc\x09\xd3\xd4\x09\x10\x5d\x8a\x10\xe2\x0f\x51\xe2\x00\x00\x00\x00\x9a\x6f\x19\x9a\xe0\x9d\x1a\xe0\x8f\x36\x94\x8f\xe6\x42\xc7\xe6\xec\x4a\xc9\xec\xfd\x5e\xd2\xfd\xab\xc1\x7f\xab\xd8\xe0\xa8\xd8"): "Twofish_mds",
+        MutablePattern("\x30\x29\x30\x0d\x06\x09\x2b\x06\x01\x04\x01\xda\x47\x0c\x02\x05\x00\x04\x18"): "PKCS_tiger",
+        MutablePattern("\x51\x50\xa7\xf4\x7e\x53\x65\x41\x1a\xc3\xa4\x17\x3a\x96\x5e\x27\x3b\xcb\x6b\xab\x1f\xf1\x45\x9d\xac\xab\x58\xfa\x4b\x93\x03\xe3\x20\x55\xfa\x30\xad\xf6\x6d\x76\x88\x91\x76\xcc\xf5\x25\x4c\x02\x4f\xfc\xd7\xe5\xc5\xd7\xcb\x2a\x26\x80\x44\x35\xb5\x8f\xa3\x62\xde\x49\x5a\xb1\x25\x67\x1b\xba\x45\x98\x0e\xea\x5d\xe1\xc0\xfe\xc3\x02\x75\x2f\x81\x12\xf0\x4c\x8d\xa3\x97\x46\x6b\xc6\xf9\xd3\x03\xe7\x5f\x8f\x15\x95\x9c\x92\xbf\xeb\x7a\x6d\x95\xda\x59\x52\xd4\x2d\x83\xbe\x58\xd3\x21\x74\x49\x29\x69\xe0\x8e\x44\xc8\xc9\x75\x6a\x89\xc2\xf4\x78\x79\x8e\x99\x6b\x3e\x58\x27\xdd\x71\xb9\xbe\xb6\x4f\xe1\xf0\x17\xad\x88\xc9\x66\xac\x20\x7d\xb4\x3a\xce\x63\x18\x4a\xdf\xe5\x82\x31\x1a\x97\x60\x33\x51\x62\x45\x7f\x53\xb1\xe0\x77\x64\xbb\x84\xae\x6b\xfe\x1c\xa0\x81\xf9\x94\x2b\x08\x70\x58\x68\x48\x8f\x19\xfd\x45\x94\x87\x6c\xde\x52\xb7\xf8\x7b\xab\x23\xd3\x73\x72\xe2\x02\x4b\xe3\x57\x8f\x1f\x66\x2a\xab\x55\xb2\x07\x28\xeb\x2f\x03\xc2\xb5\x86\x9a\x7b\xc5\xd3\xa5\x08\x37\x30\xf2\x87\x28\x23\xb2\xa5\xbf\x02\xba\x6a\x03\xed\x5c\x82\x16\x8a\x2b\x1c\xcf\xa7\x92\xb4\x79\xf3\xf0\xf2\x07\x4e\xa1\xe2\x69\x65\xcd\xf4\xda\x06\xd5\xbe\x05\xd1\x1f\x62\x34\xc4\x8a\xfe\xa6\x34\x9d\x53\x2e\xa2\xa0\x55\xf3\x05\x32\xe1\x8a\xa4\x75\xeb\xf6\x0b\x39\xec\x83\x40\xaa\xef\x60\x5e\x06\x9f\x71\xbd\x51\x10\x6e\x3e\xf9\x8a\x21\x96\x3d\x06\xdd\xdd\xae\x05\x3e\x4d\x46\xbd\xe6\x91\xb5\x8d\x54\x71\x05\x5d\xc4\x04\x6f\xd4\x06\x60\xff\x15\x50\x19\x24\xfb\x98\xd6\x97\xe9\xbd\x89\xcc\x43\x40\x67\x77\x9e\xd9\xb0\xbd\x42\xe8\x07\x88\x8b\x89\xe7\x38\x5b\x19\x79\xdb\xee\xc8\xa1\x47\x0a\x7c\x7c\xe9\x0f\x42\xf8\xc9\x1e\x84\x00\x00\x00\x00\x09\x83\x86\x80\x32\x48\xed\x2b\x1e\xac\x70\x11\x6c\x4e\x72\x5a\xfd\xfb\xff\x0e\x0f\x56\x38\x85\x3d\x1e\xd5\xae\x36\x27\x39\x2d\x0a\x64\xd9\x0f\x68\x21\xa6\x5c\x9b\xd1\x54\x5b\x24\x3a\x2e\x36\x0c\xb1\x67\x0a\x93\x0f\xe7\x57\xb4\xd2\x96\xee\x1b\x9e\x91\x9b\x80\x4f\xc5\xc0\x61\xa2\x20\xdc\x5a\x69\x4b\x77\x1c\x16\x1a\x12\xe2\x0a\xba\x93\xc0\xe5\x2a\xa0\x3c\x43\xe0\x22\x12\x1d\x17\x1b\x0e\x0b\x0d\x09\xf2\xad\xc7\x8b\x2d\xb9\xa8\xb6\x14\xc8\xa9\x1e\x57\x85\x19\xf1\xaf\x4c\x07\x75\xee\xbb\xdd\x99\xa3\xfd\x60\x7f\xf7\x9f\x26\x01\x5c\xbc\xf5\x72\x44\xc5\x3b\x66\x5b\x34\x7e\xfb\x8b\x76\x29\x43\xcb\xdc\xc6\x23\xb6\x68\xfc\xed\xb8\x63\xf1\xe4\xd7\xca\xdc\x31\x42\x10\x85\x63\x13\x40\x22\x97\x84\x20\x11\xc6\x85\x7d\x24\x4a\xd2\xf8\x3d\xbb\xae\x11\x32\xf9\xc7\x6d\xa1\x29\x1d\x4b\x2f\x9e\xdc\xf3\x30\xb2\x0d\xec\x52\x86\x77\xd0\xe3\xc1\x2b\x6c\x16\xb3\xa9\x99\xb9\x70\x11\xfa\x48\x94\x47\x22\x64\xe9\xa8\xc4\x8c\xfc\xa0\x1a\x3f\xf0\x56\xd8\x2c\x7d\x22\xef\x90\x33\x87\xc7\x4e\x49\xd9\xc1\xd1\x38\x8c\xfe\xa2\xca\x98\x36\x0b\xd4\xa6\xcf\x81\xf5\xa5\x28\xde\x7a\xda\x26\x8e\xb7\x3f\xa4\xbf\xad\x2c\xe4\x9d\x3a\x50\x0d\x92\x78\x6a\x9b\xcc\x5f\x54\x62\x46\x7e\xf6\xc2\x13\x8d\x90\xe8\xb8\xd8\x2e\x5e\xf7\x39\x82\xf5\xaf\xc3\x9f\xbe\x80\x5d\x69\x7c\x93\xd0\x6f\xa9\x2d\xd5\xcf\xb3\x12\x25\xc8\x3b\x99\xac\x10\xa7\x7d\x18\xe8\x6e\x63\x9c\xdb\x7b\xbb\x3b\xcd\x09\x78\x26\x6e\xf4\x18\x59\xec\x01\xb7\x9a\x83\xa8\x9a\x4f\xe6\x65\x6e\x95\xaa\x7e\xe6\xff\x21\x08\xcf\xbc\xef\xe6\xe8\x15\xba\xd9\x9b\xe7\x4a\xce\x36\x6f\xea\xd4\x09\x9f\x29\xd6\x7c\xb0\x31\xaf\xb2\xa4\x2a\x31\x23\x3f\xc6\x30\x94\xa5\x35\xc0\x66\xa2\x74\x37\xbc\x4e\xfc\xa6\xca\x82\xe0\xb0\xd0\x90\x33\x15\xd8\xa7\xf1\x4a\x98\x04\x41\xf7\xda\xec\x7f\x0e\x50\xcd\x17\x2f\xf6\x91\x76\x8d\xd6\x4d\x43\x4d\xb0\xef\xcc\x54\x4d\xaa\xe4\xdf\x04\x96\x9e\xe3\xb5\xd1\x4c\x1b\x88\x6a\xc1\xb8\x1f\x2c\x46\x7f\x51\x65\x9d\x04\xea\x5e\x01\x5d\x35\x8c\xfa\x73\x74\x87\xfb\x2e\x41\x0b\xb3\x5a\x1d\x67\x92\x52\xd2\xdb\xe9\x33\x56\x10\x6d\x13\x47\xd6\x9a\x8c\x61\xd7\x37\x7a\x0c\xa1\x59\x8e\x14\xf8\xeb\x89\x3c\x13\xce\xee\x27\xa9\xb7\x35\xc9\x61\xe1\xed\xe5\x1c\x7a\x3c\xb1\x47\x9c\x59\xdf\xd2\x55\x3f\x73\xf2\x18\x79\xce\x14\x73\xbf\x37\xc7\x53\xea\xcd\xf7\x5f\x5b\xaa\xfd\xdf\x14\x6f\x3d\x78\x86\xdb\x44\xca\x81\xf3\xaf\xb9\x3e\xc4\x68\x38\x2c\x34\x24\xc2\x5f\x40\xa3\x16\x72\xc3\x1d\xbc\x0c\x25\xe2\x28\x8b\x49\x3c\xff\x41\x95\x0d\x39\x71\x01\xa8\x08\xde\xb3\x0c\xd8\x9c\xe4\xb4\x64\x90\xc1\x56\x7b\x61\x84\xcb\xd5\x70\xb6\x32\x48\x74\x5c\x6c\xd0\x42\x57\xb8"): "rijndael_td3",
+        MutablePattern("\x97\xef\x45\xac\x29\x0f\x43\xcd\x45\x7e\x1b\x55\x1c\x80\x11\x34\xb1\x77\xce\x96\x2e\x72\x8e\x7c\x5f\x5a\xab\x0a\x36\x43\xbe\x18\x9d\x21\xb4\x21\xbc\x87\xb9\x4d\xa2\x9d\x27\xbd\xc7\x5b\xd7\xc3"): "MD5MAC_T",
+        MutablePattern("\x01\x01\x00\x00\x02\x01\x00\x00\x03\x01\x00\x00\x04\x01\x00\x00\x05\x01\x00\x00\x06\x01\x00\x00\x07\x01\x00\x00\x08\x01\x00\x00\x09\x01\x00\x00\x09\x01\x00\x00\x0a\x01\x00\x00\x0a\x01\x00\x00\x0b\x01\x00\x00\x0b\x01\x00\x00\x0c\x01\x00\x00\x0c\x01\x00\x00\x0d\x01\x00\x00\x0d\x01\x00\x00\x0d\x01\x00\x00\x0d\x01\x00\x00\x0e\x01\x00\x00\x0e\x01\x00\x00\x0e\x01\x00\x00\x0e\x01\x00\x00\x0f\x01\x00\x00\x0f\x01\x00\x00\x0f\x01\x00\x00\x0f\x01\x00\x00\x10\x01\x00\x00\x10\x01\x00\x00\x10\x01\x00\x00\x10\x01\x00\x00\x11\x01\x00\x00\x11\x01\x00\x00\x11\x01\x00\x00\x11\x01\x00\x00\x11\x01\x00\x00\x11\x01\x00\x00\x11\x01\x00\x00\x11\x01\x00\x00\x12\x01\x00\x00\x12\x01\x00\x00\x12\x01\x00\x00\x12\x01\x00\x00\x12\x01\x00\x00\x12\x01\x00\x00\x12\x01\x00\x00\x12\x01\x00\x00\x13\x01\x00\x00\x13\x01\x00\x00\x13\x01\x00\x00\x13\x01\x00\x00\x13\x01\x00\x00\x13\x01\x00\x00\x13\x01\x00\x00\x13\x01\x00\x00\x14\x01\x00\x00\x14\x01\x00\x00\x14\x01\x00\x00\x14\x01\x00\x00\x14\x01\x00\x00\x14\x01\x00\x00\x14\x01\x00\x00\x14\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x15\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x16\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x17\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x18\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x19\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1a\x01\x00\x00\x1b\x01\x00\x