Friday, May 16, 2014

ZwProtectVirtualMemory Hook



“ZwProtectVirtualMemory” is an undocumented API that can be used to modify the memory protections schemes. This API is seen to be widely used by exploits to modify the protection schemes of memory where the shellcode resides. 

For experimenting purpose I wrote a Python script that looks hooks into "ZwProtectVirtualMemory" and verifies if the stack is pointing to addresses > “09090909h” and the memory protection being modified to “RWX” – that is “40h”. If the condition is satisfied it prints the 10 DWORDs of memory content of ESP along with the hex dump of possible shellcode. Useful in cases for speedy analysis where “ZwProtectVirtualMemory” API in used to modify the memory where the shellcode resides.

Script tested on IE11 - Windows 7 32 bit.

##
#ZwPVM_Hook.py
#Code checks for possible shellcode
#after ZwProtectVirtual function is executed
#
#Based on analysis of CVE-2014-0322 & CVE-2014-1776
#
#Author:binaryhax0r
##

from pydbg import *
from pydbg.defines import *

import struct
import utils
import sys
import os

from ctypes import *

global is_hooked

def ZwProtect(dbg, args):
    esp_content = dbg.context.Esp

    if (esp_content >= 0x09090909):    #Arbitrary address!!!
        access_protection_addr = esp_content + 0x10    #Protection Address
        access_protection = dbg.read_process_memory(access_protection_addr, 4)
        access_protection = struct.unpack("L", access_protection)[0]

        if (access_protection == 0x40):    #0x40 - RWX protection
            print "=" * 50
            print "[*] ESP CONTENT: 0x%08x" % esp_content
            print "[*] ACCESS PROTECTION: 0x%08x" % access_protection
            print "[*] ESP ADDRESS DUMP:"
            print "*" * 50
            temp = esp_content

            for x in range(0, 10):        #no of stack variables to print - 10
                esp_addr_dump = dbg.read_process_memory(temp, 4)
                esp_addr_dump = struct.unpack("L", esp_addr_dump)[0]
                temp += 4
                print hex(esp_addr_dump)

            print "*" * 50
            shellcode = ""
            temp = int(esp_content)
            temp = dbg.read_process_memory(temp, 4)    #top of stack possibly point to shellcode location
            temp = struct.unpack("L", temp)[0]
            print "[*] POSSIBLE SHELLCODE ADDRESS: 0x%08x" % temp
            shellcode = dbg.hex_dump(dbg.read(temp, 0x1000), temp)
            print shellcode
            print "=" * 50
    return DBG_CONTINUE

def hook_ZwProtect (pydbg):
    is_hooked = 0
    hooks = utils.hook_container()
    hook_address  = pydbg.func_resolve_debuggee("ntdll.dll", "ZwProtectVirtualMemory")    #API to hook
    hook_address += 12
    if (is_hooked != 1):
        if hook_address:
            res = hooks.add(pydbg, hook_address, 0, ZwProtect, None)
            is_hooked = 1
            print "[*] ZwProtectVirtualMemory hooked at: 0x%08x" % hook_address
        else:
            print "[*] Error: Couldn't resolve hook address."
    return DBG_CONTINUE

dbg = pydbg()
dbg.set_callback(EXCEPTION_BREAKPOINT, hook_ZwProtect)
found_program = False
for (pid, name) in dbg.enumerate_processes():
    if name.lower() == "iexplore.exe":
        found_program = True
        dbg.attach(pid)
if found_program: 
    dbg.run()
else:
    dbg.detach()


 Sample output during an exploit analysis.


No comments:

Post a Comment