843 lines
31 KiB
Python
Executable File
843 lines
31 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
#-------------------------------------------------------------------------------
|
|
#
|
|
# By BoB -> Team PEiD
|
|
# http://www.PEiD.info/BobSoft/
|
|
# BobSoft@GMail.Com
|
|
#
|
|
#-------------------------------------------------------------------------------
|
|
|
|
import immlib
|
|
import getopt
|
|
import random
|
|
import ctypes
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
__VERSION__ = '1.00'
|
|
ProgName = 'HideDebug'
|
|
ProgVers = __VERSION__
|
|
DESC = "Patches lots of anti-debug protection .. (try \"!usage %s\" for details)" % ProgName.lower()
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
Docs = """
|
|
|
|
Loosely based on patch.py (c) Immunity inc .. :)
|
|
|
|
Patches:
|
|
o IsDebuggerPresent (With Poly-patch code, as too easy to detect Xor EAX, EAX)
|
|
o ZwQueryInformationProcess
|
|
o CheckRemoteDebuggerPresent
|
|
o PEB.IsDebugged
|
|
o PEB.ProcessHeap.Flag
|
|
o PEB.NtGlobalFlag
|
|
o PEB.Ldr 0xFEEEFEEE filling
|
|
o GetTickCount (With poly-patch code, as too easy to detect Mov EAX, xxxxxxxx)
|
|
o ZwQuerySystemInformation (Used by CreateToolHelp32Snapshot / Process32First / Process32Next and others)
|
|
o FindWindowA
|
|
o FindWindowW
|
|
o FindWindowExA
|
|
o FindWindowExW
|
|
o EnumWindows
|
|
|
|
|
|
Types:
|
|
o Anti-Debug Types:
|
|
IsDebuggerPresent
|
|
ZwQueryInformationProcess
|
|
CheckRemoteDebuggerPresent
|
|
PEB (All PEB patches are done)
|
|
GetTickCount
|
|
All_Debug - Applies ALL Debug detect patches ..
|
|
|
|
o Anti-Process-finding Types:
|
|
ZwQuerySystemInformation (All other process apis use this)
|
|
All_Process - Applies the debugger-process finding Api patch ..
|
|
|
|
o Anti-Window-finding Types:
|
|
FindWindowA
|
|
FindWindowW
|
|
FindWindowExA
|
|
FindWindowExW
|
|
EnumWindows
|
|
All_Window - Applies ALL debugger-window finding Api patches ..
|
|
|
|
|
|
<dodgy excuse>
|
|
Sorry for any weird code, I've only been using Python for 2 weeks .. :)
|
|
</dodgy excuse>
|
|
|
|
|
|
Description:
|
|
Most of the functions are patched to return Debugger Found = False ..
|
|
The PEB patches are to the various flags in PEB used by anti-debug ..
|
|
Patch for ZwQueryInformationProcess is if DebugPort is checked, returns not debugged ..
|
|
Patch for GetTickCount is to return same number everytime ..
|
|
Patch for ZwQuerySystemInformation is to replace all ImmunityDebugger.exe with SVCHost.EXE ..
|
|
Patch for Window finding apis call Api and if "ID" is classname then return not found ..
|
|
|
|
|
|
Maybe ToDo:
|
|
o Patch CreateThread ?
|
|
|
|
"""
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Show usage ..
|
|
|
|
def usage(imm):
|
|
imm.Log(" ")
|
|
imm.Log("%s v%s By BoB -> Team PEiD" % (ProgName, ProgVers),focus=1, highlight=1)
|
|
imm.Log("Description:")
|
|
imm.Log(" Patches many different flags and apis used to detect debuggers ..")
|
|
imm.Log(" Different combinations of patches will defeat most protections, ")
|
|
imm.Log(" and some common anti-debug apis are patched with poly code ")
|
|
imm.Log(" to avoid detection by packers like RL!Pack .. ")
|
|
imm.Log(" All apis return usual valid data, the patches do not affect normal use .. ")
|
|
imm.Log(" EG: FindWindowA('NotePad.EXE', Null) will work same if patched or not..")
|
|
imm.Log(" ")
|
|
imm.Log("Usage:")
|
|
imm.Log(" !%s <Type>" % ProgName.lower())
|
|
imm.Log(" ")
|
|
imm.Log("Type can be ..")
|
|
imm.Log(" Debugger-Detect Types:")
|
|
imm.Log(" . IsDebuggerPresent - Patches the Kernel32 Api to return false ..")
|
|
imm.Log(" . CheckRemoteDebuggerPresent - Patches the Kernel32 Api ..")
|
|
imm.Log(" . ZwQueryInformationProcess - Patches the NtDll Api only for getting DebugPort ..")
|
|
imm.Log(" . GetTickCount - Patches the Kernel32 Api to always return same value ..")
|
|
imm.Log(" . Peb - Patches PEB.IsDebugged, PEB.ProcessHeap.Flag, PEB.NtGlobalFlag and fill bytes ..")
|
|
imm.Log(" . All_Debug - Applies patches for all of the above .. ")
|
|
imm.Log(" ")
|
|
imm.Log(" Debugger-Detect by Process Types: ")
|
|
imm.Log(" . ZwQuerySystemInformation - Patches the NtDll Api to remove ImmDbg from list ..")
|
|
imm.Log(" . All_Process - Applies all process patches above .. ")
|
|
imm.Log(" ")
|
|
imm.Log(" Debugger-Detect by Window Types: (User32.DLL must be loaded)")
|
|
imm.Log(" . FindWindowA - Reports false if process looks for ImmDbg win classname ..")
|
|
imm.Log(" . FindWindowW - Reports false if process looks for ImmDbg win classname ..")
|
|
imm.Log(" . FindWindowExA - Reports false if process looks for ImmDbg win classname ..")
|
|
imm.Log(" . FindWindowExW - Reports false if process looks for ImmDbg win classname ..")
|
|
imm.Log(" . EnumWindows - Own callback function calls user callback if not ImmDbg HWnd ..")
|
|
imm.Log(" . All_Window - Applies all window patches above .. ")
|
|
imm.Log(" ")
|
|
return "See log window (Alt-L) for usage .. "
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Misc functions ..
|
|
#-------------------------------------------------------------------------------
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Write Poly instructions to patch an EAX = Dword-Value instruction onto an Api ..
|
|
|
|
def Poly_ReturnDW(imm, Value):
|
|
I = random.randint(1, 3)
|
|
if I == 1:
|
|
if random.randint(1, 2) == 1:
|
|
# 7 bytes ..
|
|
return imm.Assemble( "Sub EAX, EAX\n Add EAX, 0x%08x" % Value )
|
|
else:
|
|
# 7 bytes ..
|
|
return imm.Assemble( "Sub EAX, EAX\n Sub EAX, -0x%08x" % Value )
|
|
if I == 2:
|
|
# 6 bytes
|
|
return imm.Assemble( "Push 0x%08x\n Pop EAX\n" % Value )
|
|
if I == 3:
|
|
if random.randint(1, 2) == 1:
|
|
# 7 bytes with optimized instruction ..
|
|
return imm.Assemble( "XChg EAX, EDI\n DB 0xBF\n DD 0x%08x\n XChg EAX, EDI" % Value )
|
|
else:
|
|
# 8 bytes cos not optimized ..
|
|
return imm.Assemble( "XChg EAX, EDI\n Mov EDI, 0x%08x\n XChg EAX, EDI" % Value )
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Write Poly instructions to patch a simple EAX = 0 onto an Api ..
|
|
|
|
def Poly_Return0(imm):
|
|
I = random.randint(1, 4)
|
|
if I == 1:
|
|
# 2 bytes
|
|
return imm.Assemble( "Sub EAX, EAX" )
|
|
if I == 2:
|
|
if random.randint(1, 2) == 1:
|
|
# 6 bytes
|
|
return imm.Assemble( "Push 0\n Pop EAX" )
|
|
else:
|
|
# 3 bytes
|
|
return imm.Assemble( "DB 0x6A, 0x00\n Pop EAX" )
|
|
if I == 3:
|
|
# 4 bytes
|
|
return imm.Assemble( "XChg EAX, EDI\n Sub EDI, EDI\n XChg EAX, EDI" )
|
|
if I == 4:
|
|
return Poly_ReturnDW(imm, 0)
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Debug Detection Patches ..
|
|
#-------------------------------------------------------------------------------
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Clear various debug flags in PEB ..
|
|
|
|
def Patch_PEB(imm):
|
|
PEB = imm.getPEBaddress()
|
|
# Just incase .. ;)
|
|
if PEB == 0:
|
|
imm.Log( "No PEB to patch .. !?" )
|
|
return
|
|
|
|
imm.Log( "Patching PEB.IsDebugged ..", address = PEB + 0x02 )
|
|
imm.writeMemory(PEB + 0x02, imm.Assemble( "db 0" ) )
|
|
|
|
a = imm.readLong(PEB + 0x18)
|
|
a += 0x10
|
|
imm.Log( "Patching PEB.ProcessHeap.Flag ..", address = a )
|
|
imm.writeLong( a, 0 )
|
|
|
|
imm.Log( "Patching PEB.NtGlobalFlag ..", address = PEB + 0x68 )
|
|
imm.writeLong(PEB + 0x68, 0)
|
|
|
|
# Patch PEB_LDR_DATA 0xFEEEFEEE fill bytes .. (about 3000 of them ..)
|
|
a = imm.readLong(PEB + 0x0C)
|
|
imm.Log("Patching PEB.LDR_DATA filling ..", address = a)
|
|
while a != 0:
|
|
a += 1
|
|
try:
|
|
b = imm.readLong(a)
|
|
c = imm.readLong(a + 4)
|
|
# Only patch the filling runs ..
|
|
if (b == 0xFEEEFEEE) and (c == 0xFEEEFEEE):
|
|
imm.writeLong(a, 0)
|
|
imm.writeLong(a + 4, 0)
|
|
a += 7
|
|
except:
|
|
break
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# IsDebuggerPresent ..
|
|
# Note: This Api checks a value in PEB, so if patching PEB then no need to patch Api ..
|
|
|
|
def Patch_IsDebuggerPresent(imm):
|
|
ispresent = imm.getAddress( "kernel32.IsDebuggerPresent" )
|
|
# Just incase .. ;)
|
|
if (ispresent <= 0):
|
|
imm.Log( "No IsDebuggerPresent to patch .." )
|
|
return
|
|
|
|
imm.Log( "Patching IsDebuggerPresent...", address = ispresent )
|
|
Code = imm.Assemble("DB 0x64\n Mov EAX, DWORD PTR DS:[18]") + Poly_Return0(imm) + imm.Assemble( "ret" )
|
|
# Careful for Win2k ..
|
|
while len(Code) > 0x0E:
|
|
Code = imm.Assemble("DB 0x64\n Mov EAX, DWORD PTR DS:[18]") + Poly_Return0(imm) + imm.Assemble( "ret" )
|
|
imm.writeMemory( ispresent, Code )
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# CheckRemoteDebuggerPresent ..
|
|
# Note: This Api calls ZwQueryInformationProcess Api, so usually no need to patch both ..
|
|
|
|
def Patch_CheckRemoteDebuggerPresent(imm):
|
|
deb = imm.getAddress( "kernel32.CheckRemoteDebuggerPresent" )
|
|
# Just incase on Win2k .. ;)
|
|
if (deb <= 0):
|
|
imm.Log( "No CheckRemoteDebuggerPresent to patch .." )
|
|
return
|
|
|
|
imm.Log( "Patching CheckRemoteDebuggerPresent ..", address = deb )
|
|
imm.writeMemory( deb, 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 \
|
|
" ) )
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# ZwQueryInformationProcess ..
|
|
|
|
def Patch_ZwQueryInformationProcess(imm):
|
|
qip = imm.getAddress( "ntdll.ZwQueryInformationProcess" )
|
|
# Just incase .. ;)
|
|
if (qip <= 0):
|
|
imm.Log( "No ZwQueryInformationProcess to patch .." )
|
|
return
|
|
|
|
imm.Log( "Patching ZwQueryInformationProcess ..", address = qip )
|
|
IsPatched = False
|
|
a = 0
|
|
s = 0
|
|
# Scan Api and get size of first 2 instructions ..
|
|
# On Win2k SysCall starts with Mov EAX, xxxxxxxx\n Lea EDX, [ESP + 4] ..
|
|
# On WinXP, Win2k3 + Vista, SysCall always starts with Mov EAX, xxxxxxxx\n MOV EDX, 0x7FFE0300 ..
|
|
while a < 2:
|
|
a += 1
|
|
s += imm.disasmSizeOnly(qip + s).opsize
|
|
|
|
# Check if already patched ..
|
|
FakeCode = imm.readMemory(qip, 1) + imm.Assemble("DD 0x12345678") + imm.readMemory(qip + 5, 1)
|
|
if FakeCode == imm.Assemble( "Push 0x12345678\n Ret"):
|
|
# Definately found a push jump ..
|
|
IsPatched = True
|
|
# Get address of where it points to ..
|
|
a = imm.readLong(qip + 1)
|
|
# Get length of the 2 instructions before patch code ..
|
|
i = 0
|
|
s = 0
|
|
while i < 2:
|
|
i += 1
|
|
s += imm.disasmSizeOnly(a + s).opsize
|
|
|
|
# If not patched already, allocate some memory for patch code ..
|
|
if IsPatched == False:
|
|
# Allocate memory for hook code ..
|
|
a = imm.remoteVirtualAlloc(size=0x1000)
|
|
# Write 2 instructions from api to allocated mem ..
|
|
imm.writeMemory( a, imm.readMemory(qip, s) )
|
|
|
|
# If ProcessInformationClass = ProcessDebugPort then return 0 in
|
|
# ProcessInformation; else call ZwQueryInformationProcess as normal ..
|
|
PatchCode = " \
|
|
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 \n \
|
|
" % (qip + s)
|
|
|
|
# Write patch code in allocated memory after the original first 2 instructions ..
|
|
imm.writeMemory( a + s, imm.Assemble( PatchCode ) )
|
|
|
|
# If not patched, write Push Jmp to redirect Api to my code ..
|
|
if IsPatched == False:
|
|
imm.writeMemory( qip, imm.Assemble( "Push 0x%08X\n Ret" % a) )
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# GetTickCount ..
|
|
# Poly return cos it's an obvious one for a packer to check for Mov EAX, xxxxxxxx or Xor EAX, EAX ..
|
|
|
|
def Patch_GetTickCount(imm):
|
|
a = imm.getAddress("kernel32.GetTickCount")
|
|
# Just incase .. ;)
|
|
if (a <= 0):
|
|
imm.Log( "No GetTickCount to patch .." )
|
|
return
|
|
|
|
imm.Log("Patching GetTickCount ..", address = a)
|
|
|
|
# Keep first instruction to avoid checks ..
|
|
Code = imm.Assemble("Mov EDX, 0x7FFE0000") + Poly_ReturnDW(imm, 0xB0B1560D) + imm.Assemble("Ret")
|
|
# Careful of Win2k's lack of alignment ..
|
|
while len(Code) > 0x0F:
|
|
Code = imm.Assemble("Mov EDX, 0x7FFE0000") + Poly_ReturnDW(imm, 0xB0B1560D) + imm.Assemble("Ret")
|
|
|
|
imm.writeMemory( a, Code )
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# ImmunityDbg.Exe Process detection Patches ..
|
|
#-------------------------------------------------------------------------------
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# ZwQuerySystemInformation ..
|
|
# If called with size < needed size then just returns size ..
|
|
# If called with size >= needed size then fills buffer with list of all processes and lots of info about them ..
|
|
|
|
def Patch_ZwQuerySystemInformation(imm):
|
|
qsi = imm.getAddress( "ntdll.ZwQuerySystemInformation" )
|
|
# Just incase .. ;)
|
|
if (qsi <= 0):
|
|
imm.Log( "No ZwQuerySystemInformation to patch .." )
|
|
return
|
|
|
|
imm.Log("Patching ZwQuerySystemInformation ..", address = qsi)
|
|
IsPatched = False
|
|
a = 0
|
|
s = 0
|
|
# Scan Api and get size of first 3 instructions ..
|
|
# On Win2k thats: Mov EAX, xxxxxxxx\n Lea EDX, [ESP + 4]\n Int 0x2E ..
|
|
# On WinXP, Win2k3 + Vista thats: Mov EAX, xxxxxxxx\n MOV EDX, 0x7FFE0300\n Call [EDX] ..
|
|
# So patch code will call SysCall before doing anything else ..
|
|
while a < 3:
|
|
a += 1
|
|
s += imm.disasmSizeOnly(qsi + s).opsize
|
|
|
|
# Check if already patched ..
|
|
FakeCode = imm.readMemory(qsi, 1) + imm.Assemble("DD 0x12345678") + imm.readMemory(qsi + 5, 1)
|
|
if FakeCode == imm.Assemble( "Push 0x12345678\n Ret"):
|
|
# Definately found a push jump ..
|
|
IsPatched = True
|
|
# Get address of where it points to ..
|
|
a = imm.readLong(qsi + 1)
|
|
# Get length of the 3 instructions before patch code ..
|
|
i = 0
|
|
s = 0
|
|
while i < 3:
|
|
i += 1
|
|
s += imm.disasmSizeOnly(a + s).opsize
|
|
|
|
# If not patched already, allocate some memory for patch code ..
|
|
if IsPatched == False:
|
|
# Allocate memory for hook code ..
|
|
a = imm.remoteVirtualAlloc(size=0x1000)
|
|
# Write 3 instructions from api to allocated mem ..
|
|
imm.writeMemory( a, imm.readMemory(qsi, s) )
|
|
|
|
# If SystemInformationClass == SystemProcessesAndThreadsInformation then
|
|
# replace ImmunityDebugger.Exe with SVCHOST.EXE in returned process list .. :)
|
|
# There are no labels, so all jmps, calls etc are written as bytes ..
|
|
# Also, due to some weird bug LodsW assembles as LodsD so I put
|
|
# "DB 0x66\n LodsD" to force LodsW, and same for MovsW .. (should work after bug fix)
|
|
|
|
PatchCode = " \
|
|
\n\
|
|
Cmp EAX, 0 \n\
|
|
DB 0x74, 0x03 \n\
|
|
Ret 0x10 \n\
|
|
\n\
|
|
PushAD \n\
|
|
Mov EAX, [ESP + 0x24] \n\
|
|
Lea EBX, [ESP + 0x28] \n\
|
|
Mov ECX, [ESP + 0x2C] \n\
|
|
\n\
|
|
DB 0xE8 \n\
|
|
DD 0x2C \n\
|
|
DW 'I', 'M', 'M', 'U' \n\
|
|
DW 'N', 'I', 'T', 'Y' \n\
|
|
DW 'D', 'E', 'B', 'U' \n\
|
|
DW 'G', 'G', 'E', 'R' \n\
|
|
DW '.', 'E', 'X', 'E' \n\
|
|
DW 0x00,0x00 \n\
|
|
\n\
|
|
Pop EDI \n\
|
|
Cmp EAX, 5 \n\
|
|
DB 0x74, 0x04 \n\
|
|
PopAD \n\
|
|
Ret 0x10 \n\
|
|
\n\
|
|
Cmp ECX, 0 \n\
|
|
DB 0x74, 0xF4 \n\
|
|
Cmp EBX, 0 \n\
|
|
DB 0x74, 0xEC \n\
|
|
\n\
|
|
Mov EBX, [EBX] \n\
|
|
PushAD \n\
|
|
Xor EAX, EAX \n\
|
|
Mov ESI, [EBX + 0x3C] \n\
|
|
Cmp ESI, 0 \n\
|
|
DB 0x74, 0x0A \n\
|
|
DB 0x66 \n\
|
|
LodsD \n\
|
|
Cmp EAX, 0 \n\
|
|
DB 0x75, 0x0C \n\
|
|
\n\
|
|
Pop EDI \n\
|
|
Push EDI \n\
|
|
DB 0x8B, 0x03 \n\
|
|
Or EAX, EAX \n\
|
|
DB 0x74, 0x6F \n\
|
|
Add EBX, EAX \n\
|
|
DB 0xEB, 0xDA \n\
|
|
\n\
|
|
Cmp AL, 0x61 \n\
|
|
DB 0x7C, 0x03 \n\
|
|
Sub AL, 0x20 \n\
|
|
Cmp [EDI], AL \n\
|
|
DB 0x75, 0xE8 \n\
|
|
Inc EDI \n\
|
|
Inc EDI \n\
|
|
Cmp DWORD [EDI], 0 \n\
|
|
DB 0x75, 0xD4 \n\
|
|
\n\
|
|
Sub ESI, 0x28 \n\
|
|
\n\
|
|
DB 0xE8 \n\
|
|
DD 0x28 \n\
|
|
DW 'S', 'V', 'C', 'H' \n\
|
|
DW 'O', 'S', 'T', '.' \n\
|
|
DW 'E', 'X', 'E', 0x00 \n\
|
|
DD 0x00,0x00,0x00,0x00 \n\
|
|
\n\
|
|
XChg ESI, EDI \n\
|
|
Pop ESI \n\
|
|
Mov ECX, 0x14 \n\
|
|
DB 0x66 \n\
|
|
Rep MovsD \n\
|
|
\n\
|
|
Mov DWord [EBX + 0x40], 2 \n\
|
|
Mov DWord [EBX + 0x44], 0 \n\
|
|
DB 0xEB, 0x89 \n\
|
|
\n\
|
|
PopAD \n\
|
|
PopAD \n\
|
|
Ret 0x10 \n\
|
|
\
|
|
"
|
|
|
|
# Write patch code in allocated memory after the original first 3 instructions ..
|
|
imm.writeMemory( a + s, imm.Assemble( PatchCode ) )
|
|
|
|
# If not patched, write Push Jmp to redirect Api to my code ..
|
|
if IsPatched == False:
|
|
imm.writeMemory( qsi, imm.Assemble( "Push 0x%08X\n Ret" % a) )
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Window Detection Patches ..
|
|
#-------------------------------------------------------------------------------
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Patch for FindWindowA, FindWindowW, FindWindowExA, FindWindowExW ..
|
|
|
|
def Patch_FindWindow(imm, ex = False, suffix = "A"):
|
|
suffix = suffix.upper()
|
|
|
|
RetVal = 0x08
|
|
if ex:
|
|
suffix = "Ex" + suffix
|
|
RetVal = 0x10
|
|
|
|
FW = imm.getAddress("user32.FindWindow%s" % suffix)
|
|
# Just incase .. ;)
|
|
if (FW <= 0):
|
|
imm.Log("No FindWindow%s to patch .. (Is User32 Loaded?)" % suffix)
|
|
return False
|
|
|
|
# Find place for jmp in Api ..
|
|
p = 0
|
|
d = imm.disasm(FW)
|
|
l = d
|
|
dis = ""
|
|
FoundCall = False
|
|
while p < 100:
|
|
if d.getDisasm() == "POP EBP":
|
|
dis = l.getDisasm()
|
|
p -= l.getSize()
|
|
if l.isCall():
|
|
FoundCall = True
|
|
break
|
|
# Try to continue without expected call instrucion ..
|
|
dis = l.getDisasm()
|
|
break
|
|
# Did we already patch this api ?
|
|
if d.getDisasm() == "RETN":
|
|
if l.isPush():
|
|
imm.log("FindWindow%s already patched .." % suffix, address = FW)
|
|
return False
|
|
p += d.getSize()
|
|
l = d
|
|
d = imm.disasm(FW + p)
|
|
|
|
imm.Log("Patching FindWindow%s .." % suffix, address = FW)
|
|
HookMem = imm.remoteVirtualAlloc(size=0x1000)
|
|
HookCode = imm.Assemble("Push 0x%08X\n Ret" % HookMem)
|
|
|
|
if FoundCall == True:
|
|
# Get address pointed to by call instruction ..
|
|
a = l.getJmpAddr()
|
|
# Fix Call instruction in patch function to point to original call address ..
|
|
a = ((a - HookMem) - 5)
|
|
dis = "DB 0xE8\n DD 0x%08X" % a
|
|
|
|
# Get HWnd of ImmDbg .. If this is exposed by ImmLib, I didn't find it.. :)
|
|
ImmHWnd = ctypes.windll.LoadLibrary("User32.DLL").FindWindowA("ID", 0)
|
|
|
|
# Code calls Api, if HWnd matches ImmDbg return 0 ..
|
|
# Else all works as before ..
|
|
# Again, all jumps are as bytes cos no labels ..
|
|
|
|
PatchCode = " \
|
|
%s \n\
|
|
Cmp EAX, 0x%08X \n\
|
|
DB 0x74, 0x02 \n\
|
|
DB 0xEB, 0x02 \n\
|
|
Xor EAX, EAX \n\
|
|
Pop EBP \n\
|
|
Ret 0x%02X \n\
|
|
" % (dis, ImmHWnd, RetVal)
|
|
|
|
imm.writeMemory(HookMem, imm.Assemble(PatchCode))
|
|
imm.writeMemory(FW + p, HookCode)
|
|
return True
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
def Patch_EnumWindows(imm):
|
|
EW = imm.getAddress("user32.EnumWindows")
|
|
# Just incase .. ;)
|
|
if (EW <= 0):
|
|
imm.Log("No EnumWindows to patch .. (Is User32 Loaded?)")
|
|
return False
|
|
|
|
# Find place for jmp in Api ..
|
|
p = 0
|
|
d = imm.disasm(EW)
|
|
l = d
|
|
dis = ""
|
|
FoundCall = False
|
|
while p < 100:
|
|
if d.getDisasm() == "POP EBP":
|
|
dis = l.getDisasm()
|
|
p -= l.getSize()
|
|
if l.isCall():
|
|
FoundCall = True
|
|
break
|
|
# Try to continue without expected call instrucion ..
|
|
dis = l.getDisasm()
|
|
break
|
|
# Did we already patch this api ?
|
|
if d.getDisasm() == "RETN":
|
|
if l.isPush():
|
|
imm.log("EnumWindows already patched ..", address = EW)
|
|
return False
|
|
p += d.getSize()
|
|
l = d
|
|
d = imm.disasm(EW + p)
|
|
|
|
imm.Log("Patching EnumWindows ..", address = EW)
|
|
HookMem = imm.remoteVirtualAlloc(size=0x1000)
|
|
HookCode = imm.Assemble("Push 0x%08X\n Ret" % HookMem)
|
|
|
|
if FoundCall == True:
|
|
# Get address pointed to by call instruction ..
|
|
a = l.getJmpAddr()
|
|
# Fix Call instruction in patch function to point to original call address ..
|
|
a = ((a - (HookMem + 0x5B)) - 5) # 0x5B = offset of call instruction in patch code ..
|
|
dis = "DB 0xE8\n DD 0x%08X" % a
|
|
|
|
# Get HWnd of ImmDbg ..
|
|
ImmHWnd = ctypes.windll.LoadLibrary("User32.DLL").FindWindowA("ID", 0)
|
|
|
|
# Code calls Api, using own callback function ..
|
|
# My callback calls user's callback function (if hwnd not ImmDbg) ..
|
|
# Else all works as before ..
|
|
PatchCode = " \
|
|
DB 0xEB,0x31 \n\
|
|
\n\
|
|
Sub EAX, EAX \n\
|
|
Inc EAX \n\
|
|
PushAD \n\
|
|
DB 0x81,0x7C,0x24,0x24 \n\
|
|
DD 0x%08X \n\
|
|
DB 0x74,0x1B \n\
|
|
Push [ESP + 0x28] \n\
|
|
Push [ESP + 0x28] \n\
|
|
Call [0x0000002F] \n\
|
|
Mov [ESP + 0x1C], EAX \n\
|
|
PopAD \n\
|
|
Ret 8 \n\
|
|
\n\
|
|
DD 0xB0b1560d \n\
|
|
\n\
|
|
DB 0xE8 \n\
|
|
DD 0x00000000 \n\
|
|
Pop EAX \n\
|
|
Sub EAX, 0x38 \n\
|
|
Add [EAX + 0x20], EAX \n\
|
|
Push [EBP + 0x08] \n\
|
|
Pop [EAX + 0x2F] \n\
|
|
\n\
|
|
Inc EAX \n\
|
|
Inc EAX \n\
|
|
Push EAX \n\
|
|
Pop [ESP + 0x08] \n\
|
|
%s \n\
|
|
Pop EBP \n\
|
|
Ret 8 \n\
|
|
" % (ImmHWnd, dis)
|
|
|
|
imm.writeMemory(HookMem, imm.Assemble(PatchCode))
|
|
imm.writeMemory(EW + p, HookCode)
|
|
return True
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Main Function ..
|
|
|
|
def main(args):
|
|
ptypes={
|
|
# Debug types
|
|
'isdebuggerpresent':0, 'peb':1, 'checkremotedebuggerpresent':2,
|
|
'zwqueryinformationprocess':3, 'gettickcount':4, 'all_debug':10,
|
|
# Process Types
|
|
'zwquerysysteminformation':20, 'all_process':21,
|
|
# Window Types
|
|
'findwindowa':30, 'findwindoww':31, 'findwindowexa':32, 'findwindowexw':33,
|
|
'enumwindows':34, 'all_window':35,
|
|
# Packers (some example ones - of course many more are supported, add them as you find them)
|
|
'upx-lock':100, 'nspack':101, 'exestealth':102, 'escargot':103, 'rlpack':104
|
|
}
|
|
|
|
imm = immlib.Debugger()
|
|
|
|
if not args:
|
|
usage(imm)
|
|
return "Error : No patch type .. See log window for usage (Alt-L) .."
|
|
|
|
ptype = args[0].lower()
|
|
if ptypes.has_key( ptype ):
|
|
ptype = ptypes[ ptype ]
|
|
else:
|
|
return "Invalid type: %s" % ptype
|
|
|
|
# Intro text ..
|
|
imm.Log(" ")
|
|
imm.Log("%s v%s By BoB -> Team PEiD" % (ProgName, ProgVers), highlight=1)
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
# IsDebuggerPresent ..
|
|
# If patch PEB then no need for this ..
|
|
if ptype == 0:
|
|
Patch_IsDebuggerPresent(imm)
|
|
return "IsDebuggerPresent patched .."
|
|
|
|
# PEB ..
|
|
elif ptype == 1:
|
|
Patch_PEB(imm)
|
|
return "PEB Flags patched .."
|
|
|
|
# CheckRemoteDebuggerPresent ..
|
|
# If patch ZwQueryInformationProcess then no need for this ..
|
|
elif ptype == 2:
|
|
Patch_CheckRemoteDebuggerPresent(imm)
|
|
return "CheckRemoteDebuggerPresent patched .."
|
|
|
|
# ZwQueryInformationProcess ..
|
|
elif ptype == 3:
|
|
Patch_ZwQueryInformationProcess(imm)
|
|
return "ZwQueryInformationProcess patched .."
|
|
|
|
# GetTickCount ..
|
|
elif ptype == 4:
|
|
Patch_GetTickCount(imm)
|
|
return "GetTickCount patched .."
|
|
|
|
# Patch all anti-debug / debug-detection Apis and flags ..
|
|
elif ptype == 10:
|
|
Patch_PEB(imm)
|
|
Patch_IsDebuggerPresent(imm)
|
|
Patch_CheckRemoteDebuggerPresent(imm)
|
|
Patch_ZwQueryInformationProcess(imm)
|
|
Patch_GetTickCount(imm)
|
|
return "All Anti-debug Apis and flags patched .."
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# ZwQuerySystemInformation ..
|
|
elif ptype == 20:
|
|
Patch_ZwQuerySystemInformation(imm)
|
|
return "ZwQuerySystemInformation patched .."
|
|
|
|
# Patch all Process Apis to not return ImmDbg.EXE ..
|
|
elif ptype == 21:
|
|
Patch_ZwQuerySystemInformation(imm)
|
|
return "All debugger process finding Apis patched .."
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# User32.DLL isn't always in memory, so these are done slightly differently ..
|
|
|
|
# FindWindowA ..
|
|
elif ptype == 30:
|
|
if Patch_FindWindow(imm) == True:
|
|
return "FindWindowA patched .."
|
|
return "FindWindowA not patched .."
|
|
|
|
# FindWindowW ..
|
|
elif ptype == 31:
|
|
if Patch_FindWindow(imm, "W") == True:
|
|
return "FindWindowW patched .."
|
|
return "FindWindowW not patched .."
|
|
|
|
# FindWindowExA ..
|
|
elif ptype == 32:
|
|
if Patch_FindWindow(imm, True) == True:
|
|
return "FindWindowExA patched .."
|
|
return "FindWindowExA not patched .."
|
|
|
|
# FindWindowExW ..
|
|
elif ptype == 33:
|
|
if Patch_FindWindow(imm, True, "W") == True:
|
|
return "FindWindowExW patched .."
|
|
return "FindWindowExW not patched .."
|
|
|
|
# EnumWindows ..
|
|
elif ptype == 34:
|
|
if Patch_EnumWindows(imm) == True:
|
|
return "EnumWindows patched .."
|
|
return "EnumWindows not patched .."
|
|
|
|
# All Window functions ..
|
|
elif ptype == 35:
|
|
a = True
|
|
b = Patch_FindWindow(imm)
|
|
if b == False:
|
|
a = b
|
|
b = Patch_FindWindow(imm, suffix = "W")
|
|
if b == False:
|
|
a = b
|
|
b = Patch_FindWindow(imm, True, "A")
|
|
if b == False:
|
|
a = b
|
|
b = Patch_FindWindow(imm, True, "W")
|
|
if b == False:
|
|
a = b
|
|
b = Patch_EnumWindows(imm)
|
|
if b == False:
|
|
a = b
|
|
if a:
|
|
return "All debugger Window finding Apis patched .."
|
|
return "Some Window Apis not patched .. See Log .."
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
# Fix Anti-Debug of Upx-Lock ..
|
|
elif ptype == 100:
|
|
Patch_IsDebuggerPresent(imm)
|
|
Patch_GetTickCount(imm)
|
|
return "ImmDbg hidden from Upx-Lock .."
|
|
|
|
# Fix Anti-Debug of NsPack ..
|
|
elif ptype == 101:
|
|
Patch_PEB(imm)
|
|
return "ImmDbg hidden from NsPack .."
|
|
|
|
# Fix Anti-Debug of ExeStealth ..
|
|
elif ptype == 102:
|
|
Patch_PEB(imm)
|
|
return "ImmDbg hidden from ExeStealth .."
|
|
|
|
# Fix Anti-Debug of Escargot ..
|
|
elif ptype == 103:
|
|
Patch_IsDebuggerPresent(imm)
|
|
return "ImmDbg hidden from Escargot .."
|
|
|
|
# Fix Anti-Debug of RL!Pack (v1.18+ Still detects debug by guard page) ..
|
|
elif ptype == 104:
|
|
Patch_PEB(imm)
|
|
Patch_ZwQueryInformationProcess(imm)
|
|
Patch_EnumWindows(imm)
|
|
return "ImmDbg hidden from RL!Pack .."
|
|
|
|
|