Skip to content

Built in script function encapsulation

In the x64dbg debugger, there are rich built-in script commands that are very useful for developers. However, sometimes when writing native scripts, we may want to encapsulate these commands in a more readable and efficient form to improve code maintainability and development efficiency.

To achieve this goal, we can write Python functions to encapsulate these native commands. In this way, developers can directly call these functions in Python code without having to read the document or remember specific commands and parameters. This encapsulation makes the code clearer and easier to understand, and can be better integrated into the development workflow, significantly improving development efficiency.

Module command encapsulation

This code is a Python script that interacts with the debugger using the x32dbg module. It defines a ModuleInfo class that provides methods to obtain information about the module where the executing instruction is located, including base address, size, hash value, etc. Obtain the address (EIP) of the current executing instruction by connecting to the debugger, then use the ModuleInfo class to retrieve module information related to that address and print it out.

python
from x32dbg import Debugger

# Module related classes
class ModuleInfo:
    def __init__(self, debugger):
        self.debugger = debugger

    # Accept integer type parameters passed in
    def get_module_info(self, decimal_address, info_type):
        try:
            ref = self.debugger.script_runcmd_ex("mod.{}({})".
            	format(info_type, hex(decimal_address)))
            return ref if ref is not None else False
        except Exception:
            return False

    def base(self, decimal_address):
        return self.get_module_info(decimal_address, "base")

    def party(self, decimal_address):
        return self.get_module_info(decimal_address, "party")

    def size(self, decimal_address):
        return self.get_module_info(decimal_address, "size")

    def hash(self, decimal_address):
        return self.get_module_info(decimal_address, "hash")

    def entry(self, decimal_address):
        return self.get_module_info(decimal_address, "entry")

    def system(self, decimal_address):
        return self.get_module_info(decimal_address, "system")

    def user(self, decimal_address):
        return self.get_module_info(decimal_address, "user")

    def main(self):
        return self.get_module_info(0, "main")

    def rva(self, decimal_address):
        return self.get_module_info(decimal_address, "rva")

    def offset(self, decimal_address):
        return self.get_module_info(decimal_address, "offset")

    def isexport(self, decimal_address):
        return self.get_module_info(decimal_address, "isexport")

if __name__ == "__main__":
    dbg = Debugger(address="127.0.0.1", port=6589)
    if dbg.connect() == True:

        # Define module classes
        module = ModuleInfo(dbg)
        eip = dbg.get_eip()

        # Obtain the mode number of the module.
        # Returning false indicates that it is a user module, 
        # while returning true indicates that it is a system module
        party = module.party(eip)
        print("party = {}".format(party))

        # Get module base address
        base = module.base(eip)
        print("base = {}".format(hex(base)))

        # Reverse remittance module size
        size = module.size(eip)
        print("size = {}".format(size))

        # Reverse module hash
        hash = module.hash(eip)
        print("hash = {}".format(hash))

        # Return module entry address
        entry = module.entry(eip)
        print("entry = {}".format(hex(entry)))

        # If eip is a system module, it is true; otherwise, it is false
        system = module.system(eip)
        print("system = {}".format(system))

        # If eip is a user module, return true; otherwise, it is false
        user = module.system(eip)
        print("user = {}".format(user))

        # Return the RVA offset address at the eip position
        rva = module.rva(eip)
        print("rva = {}".format(hex(rva)))

        # Return the FOA address at the eip location
        foa = module.offset(eip)
        print("foa = {}".format(hex(foa)))

        # Determine if the address is a function exported from the module
        isexport = module.isexport(eip)
        print("isexport = {}".format(isexport))

        dbg.close_connect()
    else:
        print("Failed to connect debugger")

After the program runs successfully, it will output the following content:

python
party = False
base = 0x330000
size = 28672
hash = 303466845
entry = 0x3315bb
system = False
user = False
rva = 0x15bb
foa = 0x9bb
isexport = False

Disassembly command encapsulation

This code implements a DisassemblyInfo class to retrieve disassembly information for instructions at specific addresses through the x32dbg debugger. This class connects to the debugger and utilizes the command execution function provided by the debugger to obtain various instruction information at a specified address, such as instruction length, instruction type (conditional branch, jump, etc.), immediate value, etc.

The core purpose of the code is to obtain disassembly information at a specified address through a debugger and display various instruction properties, such as instruction length, whether it is a conditional branch, whether it is a jump instruction, whether it is a call instruction, etc. By calling different methods, various information about instructions at specific addresses can be obtained, which is helpful for debugging and analyzing binary programs.

python
from x32dbg import Debugger

# Disassembly related classes
class DisassemblyInfo:
    def __init__(self, debugger):
        self.debugger = debugger
    def get_disassembly_info(self, decimal_address, info_type):
        try:
            ref = self.debugger.script_runcmd_ex("dis.{}({})".
                format(info_type, hex(decimal_address)))
            return ref if ref is not None else False
        except Exception:
            return False
    def len(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "len")

    def iscond(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "iscond")

    def isbranch(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "isbranch")

    def isret(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "isret")

    def iscall(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "iscall")

    def ismem(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "ismem")

    def isnop(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "isnop")

    def isunusual(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "isunusual")

    def branchdest(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "branchdest")

    def branchexec(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "branchexec")

    def imm(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "imm")

    def brtrue(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "brtrue")

    def brfalse(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "brfalse")

    def next(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "next")

    def prev(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "prev")

    def iscallsystem(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "iscallsystem")

    def mnemonic(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "mnemonic")

    def text(self, decimal_address):
        return self.get_disassembly_info(decimal_address, "text")

if __name__ == "__main__":
    dbg = Debugger(address="127.0.0.1", port=6589)
    if dbg.connect() == True:

        # Define Disassembly classes
        disassembly = DisassemblyInfo(dbg)
        eip = dbg.get_eip()

        # Get the instruction length at eip
        dis_len = disassembly.len(eip)
        print("len = {}".format(dis_len))

        # Determine whether the current eip 
        # position is a conditional jump instruction
        dis_iscond = disassembly.iscond(eip)
        print("lscond = {}".format(dis_iscond))

        # Determine whether the current EIP address is a branch instruction
        dis_isbranch = disassembly.isbranch(eip)
        print("isbranch = {}".format(dis_isbranch))

        # Determine whether the current EIP address is a RET return instruction
        dis_isret = disassembly.isret(eip)
        print("isret = {}".format(dis_isret))

        # Determine whether the current EIP address is a call instruction
        dis_iscall = disassembly.iscall(eip)
        print("iscall = {}".format(dis_iscall))

        # Determine whether the current EIP address is a memory operand
        dis_ismem = disassembly.ismem(eip)
        print("ismem = {}".format(dis_ismem))

        # Determine if the current address is nop
        dis_isnop = disassembly.isnop(eip)
        print("ienop = {}".format(dis_isnop))

        # Determine if the current address indicates an abnormal address
        dis_isunusual = disassembly.isunusual(eip)
        print("isunusual = {}".format(dis_isunusual))

        # Branch destination eip of instructions
        dis_branchdest = disassembly.branchdest(eip)
        print("branchdest = {}".format(dis_branchdest))

        # If a branch of at is to be executed, return true
        dis_branchexec = disassembly.branchexec(eip)
        print("branchexec = {}".format(dis_branchexec))

        # Get the immediate value of the current instruction position
        dis_imm = disassembly.imm(eip)
        print("imm = {}".format(dis_imm))

        # The branch destination address of the instruction is eip
        dis_brtrue = disassembly.brtrue(eip)
        print("brtrue = {}".format(dis_brtrue))

        # If there is a conditional branch at eip, 
        # return the address of the next instruction
        dis_brfalse = disassembly.brfalse(eip)
        print("brfalse = {}".format(dis_brfalse))

        # Return the address of the next instruction to the EIP address
        dis_next = disassembly.next(eip)
        print("next = {}".format(hex(dis_next)))

        # Return the address of the previous instruction to the EIP address
        dis_prev = disassembly.prev(eip)
        print("dis_prev = {}".format(hex(dis_prev)))

        # Determine whether the current instruction is a system module instruction
        dis_iscallsystem = disassembly.iscallsystem(eip)
        print("dis_iscallsystem = {}".format(dis_iscallsystem))

        # Returns the mnemonic at the eip address
        dis_mnemonic = disassembly.mnemonic(eip)
        print("mnemonic = {}".format(dis_mnemonic))

        dbg.close_connect()
    else:
        print("Failed to connect debugger")

After the program runs successfully, it will output the following content:

python
len = 2
lscond = 1
isbranch = 1
isret = False
iscall = False
ismem = False
ienop = False
isunusual = False
branchdest = 3347811
branchexec = 1
imm = 3347811
brtrue = 3347811
brfalse = 3347799
next = 0x331557
dis_prev = 0x331552
dis_iscallsystem = False
mnemonic = False

Memory command encapsulation

This code mainly involves the interaction function with the debugger, establishing a connection with the debugger through the x32dbg module, and implementing various operations through the Debugger class. Among them, the MemoryInfo class encapsulates methods for obtaining memory information, reading memory content, and handling exception information. By calling the corresponding command, the memory information at a given address can be obtained, including validity, base address, size, and whether it is executable. At the same time, the memory content can be read and exception information can be processed. Finally, the result is output for the user to view.

python
from x32dbg import Debugger

# Memory related classes
class MemoryInfo:
    def __init__(self, debugger):
        self.debugger = debugger

    def get_memory_info(self, decimal_address, info_type):
        try:
            ref = self.debugger.script_runcmd_ex("mem.{}({})".
                format(info_type, hex(decimal_address)))
            return ref if ref is not None else False
        except Exception:
            return False

    def valid(self, decimal_address):
        return self.get_memory_info(decimal_address, "valid")

    def base(self, decimal_address):
        return self.get_memory_info(decimal_address, "base")

    def size(self, decimal_address):
        return self.get_memory_info(decimal_address, "size")

    def iscode(self, decimal_address):
        return self.get_memory_info(decimal_address, "iscode")

    def decodepointer(self, decimal_address):
        return self.get_memory_info(decimal_address, "decodepointer")

    def bswap(self, decimal_address):
        return self.debugger.script_runcmd_ex("bswap({})".format(decimal_address))

    def readbyte(self, decimal_address):
        return self.debugger.script_runcmd_ex("ReadByte({})".format(decimal_address))

    def readword(self, decimal_address):
        return self.debugger.script_runcmd_ex("ReadWord({})".format(decimal_address))

    def readdword(self, decimal_address):
        return self.debugger.script_runcmd_ex("ReadDword({})".format(decimal_address))

    def readqword(self, decimal_address):
        return self.debugger.script_runcmd_ex("ReadQword({})".format(decimal_address))

    def readptr(self, decimal_address):
        return self.debugger.script_runcmd_ex("ReadPtr({})".format(decimal_address))

    def firstchance(self):
        return self.debugger.script_runcmd_ex("ex.firstchance()")

    def addr(self):
        return self.debugger.script_runcmd_ex("ex.addr()")

    def code(self):
        return self.debugger.script_runcmd_ex("ex.code()")

    def flags(self):
        return self.debugger.script_runcmd_ex("ex.flags()")

    def infocount(self):
        return self.debugger.script_runcmd_ex("ex.infocount()")

    def info(self, decimal_address):
        return self.debugger.script_runcmd_ex("ex.info({})".format(decimal_address))

if __name__ == "__main__":
    dbg = Debugger(address="127.0.0.1", port=6589)
    if dbg.connect() == True:

        # Define Memory classes
        memory = MemoryInfo(dbg)
        eip = dbg.get_eip()

        # Check if the EIP address is valid, return true if it is valid
        valid = memory.valid(eip)
        print("valid = {}".format(valid))

        # Obtain the base address at the eip location
        base = memory.base(eip)
        print("base = {}".format(hex(base)))

        # Get the current size of EIP memory
        size = memory.size(eip)
        print("size = {}".format(size))

        # Check if the current EIP is an executable page and return True successfully
        iscode = memory.iscode(eip)
        print("iscode = {}".format(iscode))

        # Decrypting eip memory pointers
        decodepointer = memory.decodepointer(eip)
        print("decodepointer = {}".format(decodepointer))

        # Read EIP memory integer type and pass in hexadecimal format
        readbyte = memory.readbyte(hex(eip))
        print("readbyte = {}".format(hex(readbyte)))

        # Is the last exception the first exception
        exfirstchance = memory.ex_firstchance()
        print("exfirstchance = {}".format(exfirstchance))

        # Last abnormal address
        exaddr = memory.ex_addr()
        print("exaddr = {}".format(exaddr))

        # The last exception code
        excode = memory.ex_code()
        print("excode = {}".format(excode))

        # The last exception flag
        exflag = memory.ex_flags()
        print("exflag = {}".format(exflag))

        # Last abnormal information count
        exinfocount = memory.ex_infocount()
        print("exinfocount = {}".format(exinfocount))

        # The last exception message is zero if the index is out of range
        exinfo = memory.ex_info(1)
        print("exinfo = {}".format(exinfo))

After the program runs successfully, it will output the following content:

python
valid = 1
base = 0xc21000
size = 4096
iscode = 1
decodepointer = 3832413802
readbyte = 0xe8
exfirstchance = 1
exaddr = 12719547
excode = 2147483651
exflag = False
exinfocount = 1
exinfo = False

Arithmetic command encapsulation

This code defines a class ArithmeticInfo that provides methods for performing various arithmetic operations on registers using the x32dbg debugger. The class initializes with a debugger object and allows for actions like incrementing, decrementing, addition, subtraction, multiplication, division, bitwise AND, OR, XOR, negation, left/right shifts, logical and arithmetic shifts, and bitwise NOT on registers. The script demonstrates the usage of these methods by connecting to a debugger instance and performing a series of register operations.

python
from x32dbg import Debugger

class ArithmeticInfo:
    def __init__(self, debugger):
        self.debugger = debugger

    def set_arithmetic_info(self, action, register, decimal_int=None):
        try:
            if decimal_int is not None:
                ref = self.debugger.script_runcmd("{} {},{}".format(action, register, decimal_int))
            else:
                ref = self.debugger.script_runcmd("{} {}".format(action, register))
            return ref if ref is not None else False
        except Exception:
            return False

    def reg_inc(self, register):
        return self.set_arithmetic_info("inc", register)

    def reg_dec(self, register):
        return self.set_arithmetic_info("dec", register)

    def reg_add(self, register, decimal_int):
        return self.set_arithmetic_info("add", register, decimal_int)

    def reg_sub(self, register, decimal_int):
        return self.set_arithmetic_info("sub", register, decimal_int)

    def reg_mul(self, register, decimal_int):
        return self.set_arithmetic_info("mul", register, decimal_int)

    def reg_div(self, register, decimal_int):
        return self.set_arithmetic_info("div", register, decimal_int)

    def reg_and(self, register, decimal_int):
        return self.set_arithmetic_info("and", register, decimal_int)

    def reg_or(self, register, decimal_int):
        return self.set_arithmetic_info("or", register, decimal_int)

    def reg_xor(self, register, decimal_int):
        return self.set_arithmetic_info("xor", register, decimal_int)

    def reg_neg(self, register):
        return self.set_arithmetic_info("neg", register)

    def reg_rol(self, register, decimal_int):
        return self.set_arithmetic_info("rol", register, decimal_int)

    def reg_ror(self, register, decimal_int):
        return self.set_arithmetic_info("ror", register, decimal_int)

    def reg_shl(self, register, decimal_int):
        return self.set_arithmetic_info("shl", register, decimal_int)

    def reg_shr(self, register, decimal_int):
        return self.set_arithmetic_info("shr", register, decimal_int)

    def reg_sal(self, register, decimal_int):
        return self.set_arithmetic_info("sal", register, decimal_int)

    def reg_sar(self, register, decimal_int):
        return self.set_arithmetic_info("sar", register, decimal_int)

    def reg_not(self, register, decimal_int):
        return self.set_arithmetic_info("not", register, decimal_int)

if __name__ == "__main__":
    dbg = Debugger(address="127.0.0.1", port=6589)
    if dbg.connect() == True:

        # Define Arithmetic classes
        arithmetic = ArithmeticInfo(dbg)

        # These methods return True if successful, False otherwise

        # Increment register
        arithmetic.reg_inc("eax")

        # Decrement register
        arithmetic.reg_dec("ebx")

        # Perform add operation on register
        arithmetic.reg_add("ecx", 10)

        # Perform sub operation on register
        arithmetic.reg_sub("edx", 5)

        # Perform mul operation on register
        arithmetic.reg_mul("esi", 2)

        # Perform div operation on register
        arithmetic.reg_div("edi", 3)

        # Perform and operation on register
        arithmetic.reg_and("ebp", 0xFF)

        # Perform or operation on register
        arithmetic.reg_or("esp", 0x0F)

        # Perform xor operation on register
        arithmetic.reg_xor("eax", 0xAA)

        # Perform neg operation on register
        arithmetic.reg_neg("ebx")

        # Perform rol operation on register
        arithmetic.reg_rol("ecx", 2)

        # Perform ror operation on register
        arithmetic.reg_ror("edx", 3)

        # Perform shl operation on register
        arithmetic.reg_shl("esi", 4)

        # Perform shr operation on register
        arithmetic.reg_shr("edi", 5)

        # Perform sal operation on register
        arithmetic.reg_sal("ebp", 2)

        # Perform sar operation on register
        arithmetic.reg_sar("esp", 3)

        # Perform not operation on register
        arithmetic.reg_not("eax", 0xFF)

All functions in this code return true if executed successfully, otherwise return false.