Skip to content

PE Structure Retrieval SafeSEH Memory Address

SafeSEH (Safe Structured Exception Handling) is a security mechanism provided by the Windows operating system to prevent malicious software from exploiting buffer overflow vulnerabilities to attack applications. When an application uses structured exception handling (SEH), its exception handling linked list (ExceptionHandler) can be used by attackers to execute code injection attacks. This is because the exception handling linked list is essentially a pointer array. If an application uses unverified pointers to point to the exception handling function, attackers can construct malicious exception handling modules to overwrite the original handler, forcing the program to execute the code injected by the attacker. This attack technique is called SEH Overwrite.

To address this issue, the SafeSEH mechanism has been introduced into the Windows operating system. The main idea is to add a SafeSEH table to the import table of the application, which is used to store the address list of SEH processing functions generated by the compiler. During program execution, the Windows operating system checks whether the pointers in the program's SEH linked list exist in the SafeSEH table. If the pointer does not exist in the SafeSEH table, the Windows operating system will terminate the execution of the application. The SafeSEH mechanism can improve the security and reliability of the system, preventing malicious software from exploiting vulnerabilities such as buffer overflow and SEH injection to attack applications.

Search for the SafeSEH memory address, read the PE file into the memory, verify whether the SEH protection of the program is enabled, and if it is enabled, attempt to output the SEH memory address. In fact, modern code can be summarized as the following examples;

python
from x32dbg import Debugger

# Do you want to print the switch for SEH Handlers
LOG_HANDLERS = True

# Connect to the debugger and return an instance of the Debugger object
def connect_to_debugger(address, port):
    dbg = Debugger(address=address, port=port)
    if not dbg.connect():
        raise RuntimeError("Failed to connect to debugger")
    return dbg

# Get the base address of the PE file header
def get_pe_header_base_address(dbg):
    # Obtain the base address of the memory image
    memory_image_base = dbg.get_base_from_address(dbg.get_memory_localbase())

    # Obtain the offset of the PE header
    pe_offset = dbg.get_memory_dword(memory_image_base + 0x3c)

    # Return the base address of the PE file header
    return memory_image_base + pe_offset

# Check the SafeSEH protection status
def check_safeseh_protection(dbg, pe_base):
    # Obtain the value of SafeSEH related flag bits in the PE header
    flags = dbg.get_memory_word(pe_base + 0x5e)

    # Check if SafeSEH protection is enabled
    if flags & 0x400 != 0:
        print("SafeSEH | NoHandler")

# Analyze the SEH table
def analyze_seh_table(dbg, pe_base):
    # Obtain the number of entries in the SEH table
    number_of_entries = dbg.get_memory_dword(pe_base + 0x74)

    # If the number of entries in the SEH table is greater than 10
    if number_of_entries > 10:
        # Calculate the address of the SEH table and convert it to a virtual memory address
        section_address = dbg.get_memory_dword(pe_base + 0x78 + 8 * 10) + dbg.get_base_from_address(dbg.get_memory_localbase())

        # Get the size of the SEH table
        section_size = dbg.get_memory_dword(pe_base + 0x78 + 8 * 10 + 4)

        # Obtain data from the SEH table
        data = dbg.get_memory_dword(section_address)

        # Check if the SEH table is valid
        if section_size != 0 and (section_size == 0x40 or section_size == data):

            # Print SafeSEH protection information
            print("[+] SafeSEH Protection | Size: {}".format(section_size))

            # If the switch for printing SEH Handlers is enabled
            if LOG_HANDLERS:
                # Get the address of the SEH Handlers table
                seh_list_address = dbg.get_memory_dword(section_address + 0x40)
                # Get the size of the SEH Handlers table
                seh_list_size = dbg.get_memory_dword(section_address + 0x40 + 4)

                # If the SEH Handlers table address and size are non-zero
                if seh_list_address != 0 and seh_list_size != 0:
                    print("SEH Handlers:")
                    # Traverse each entry in the SEH Handlers table
                    for i in range(seh_list_size):
                        # Calculate the virtual address of the SEH handler
                        seh_address = dbg.get_memory_dword(seh_list_address + 4 * i) + dbg.get_base_from_address(dbg.get_memory_localbase())

                        # Print the address of the SEH handler
                        print("  SEHAddress = {}".format(hex(seh_address)))

                        # Obtain the type and detailed information of SEH Handler
                        handler_type = dbg.get_memory_dword(seh_address)
                        print("    Handler Type: {}".format(hex(handler_type)))
        else:
            # Print notification message that SafeSEH protection is not enabled
            print("[-] SafeSEH Not Protected")
            # If the data in the SEH table is less than 0x48
            if data < 0x48:
                # Print prompt information for unrecognized DLL/EXE programs
                print("[-] Unrecognized DLL/EXE Program")

if __name__ == "__main__":
    try:
        dbg = connect_to_debugger("127.0.0.1", 6589)
        # Get the base address of the PE file header
        pe_base = get_pe_header_base_address(dbg)
        # Check the SafeSEH protection status
        check_safeseh_protection(dbg, pe_base)
        # Analyze the SEH table
        analyze_seh_table(dbg, pe_base)
        dbg.close()
    except Exception as e:
        print(f"An error occurred: {str(e)}")

Running this code will output all memory address spaces within the current process that have SafeSEH enabled, as shown below;

python
[+] SafeSEH Protection | Size: 164
SEH Handlers:
  SEHAddress = 0x77832790
    Handler Type: 0x4244c8b
  SEHAddress = 0x77832820
    Handler Type: 0x300d8b64
  SEHAddress = 0x778349c0
    Handler Type: 0x4244c8b
  SEHAddress = 0x778386d0
    Handler Type: 0x8b55ff8b
  SEHAddress = 0x778391e0
    Handler Type: 0x4244c8b
  SEHAddress = 0x77845050
    Handler Type: 0x4244c8b