예제 #1
0
    def get_args(self):
        """Try to get the args of the all XREFS of the function"""

        # load the pattern
        args_pattern = open(self.cfg["call_pattern"], "r").read()
        args_pattern = args_pattern.replace("FILL_ADDR", str(hex(self.func_addr)))

        #print Color.step("Searching for arguments of each XREFS to the function 0x{:X}".format(self.func_addr))

        # match the pattern
        args_matches = pygrap.match_graph(args_pattern, self.test_graph)

        for match in args_matches.items()[0][1]:
            self.args.append(match)
예제 #2
0
    def get_xrefs(self):
        """Try to get the xrefs of the matched function"""

        # generate our pattern for xrefs
        xrefs_pattern = XREFS_PATTERN.replace("FILL_ADDR", str(hex(self.func_addr)))
        
        #print Color.step("Searching for the XREFS to the function 0x{:X}".format(self.func_addr))

        # match the pattern
        call_matches = pygrap.match_graph(xrefs_pattern, self.test_graph)

        # creation of the list of xrefs
        if len(call_matches) == 0:
            print Color.error("No XREFS to the function 0x{:X} was found".format(self.func_addr))
        else:
            for match in call_matches["xrefs"]:
                self.xrefs.append(int(match["call"][0].info.address))
예제 #3
0
def decrypt_strings(pe, pe_baseaddr, pe_cfg):
    r = parse_decrypt_function(pe, pe_baseaddr, pe_cfg)
    if r is None:
        print("ERROR: unable to find the decryption function")
        sys.exit(1)
    func_addr, xorkey_addr, cipher_addr, cipher_size = r

    pattern_call_final = pattern_call.replace("FILL_ADDRESS", hex(func_addr))
    matches = pygrap.match_graph(pattern_call_final, pe_cfg)
    
    print("\nStrings:")
    if "push_call_func" in matches:
        for match in matches["push_call_func"]:
            push_inst = match["PUSH"][0]
            push_addr = push_inst.info.address
            offset = pygrap.parse_first_immediate(push_inst.info.arg1)
            if offset:
                dec = decrypt_string(pe, pe_baseaddr, offset, xorkey_addr, cipher_addr, cipher_size)
                print(hex(push_addr), hex(offset), dec)
예제 #4
0
def parse_decrypt_function(pe, pe_baseaddr, pe_cfg):
    matches = pygrap.match_graph(pattern_function, pe_cfg)
    if "qakbot_strings_parsing_func" in matches:
        for match in matches["qakbot_strings_parsing_func"]:
            func_addr = match["0_EP"][0].info.address
            size1 = pygrap.parse_first_immediate(match["1_CIPHERSIZE1"][0].info.arg1)
            cipher_addr = pygrap.parse_first_immediate(match["2_CIPHER"][0].info.arg1)
            size2 = pygrap.parse_first_immediate(match["3_CIPHERSIZE2"][0].info.arg2)
            xorkey_addr = pygrap.parse_first_address(match["5_XORKEY"][0].info.arg2)
            
            if size1 != size2 + 1:
                print("ERROR: size1 and size2 do not match")
                return
            print("Decryption function at:", hex(func_addr))
            print("XOR key at:", hex(xorkey_addr))
            print("XOR key:", pe.get_data(xorkey_addr - pe_baseaddr, 0x40).hex())
            print("Cipher block at:", hex(cipher_addr))
            print("Cipher block size:", hex(size1))
            
            return func_addr, xorkey_addr, cipher_addr, size1
예제 #5
0
    def get_func_ep_grap(self):
        """Try to get the entrypoint of the matched function"""

        first_inst = self.match["first_inst"][0]
        first_addr = first_inst.info.address

        # Find the beginning of the function:
        # It is a node with at least 2 fathers which address a fulfills: address - 0x7 <= a <= address
        address_cond = "address >= " + str(hex(int(first_addr - 0x7))) + " and address <= " + str(hex(int(first_addr)))
        ep_pattern = EP_PATTERN.replace("FILL_ADDR_COND", address_cond)

        #print Color.step("Searching for the function entrypoint near 0x{:X}".format(first_addr))

        # search for the EP
        ep_matches = pygrap.match_graph(ep_pattern, self.test_graph)
        
        if len(ep_matches) != 1 or len(ep_matches["ep_func"]) != 1:
            print Color.error("Entrypoint not found or the function was not call for the address 0x{:X}".format(first_addr))
        else:
            self.func_addr = int(ep_matches["ep_func"][0]["ep"][0].info.address)
예제 #6
0
    def __init__(self, cfg, test_graph):
        """Initialization

        Arguments:
         - cfg(Dict): Config for a given pattern
         - test_graph(pygrap.graph_t): Grap representation of the binary
        """
        self.cfg = cfg
        self.test_graph = test_graph
        self.matches = []
        
        # search for patterns
        matches = pygrap.match_graph(self.cfg["func_pattern"], self.test_graph).items()[0][1]

        print Color.info("The graph {name} was found {size} time(s)".format(name=self.cfg["name"],
                                                                            size=len(matches)))
        # process matches
        for match in matches: 
            try:
                self.matches.append(Match(match, self.test_graph, self.cfg))
            except:
                continue
예제 #7
0
def main():
    if len(sys.argv) <= 1:
        print_usage()
        sys.exit(1)
    elif len(sys.argv) <= 2:
        verbose = False
        bin_path = sys.argv[1]
        dot_path = sys.argv[1] + ".grapcfg"
    else:
        verbose = True
        bin_path = sys.argv[2]
        dot_path = sys.argv[2] + ".grapcfg"

    if len(sys.argv) == 2 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
        print_usage()
        sys.exit(1)

    if bin_path[-8:] == ".grapcfg":
        print "The argument should be a binary and not a .grapcfg file"
        sys.exit(1)

    # use_existing specifies wether an existing dot file should be used unchanged or overwritten
    pygrap.disassemble_file(bin_path=bin_path,
                            dot_path=dot_path,
                            use_existing=True)

    test_graph = pygrap.getGraphFromPath(dot_path)

    if verbose:
        print "Analyzing", bin_path

    if not os.path.isfile(bin_path) or not os.path.isfile(dot_path):
        print "Error: binary or dot file doesn't exist, exiting."
        sys.exit(1)

    pattern_decrypt = "backspace_decrypt_algos.grapp"
    matches_decrypt = pygrap.match_graph(pattern_decrypt, test_graph)

    if len(matches_decrypt) >= 2:
        print "Error: two or more decryption algorithms matched, exiting."
        sys.exit(1)

    for algorithm_name in matches_decrypt:
        if verbose:
            print "Matched algorithm:", algorithm_name

        first_match = matches_decrypt[algorithm_name][0]
        first_group = first_match["A"]
        first_instruction = first_group[0]
        description = first_instruction.info.inst_str
        address = first_instruction.info.address

        if verbose:
            print "Found decryption pattern at address", hex(int(address))

        # Find the beginning of the function:
        # It is a node with at least 5 fathers which address a fulfills: address - 30 <= a <= address
        address_cond = "address >= " + str(hex(
            int(address - 30))) + " and address <= " + str(hex(int(address)))
        entrypoint_pattern = """
        digraph decrypt_fun_begin{
            ep [label="ep", cond="nfathers >= 5 and FILL_ADDR_COND", getid="ep"]
        }
        """.replace("FILL_ADDR_COND", address_cond)

        if verbose:
            print "Looking for entrypoint with pattern:"
            print entrypoint_pattern
        matches_entrypoint = pygrap.match_graph(entrypoint_pattern, test_graph)

        if len(matches_entrypoint) != 1 or len(
                matches_entrypoint["decrypt_fun_begin"]) != 1:
            print "Error: Entrypoint not found, exiting"
            sys.exit(1)

        entrypoint = hex(
            int(matches_entrypoint["decrypt_fun_begin"][0]["ep"]
                [0].info.address))

        if verbose:
            print "Found decryption function at", entrypoint

        push_call_pattern = """
        digraph push_call_decrypt{
            push [label="push", cond="opcode is push", repeat=2, getid=push]
            call [label="call", cond="opcode is call"]
            entrypoint [label="entrypoint", cond="address == FILL_ADDR"]

            push -> call
            call -> entrypoint [childnumber=2]
        }
        """.replace("FILL_ADDR", entrypoint)

        if verbose:
            print "Looking for calls to decrypt function with pattern:"
            print push_call_pattern
        matches_calls = pygrap.match_graph(push_call_pattern, test_graph)

        if len(matches_calls) == 0:
            print "error: No call found, exiting"
            sys.exit(1)

        if verbose:
            print len(matches_calls["push_call_decrypt"]
                      ), "calls to decrypt function found."

        str_tuple = []
        for m in matches_calls["push_call_decrypt"]:
            # Work on matches with immediate arguments such as:
            # PUSH (between 2 and 5) with hex arguments (for instance: 9, 0x12 or 0x4012a3)
            # CALL entrypoint
            if len(m["push"]
                   [-2].info.arg1) == 1 or "0x" in m["push"][-2].info.arg1:
                str_tuple.append((int(m["push"][-2].info.arg1,
                                      16), int(m["push"][-1].info.arg1, 16)))

        decrypted_strings = decrypt_strings(algorithm_name, str_tuple,
                                            bin_path)

        print "Decrypted strings in", bin_path + ":"
        for d in decrypted_strings:
            print d
        print ""

    pygrap.graph_free(test_graph, True)
예제 #8
0
    def _analyzing(self):

        cfg = self.graph

        # Clear the list
        del self._analyzed_patterns[:]

        #
        # Control flow graph extraction
        #

        if not cfg.graph:
            print "[I] Creation of the Control Flow Graph (can take few seconds)"

            # Get the CFG of the binary
            cfg.extract()

        #
        # Pattern searching
        #
        print "[I] Searching for patterns."

        # Update "Test" patterns
        MODULES["Test"]["Misc"] = get_test_misc()

        # Group
        for grp_name, grp in MODULES.iteritems():
            #print "Group: " + grp_name

            # Group->Type
            for tp_name, tp in grp.iteritems():
                #print "\tType: " + tp_name

                if grp_name == "Test" and tp_name == "Misc":
                    patterns_path_list = []
                    for algo in tp:
                        for patterns in algo.get_patterns():
                            for pattern in patterns.get_patterns():
                                path = pattern.get_file()
                                #print "\t\tAdded patterns from " + path
                                print "Added patterns from " + path
                                patterns_path_list.append(path)

                    #print "\t\tMatching patterns against binary... this may take a few seconds"
                    print "Matching patterns against binary... this may take a few seconds"
                    matches = match_graph(patterns_path_list,
                                          cfg.graph,
                                          print_all_matches=True)
                    #print "\t\t", len(matches), "patterns found."
                    print len(matches), "patterns found."
                    for pattern_name in matches:
                        pattern = Pattern(name=pattern_name)
                        patterns_t = Patterns(patterns=[pattern],
                                              name=pattern_name,
                                              perform_analysis=False,
                                              matches=matches[pattern_name])
                        pattern._matches = []
                        for c_match in matches[pattern_name]:
                            match = Match(c_match, pattern_name)
                            pattern._matches.append(match)
                        ana = PatternsAnalysis(patterns_t, None)
                        ana.filter_patterns()
                        self._analyzed_patterns.append(ana)
                else:
                    for algo in tp:

                        # print algo
                        print "\t\tAlgorithm: %s" % algo.get_name()

                        # List of Patterns
                        for patterns in algo.get_patterns():

                            print "\t\t\tFunction: %s" % patterns.get_name()

                            # List of Pattern
                            for pattern in patterns.get_patterns():
                                print "\t\t\t\t[I] Searching for " + pattern.get_name(
                                )

                                pattern.parcourir(cfg.graph)
                                print "\t\t\t\t[I] %d %s pattern found" % (len(
                                    pattern.get_matches()), pattern.get_name())

                            #
                            # Analyzing
                            #
                            if patterns._perform_analysis:
                                print "\t\t\t\t[I] Linking the patterns matches which are in the same area"
                            ana = PatternsAnalysis(patterns, algo)

                            print "\t\t\t\t[I] Filtering those patterns"
                            ana.filter_patterns()

                            # Add the analyzed patterns to the list
                            self._analyzed_patterns.append(ana)
예제 #9
0
 def match(self, graph):
     path_list = [p.get_file() for p in self._patterns]
     self._matches = match_graph(path_list, graph)