def find_instruction_length(self,address,num_instructions): """ This is a helper function to assist in determining the size of a set of instructions. It's really just a fancy wrapper for the disasmForward* family of functions, but it aims to prevent writing these while loops repeatedly in your scripts. @type address: DWORD @param address: The address of where to start the size calculation. @type num_instructions: Integer @param num_instructions: This is the number of instructions from the starting point you wish to include. @raise mfx: An exception is raised if this function fails. @rtype: Integer @return: Returns the size of the instructions as an integer. """ # BoB: Get the size of the first 2 instructions count = 1 instruction_size = 0 # You're being silly if you are using this function to a length # calculation. Use imm.disasm(address).opsize to get a length instead if num_instructions <= 1: raise mfx("[*] You silly goose, use imm.disasm(address).opsize for a single instruction length.") while count <= num_instructions: try: instruction_size += self.imm.disasmForwardSizeOnly(address,nlines=count-1).opsize except TypeError: raise mfx("[*] Obtaining instruction size failed. Please make sure this is a sane address.") count += 1 return instruction_size
def check_remote_debugger_present(self): """ Patches the instructions in the beginning of the CheckRemoteDebuggerPresent() function call. @rtype: Boolean @return: True if the patch to CheckRemoteDebuggerPresent() succeeds. """ func_address = self.imm.getAddress("kernel32.CheckRemoteDebuggerPresent") if func_address <= 0: self.imm.Log("[*] No CheckRemoteDebuggerPresent() function.") self.imm.Log("[*] Patching CheckRemoteDebuggerPresent.", address=func_address) # Patch instructions in to bypass the call patch_code = self.imm.Assemble( " \ Mov EDI, EDI \n \ Push EBP \n \ Mov EBP, ESP \n \ Mov EAX, [EBP + C] \n \ Push 0 \n \ Pop [EAX] \n \ Xor EAX, EAX \n \ Pop EBP \n \ Ret 8 \ " ) bytes_written = self.imm.writeMemory(func_address, patch_code) if bytes_written == 0: raise mfx("[*] Could not patch CheckRemoteDebuggerPresent()") return True
def zw_query_information_process(self): """ This patches ntdll.ZwQueryInformationProcess to hide that there is a debugger present. This essentially patches the ProcessDebugPort in the ProcessInformationClass parameter. @rtype: Boolean @return: Returns True if the patch was successful, False if it failed. """ function_address = self.imm.getAddress("ntdll.ZwQueryInformationProcess") if function_address <= 0: self.imm.Log("[*] No ZwQueryInformationProcess to patch.") return True self.imm.Log("[*] Patching ntdll.ZwQueryInformationProcess.") is_patched = False instruction_length = self.patch_utils.find_instruction_length(function_address, 2) # Now let's test to see if the function has been patched already fake_code = ( self.imm.readMemory(function_address, 1) + self.imm.Assemble("DD 0x12345678") + self.imm.readMemory(function_address + 5, 1) ) if fake_code == self.imm.Assemble("Push 0x12345678\n Ret"): # It's been patched already is_patched = True # Find the address where the patch points to address = self.imm.readLong(function_address + 1) # Find the length of the two instructions instruction_length = self.patch_utils.find_instruction_length(address, 2) # If the function hasn't been patched already then # allocate a page for our detour code and write the # first two original instructions if is_patched == False: # Allocate our detour page stub_address = self.imm.remoteVirtualAlloc() # Write the instructions bytes_written = self.imm.writeMemory( stub_address, self.imm.readMemory(function_address, instruction_length) ) if bytes_written < instruction_length: raise mfx("[*] ZwQueryInformation Patch failed - couldn't write to memory page.") patch_body = ( " \ CMP DWORD [ESP+8], 7 \n \ DB 0x74, 0x06 \n \ \n \ PUSH 0x%08x \n \ RET \n \ \n \ MOV EAX, DWORD [ESP+0x0C] \n \ PUSH 0 \n \ POP [EAX] \n \ XOR EAX, EAX \n \ RET 14" % (function_address + instruction_length) ) patch_body = self.imm.Assemble(patch_body) # Write the patch code after the first two original instructions bytes_written = self.imm.writeMemory(stub_address + instruction_length, patch_body) if bytes_written < len(patch_body): raise mfx("[*] ZwQueryInformationProcess - couldn't write primary patch code to memory.") # If it hasn't been patched already then write the detour # jmp as a PUSH/RET combo if is_patched == False: detour_jmp = self.imm.Assemble("PUSH 0x%08x\n RET" % stub_address) bytes_written = self.imm.writeMemory(function_address, detour_jmp) if bytes_written < len(detour_jmp): raise mfx( "[*] ZwQueryInformationProcess - couldn't write the detour combo at original function address." ) return True
def patch_peb(self, peb_flag=None): """ Various patches for the PEB. Use the Flag variable to select particular fields in the PEB that you wish to patch. @type peb_flag: String @param peb_flag: (Optional) Specific flag you wish to patch. Values can be one of (case IN-sensitive):: BeingDebugged ProcessHeap NtGlobalFlag LDR_DATA @rtype: Boolean @return: Returns True if the patch was successful. """ try: peb_address = self.imm.getPEBaddress() peb = self.imm.getPEB() except: raise mfx("[*] Could not obtain PEB address.") # Patch the IsDebugged member if peb_flag is None or peb_flag.lower() == "beingdebugged": offset = peb_address + 0x02 self.imm.Log("[*] Patching PEB.BeingDebugged", address=offset) # Zero out the flag, BoB's original patch assembled a DB 0 into that position self.imm.writeMemory(offset, "\x00") # Patch the ProcessHeap member if peb_flag is None or peb_flag.lower() == "processheap": offset = self.imm.readLong(peb_address + 0x18) + 0x10 self.imm.Log("[*] Patching PEB.ProcessHeap", address=offset) self.imm.writeMemory(offset, DWORD_ZERO) # Patch the NtGlobalFlag member if peb_flag is None or peb_flag.lower() == "ntglobalflag": offset = peb_address + 0x68 self.imm.Log("[*] Patching PEB.NtGlobalFlag", address=offset) self.imm.writeMemory(offset, DWORD_ZERO) # JMS: Patch the PEB_LDR_DATA struct by replaciong 0xFEEEFEEE # with zeros if peb_flag is None or peb_flag.lower() == "ldr_data": # Grab the memory page where the LDR_DATA struct resides # dump it to dirty_memory, and then replace all 0xFEEEFEEE page = self.imm.getMemoryPagebyAddress(peb.Ldr[0]) dirty_memory = page.getMemory() clean_memory = dirty_memory.replace("\xEE\xFE\xEE\xFE", "\x00\x00\x00\x00") bytes_written = self.imm.writeMemory(page.getBaseAddress(), clean_memory) if bytes_written == 0: raise mfx("[*] Could not write the memory page to patch PEB.LDR_DATA.") return True