elbee-cyber / RopView

A BinaryNinja plugin for contextual gadget analysis and semantic/hueristic based querying.
MIT License
48 stars 1 forks source link

Rewrite GadgetSearch for regex support #24

Closed elbee-cyber closed 6 months ago

elbee-cyber commented 6 months ago

Use regex matching to find control sites when performing gadget search

elbee-cyber commented 6 months ago

https://github.com/sashs/Ropper/blob/a1141ab665d97806bee512c186ee04ef6666499c/ropper/arch.py#L205

elbee-cyber commented 6 months ago

This will require redoing GadgetSearch and adding cleaning constants

elbee-cyber commented 6 months ago
.constants:

cop_x86 = (
    (b'\xff',2,b'\xff[\x10\x11\x12\x13\x16\x17]','call'),                   # call [reg]
    (b'\xf2\xff',3,b'\xf2\xff[\x10\x11\x12\x13\x16\x17]','call'),               # bnd call [reg]
    (b'\xff',2,b'\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]','call'),               # call reg
    (b'\xf2\xff',3,b'\xf2\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]','call'),           # bnd call reg
    (b'\xff\x14\x24',3,b'\xff\x14\x24','call'),                     # call [rsp]
    (b'\xf2\xff\x14\x24',4,b'\xf2\xff[\x14\x24]\x24','call'),               # bnd call [rsp]
    (b'\xff\x55\x00',3,b'\xff\x55\x00','call'),                     # call [rbp]
    (b'\xf2\xff\x55\x00',4,b'\xf2\xff\x55\x00','call'),                 # bnd call [rbp]
    (b'\xff',3,b'\xff[\x50-\x53\x55-\x57][\x00-\xff]{1}','call'),               # call [reg+n]
    (b'\xf2\xff',4,b'\xf2\xff[\x50-\x53\x55-\x57][\x00-\xff]{1}','call'),           # bnd call [reg+n]
    (b'\xe8',5,b'\xe8[\x00-\xff]{4}','call'),                       # call n
    (b'\xff',6,b'\xff[\x90\x91\x92\x93\x94\x96\x97][\x00-\x0ff]{4}','call')         # call [reg+n]
)

rop_x86 = (
    (b'\xc3',1,b'\xc3','ret'),                              # ret
    (b'\xc2',3,b'\xc2[\x00-\xff]{2}','ret')                         # ret n
)

jop_x86 = (
    (b'\xff',2,b'\xff[\x20\x21\x22\x23\x26\x27]','jmp'),                    # jmp [reg]
    (b'\xf2\xff',3,b'\xf2\xff[\x20\x21\x22\x23\x26\x27]','jmp'),                # bnd jmp [reg]
    (b'\xff',2,b'\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]','jmp'),                # jmp reg
    (b'\xf2\xff',3,b'\xf2\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]','jmp'),            # bnd jmp reg
    (b'\xff\x24\x24',3,b'\xff\x24\x24','jmp'),                      # jmp [rsp]
    (b'\xf2\xff\x24\x24',4,b'\xf2\xff\x24\x24','jmp'),                  # bnd jmp [rsp]
    (b'\xff\x65\x00',3,b'\xff\x65\x00','jmp')                       # jmp [rbp]
    (b'\xf2\xff\x65\x00',4,b'\xf2\xff\x65\x00','jmp'),                  # bnd jmp [rbp]
    (b'\xff',6,b'\xff[\xa0\xa1\xa2\xa3\xa6\xa7][\x00-\x0ff]{4}','jmp'),         # jmp [reg+n]
    (b'\xf2\xff',7,b'\xf2\xff[\xa0\xa1\xa2\xa3\xa6\xa7][\x00-\x0ff]{4}','jmp'),     # bnd jmp [reg+n]
    (b'\xff\xa4\x24',7,b'\xff\xa4\x24[\x00-\xff]{4}','jmp'),                # jmp [rsp+n]
    (b'\xf2\xff\xa4\x24',8,b'\xf2\xff\xa4\x24[\x00-\xff]{4}','jmp'),            # bnd jmp [rsp+n]
    (b'\xff',3,b'\xff[\x60-\x63\x65-\x67][\x00-\xff]{1}','jmp),             # jmp [reg+n]
    (b'\xf2\xff',4,b'\xf2\xff[\x60-\x63\x65-\x67][\x00-\xff]{1}','jmp'),            # bnd jmp [reg+n]
    (b'\xe9',5,b'\xe9[\x00-\xff]{4}','jmp')                         # jmp n
)

sys_x86 = (
    (b'\xcd\x80',2,b'\xcd\x80','int 0x80'),                         # int 0x80
    (b'\x0f\x05',2,b'\x0f\x05','syscall'),                          # syscall
    (b'\x0f\x34',2,b'\0x0f\x34','sysenter'),                        # sysenter
    (b'\x65\xff\x15\x10\x00\x00\x00',7,b'\x65\xff\x15\x10\x00\x00\x00','call gs:[10]')  # call gs:[10]
)

ctrl_mnemonics_x86 = (
    'jmp',
    'ret',
    'call',
    'jne',
    ...
)

ctrl_x86 = {
    "rop":rop_x86,
    "jop":jop_x86,
    "cop":cop_x86,
    "sys":sys_x86,
    "mnemonics":ctrl_mnemonics_x86
}

.gadgetsearch
# Combine ctrl pool based on options
control_insn = build_ctrl_pool()

for ctrl in control_insn:
    curr_site = bv.start

    # Search all of bv for current ctrl
    while curr_site is not None:
        # Find ctrl insn header constant
        curr_site = bv.find_next_data(curr_site,ctrl[0])
        if curr_site is None:
            break
        # Confirm site actually contains current ctrl insn
        if re.match(ctrl[2],bv.read(curr_site,ctrl[1])) != None:
            # Saved to increase next curr_site after depth search
            save = curr_site
            for i in range(0,depth):
                if not bv.get_segment_at(curr_site).executable:
                    break
                else:
                    disasm = capstone_process()
                    curr_site = save-i
                    insn = bv.read(curr_site,i+ctrl[1])

                    # Multi-branch check
                    if not multibranch_opt:
                        multibranch = 0
                        for mnemonic in control_mnemonics:
                            if mnemonic in disasm:
                                multibranch += 1
                        if multibranch > 1:
                            break

                    # Broken gadget (original ctrl_insn no longer exists
                    if disasm == '' or disasm == ' ' or ctrl[3] not in disasm:
                        continue

                    # Handle duplicates
                    if not repeat:
                        if insn in used_gadgets:
                            continue
                        used_gadgets.append(insn)

                    # All checks passed, save to cache
                    access['pool_disasm'][curr_site] = disasm
                    access['pool_asm'][curr_site] = insn
        # Next start address for ctrl insn site search
        curr_site = save+1
elbee-cyber commented 6 months ago

The above psuedocode would also fix #27

elbee-cyber commented 6 months ago

We'd store pools in bv so this fixes #22