LyScript Batch Search Disassembly Feature

The LyScript plug-in implements the batch search function for specific assembly instruction fragments. The user passes in an assembly instruction list, and then loops through all instruction features in the list. If found, the memory address of the instruction is returned.

Get the machine code of the assembly instruction: This function is mainly realized to get the machine code corresponding to the assembly instruction passed in by the user. You can implement this code in this way.

from LyScript32 import MyDebug

if __name__ == "__main__":
    dbg = MyDebug()
    connect_flag = dbg.connect()
    print("Connection Status: {}".format(connect_flag))

    addr = dbg.create_alloc(1024)

    print("heap space: {}".format(hex(addr)))

    asm_size = dbg.assemble_code_size("mov eax,1")
    print("Assembly code occupies bytes: {}".format(asm_size))

    write = dbg.assemble_write_memory(addr,"mov eax,1")

    byte_code = bytearray()

    for index in range(0,asm_size):
        read = dbg.read_memory_byte(addr + index)
        print("{:02x} ".format(read),end="")

    dbg.delete_alloc(addr)

Encapsulate the above code interface, implement get_opcode_from_assemble(), the user passes in the assembly instruction, and obtains the machine code corresponding to the instruction.

from LyScript32 import MyDebug

# Pass in the assembly code to get the corresponding machine code
def get_opcode_from_assemble(dbg_ptr,asm):
    byte_code = bytearray()

    addr = dbg_ptr.create_alloc(1024)
    if addr != 0:
        asm_size = dbg_ptr.assemble_code_size(asm)
        # print("Assembly code occupies bytes: {}".format(asm_size))

        write = dbg_ptr.assemble_write_memory(addr,asm)
        if write == True:
            for index in range(0,asm_size):
                read = dbg_ptr.read_memory_byte(addr + index)
                # print("{:02x} ".format(read),end="")
                byte_code.append(read)
        dbg_ptr.delete_alloc(addr)
        return byte_code
    else:
        return bytearray(0)

if __name__ == "__main__":
    dbg = MyDebug()
    connect_flag = dbg.connect()
    print("Connection Status: {}".format(connect_flag))

    # Get assembly code
    byte_array = get_opcode_from_assemble(dbg,"xor eax,eax")
    for index in byte_array:
        print(hex(index),end="")
    print()

    # assemble a sequence
    asm_list = ["xor eax,eax", "xor ebx,ebx", "mov eax,1"]
    for index in asm_list:
        byte_array = get_opcode_from_assemble(dbg, index)
        for index in byte_array:
            print(hex(index),end="")
        print()

    dbg.close()

Run the above code to find the memory address that meets the conditions.

Batch search for disassembly code: Similar to searching for machine code, this function implements the search for all instruction sets in the code segment, whether it exists in the matching list, and returns the address if it exists.

from LyScript32 import MyDebug

if __name__ == "__main__":
    dbg = MyDebug()
    dbg.connect()

    local_base_start = dbg.get_local_base()
    local_base_end = local_base_start + dbg.get_local_size()
    print("start address: {} --> end address: {}".format(hex(local_base_start),hex(local_base_end)))

    search_asm = ['test eax,eax', 'cmp esi, edi', 'pop edi', 'cmp esi,edi', 'jmp esp']

    while local_base_start <= local_base_end:
        disasm = dbg.get_disasm_one_code(local_base_start)
        print("address: 0x{:08x} --> disassembly: {}".format(local_base_start,disasm))

        # find instructions
        for index in range(0, len(search_asm)):
            if disasm == search_asm[index]:
                print("address: {} --> disassembly: {}".format(hex(local_base_start), disasm))

        # increment counter
        local_base_start = local_base_start + dbg.get_disasm_operand_size(local_base_start)

    dbg.close()

Search disassembly list features: Use python to implement the method, scan the memory range through a specific method, and output the specific memory address of the instruction if the instruction set sequence we need appears.

from LyScript32 import MyDebug

# Pass in the assembly code to get the corresponding machine code
def get_opcode_from_assemble(dbg_ptr,asm):
    byte_code = bytearray()

    addr = dbg_ptr.create_alloc(1024)
    if addr != 0:
        asm_size = dbg_ptr.assemble_code_size(asm)
        # print("Assembly code occupies bytes: {}".format(asm_size))

        write = dbg_ptr.assemble_write_memory(addr,asm)
        if write == True:
            for index in range(0,asm_size):
                read = dbg_ptr.read_memory_byte(addr + index)
                # print("{:02x} ".format(read),end="")
                byte_code.append(read)
        dbg_ptr.delete_alloc(addr)
        return byte_code
    else:
        return bytearray(0)

# Search for the machine code and return it if it exists
def SearchOpCode(dbg_ptr, Search):

    # Search machine code and convert to list
    op_code = []
    for index in Search:
        byte_array = get_opcode_from_assemble(dbg, index)
        for index in byte_array:
            op_code.append(hex(index))

    # print("Machine code list: {}".format(op_code))

    # Convert a list of machine codes to a string
    # 1. First convert to a list of strings
    x = [str(i) for i in op_code]

    # 2. Convert the list of strings to strings
    # search_code = ' '.join(x).replace("0x","")
    search_code = []

    # Add less than three leading 0s
    for l in range(0,len(x)):
        if len(x[l]) <= 3:
            # If it is less than 3 digits, add 0 in front
            # print(''.join(x[l]).replace("0x","").zfill(2))
            search_code.append(''.join(x[l]).replace("0x","").zfill(2))
        else:
            search_code.append(''.join(x[l]).replace("0x", ""))

    # 3. Turn into a string
    search_code = ' '.join(search_code).replace("0x", "")
    print("search string: {}".format(search_code))

    # Invoke the search command
    ref = dbg.scan_memory_one(search_code)
    if ref != None or ref != 0:
        return ref
    else:
        return 0
    return 0

if __name__ == "__main__":
    dbg = MyDebug()
    connect_flag = dbg.connect()
    print("Connection Status: {}".format(connect_flag))

    # Search for a sequence of instructions to quickly find and build exploit code
    SearchCode = [
        ["pop ecx", "pop ebp", "ret", "push ebp"],
        ["push ebp", "mov ebp,esp"],
        ["mov ecx, dword ptr ds:[eax+0x3C]", "add ecx, eax"]
    ]

    # Retrieve memory instruction set
    for item in range(0, len(SearchCode)):
        Search = SearchCode[item]
        ret = SearchOpCode(dbg, Search)
        print("The memory where the searched instruction is located: {}".format(hex(ret)))

    dbg.close()

In the above code, the first function get_opcode_from_assemble(dbg_ptr,asm) is used to obtain the machine code from the assembly instruction passed in by the user, and the function SearchOpCode(dbg_ptr, Search) is used to convert the assembly list passed in by the user into a continuous string .

1. Fragment 1 realizes converting the machine code into a hexadecimal array

    op_code = []
    for index in Search:
        byte_array = get_opcode_from_assemble(dbg, index)
        for index in byte_array:
            op_code.append(hex(index))

2. Fragment 2 removes the 0x prefix from the hexadecimal machine code, and judges whether the hexadecimal number is less than or equal to 3 digits, and if so, adds 0 to the output prefix, otherwise directly outputs to the search_code variable.

    # Convert a list of machine codes to a string
    # 1. First convert to a list of strings
    x = [str(i) for i in op_code]

    # 2. Convert the list of strings to strings
    # search_code = ' '.join(x).replace("0x","")
    search_code = []

    # Add less than three leading 0s
    for l in range(0,len(x)):
        if len(x[l]) <= 3:
            # If it is less than 3 digits, add 0 in front
            # print(''.join(x[l]).replace("0x","").zfill(2))
            search_code.append(''.join(x[l]).replace("0x","").zfill(2))
        else:
            search_code.append(''.join(x[l]).replace("0x", ""))

3. Fragment 3, finally call the search machine code command, first convert the string list into a string, and then call dbg.scan_memory_one(search_code) to complete the entire search process.

    search_code = ' '.join(search_code).replace("0x", "")
    print("search string: {}".format(search_code))

    # Invoke the search command
    ref = dbg.scan_memory_one(search_code)
    if ref != None or ref != 0:
        return ref
    else:
        return 0
    return 0

In the final call, the user passes in a two-dimensional list, and then searches for all eligible memory addresses in the list in turn.

Tags: Python

Posted by Clandestinex337 on Fri, 10 Feb 2023 20:22:34 +0530