Appearance
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