Может кто набросать примеры антиотладочных приемов на С без применения асма или с минимальным применением.
1. Breakpoints
○ Int 3
○ Memory
○ Hardware
2. Timing Attacks
○ RDTSC
○ Win32 Timing APIs
3. Windows Internals
○ ProcessDebugFlags
○ Debug Object Handle
○ Thread Hiding
○ BlockInput
○ OutputDebugString
4. Process Exploitation
○ Open Process
○ Parent Processes
○ Self-Debugging
○ UnhandledExceptionFilter
○ NtQueryObject
5. Anti-Dumping
○ Nanomites
○ Stolen Code (Stolen Bytes)
○ SizeOfImage
○ Virtual Machines
○ Guard Pages
○ Removing the PE Header
6. IA-32 Instruction Exploits
○ Interrupt 2D
○ Stack Segment
○ Instruction Prefixes
7. OllyDBG Specific
○ FindWindow
○ OutputDebugString Exploit
8. WinDBG Specific
○ FindWindow
9. Other Techniques
○ Junk Code
○ Native Code Permutations
Introduction
In my previous article, I gave a short introduction into some Anti-Debugging/Debugger Detection techniques that primarily involved the use of Win32 API functions. In this article, I plan to travel a bit deeper into the interesting world of reverse engineering and explore some more intermediate level techniques for annoying reverse engineers. Some comments in my previous article noted that the techniques I presented could, and are most of the time, easily bypassed by intermediate level reversers; one statement I would like to make is that there is an ongoing battle between the coders who develop programs that protect against cracking and reverse engineering and the engineers themselves. Every time the protectors release a new technique, the engineers find a way around that specific method. This is the driving force behind the cracking "scene" and anti-reverse engineering fields. Most of the techniques here can easily be bypassed, and some of the others aren't as easily taken out of the picture; however, all of them can in one way, shape, or form be bypassed. I'm presenting these methods here to share the knowledge, and perhaps inspire others to find ways to apply these methods and utilize them in new and creative ways that challenge contemporary methodology.
Background
Anyone who is interested in the field of reverse engineering needs a strong understanding of Assembly language, so if your ASM is a little rusty or if you're just beginning to learn, here are some sites that can assist:
• Introduction to Assembly Language
• IA-32 Instruction Reference
• Iczelion's Win32 Assembly Homepage
Inline Functions
I didn't feel this side note required its own section; however, when reading this article or the attached source, one will notice the functions being marked inline. While this can cause bloat inside an executable, it is important in anti-reverse engineering. If there are very detailed function entries and sections, then the job for the reverse engineer just got much easier. Now, he or she knows exactly what is happening when that function is called. When in-lining, this doesn't happen, and the engineer is left guessing as to what is actually happening.
Breakpoints
There are three types of breakpoints available to a reverse engineer: hardware, memory, and INT 3h breakpoints. Breakpoints are essential to a reverse engineer, and without them, live analysis of a module does him or her little good. Breakpoints allow for the stopping of execution of a program at any point where one is placed. By utilizing this, reverse engineers can put breakpoints in areas like Windows APIs, and can very easily find where a badboy message (a messagebox saying you entered a bad serial, for example) is coming from. In fact, this is probably the most utilized technique in cracking, the only competition would be a referenced text string search. This is why breakpoint checks are done over important APIs like MessageBox, VirtualAlloc,CreateDialog, and others that play an important role in the protecting user information process. The first example will cover the most common type of breakpoint which utilizes the INT 3h instruction.
INT 3
INT 3h breakpoints are represented in in the IA-32 instruction set with the opcode CC (0xCC). This is the most common expression of this type of breakpoint; however, it can also be expressed as the byte sequence 0xCD 0x03 which can cause some troubles. Detecting this type of breakpoint is relatively simple, and some source would look like the following sample. However, we should be careful because using this method of scanning can lead to false positives.
Collapse
bool CheckForCCBreakpoint(void* pMemory, size_t SizeToCheck)
{
unsigned char *pTmp = (unsigned char*)pMemory;
for (size_t i = 0; i < SizeToCheck; i++)
{
if(pTmp == 0xCC)
return true;
}
return false;
}
Here's another obfuscated method for checking for INT 3 breakpoints. It is important to remember that the code shown above would stick out like a sore thumb to even new reversers. By adding another level of indirection, you, the protector, are improving your chances of successfully protecting the application.
Collapse
bool CheckForCCBreakpointXor55(void* pMemory, size_t SizeToCheck)
{
unsigned char *pTmp = (unsigned char*)pMemory;
unsigned char tmpchar = 0;
for (size_t i = 0; i < SizeToCheck; i++)
{
tmpchar = pTmp;
if( 0x99 == (tmpchar ^ 0x55) ) // 0xCC xor 0x55 = 0x99
return true;
}
return false;
}
Memory Breakpoints
Memory breakpoints are implemented by a debugger using guard pages, and they act like "a one-shot alarm for memory page access" (Creating Guard Pages). In a nutshell, when a page of memory is marked as PAGE_GUARD and is accessed, a STATUS_GUARD_PAGE_VIOLATION exception is raised, which can then be handled by the current program. At the moment, there's no accurate way to check for memory breakpoints. However, we can use the techniques a debugger uses to implement memory breakpoints to discover if our program is currently running under a debugger. In essence, what occurs is that we allocate a dynamic buffer and write a RET to the buffer. We then mark the page as a guard page and push a potential return address onto the stack. Next, we jump to our page, and if we're under a debugger, specifically OllyDBG, then we will hit the RET instruction and return to the address we pushed onto the stack before we jumped to our page. Otherwise, a STATUS_GUARD_PAGE_VIOLATION exception will occur, and we know we're not being debugged by OllyDBG. Here is an example in source:
Collapse
bool MemoryBreakpointDebuggerCheck()
{
unsigned char *pMem = NULL;
SYSTEM_INFO sysinfo = {0};
DWORD OldProtect = 0;
void *pAllocation = NULL; // Get the page size for the system
GetSystemInfo(&sysinfo); // Allocate memory
pAllocation = VirtualAlloc(NULL, sysinfo.dwPageSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (pAllocation == NULL)
return false;
// Write a ret to the buffer (opcode 0xc3)
pMem = (unsigned char*)pAllocation;
*pMem = 0xc3;
// Make the page a guard page
if (VirtualProtect(pAllocation, sysinfo.dwPageSize,
PAGE_EXECUTE_READWRITE | PAGE_GUARD,
&OldProtect) == 0)
{
return false;
}
__try
{
__asm
{
mov eax, pAllocation
// This is the address we'll return to if we're under a debugger
push MemBpBeingDebugged
jmp eax // Exception or execution, which shall it be?
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
// The exception occured and no debugger was detected
VirtualFree(pAllocation, NULL, MEM_RELEASE);
return false;
}
__asm{MemBpBeingDebugged:}
VirtualFree(pAllocation, NULL, MEM_RELEASE);
return true;
}
Hardware Breakpoints
Hardware breakpoints are a technology implemented by Intel in their processor architecture, and are controlled by the use of special registers known as Dr0-Dr7. Dr0 through Dr3 are 32 bit registers that hold the address of the breakpoint. Dr4 and 5 are reserved by Intel for debugging the other registers, and Dr6 and 7 are used to control the behavior of the breakpoints (Intel1). There is a little bit too much information for me to cover how the Dr6 and Dr7 registers affect breakpoint behavior. However, anyone who is interested should read the Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide for an in-depth explanation of how the registers work.
Now, in order to detect and/or remove hardware breakpoints, there are two methods we can utilize: the Win32 GetThreadContext andSetThreadContext, or using Structured Exception Handling. In the first example, I'll show how to use the Win32 API functions:
Collapse
// CheckHardwareBreakpoints returns the number of hardware
// breakpoints detected and on failure it returns -1.
int CheckHardwareBreakpoints()
{
unsigned int NumBps = 0;
// This structure is key to the function and is the
// medium for detection and removal
CONTEXT ctx;
ZeroMemory(&ctx, sizeof(CONTEXT));
// The CONTEXT structure is an in/out parameter therefore we have
// to set the flags so Get/SetThreadContext knows what to set or get.
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
// Get a handle to our thread
HANDLE hThread = GetCurrentThread();
// Get the registers
if(GetThreadContext(hThread, &ctx) == 0)
return -1;
// Now we can check for hardware breakpoints, its not
// necessary to check Dr6 and Dr7, however feel free to
if(ctx.Dr0 != 0)
++NumBps;
if(ctx.Dr1 != 0)
++NumBps;
if(ctx.Dr2 != 0)
++NumBps;
if(ctx.Dr3 != 0)
++NumBps;
return NumBps;
}
The SEH method of manipulating the debug registers is much more commonly seen in anti-reverse engineering programs, and is implemented easier in ASM, as shown in the following example:
Collapse
; One quick note about this little prelude; in Visual Studio 2008
; release builds are compiled with the /SAFESEH flag which helps
; prevent exploitation of SEH by shellcode and the likes.
; What this little snippet does is add our SEH Handler to a
; special table containing a list of "safe" exceptions handlers , which
; if we didn't in release builds our handler would never be called,
; this problem plauged me for a long time, and im considering writing
; a short article on it
ClrHwBpHandler proto
.safeseh ClrHwBpHandler
ClearHardwareBreakpoints proc
assume fs:nothing
push offset ClrHwBpHandler
push fs:[0]
mov dword ptr fs:[0], esp ; Setup SEH
xor eax, eax
div eax ; Cause an exception
pop dword ptr fs:[0] ; Execution continues here
add esp, 4
ret
ClearHardwareBreakpoints endp
ClrHwBpHandler proc
xor eax, eax
mov ecx, [esp + 0ch] ; This is a CONTEXT structure on the stack
mov dword ptr [ecx + 04h], eax ; Dr0
mov dword ptr [ecx + 08h], eax ; Dr1
mov dword ptr [ecx + 0ch], eax ; Dr2
mov dword ptr [ecx + 10h], eax ; Dr3
mov dword ptr [ecx + 14h], eax ; Dr6
mov dword ptr [ecx + 18h], eax ; Dr7
add dword ptr [ecx + 0b8h], 2 ; We add 2 to EIP to skip the div eax
ret
ClrHwBpHandler endp
Timing Attacks
The theory behind timing attacks is that executing a section of code, especially a small section, should only take a miniscule amount of time. Therefore, if a timed section of code takes a greater amount of time than a certain set limit, then there is most likely a debugger attached, and someone is stepping through the code. This genre of attacks has many small variations, and the most common example uses the IA-32 RDTSC instruction. Other methods utilize different timing methods such as timeGetTime, GetTickCount, and QueryPerformanceCounter.
RDTSC
RDTSC is an IA-32 instruction that stands for Read Time-Stamp Counter, which is pretty self-explanatory in itself. Processors since the Pentium have had a counter attached to the processor that is incremented every clock cycle, and reset to 0 when the processor is reset. As you can see, this is a very powerful timing technique; however, Intel doesn't serialize the instruction; therefore, it is not guaranteed to be 100% accurate. This is why Microsoft encourages the use of its Win32 timing APIs since they're supposed to be as accurate as Windows can guarantee. The great thing about timing attacks, in general, though is that implementing the technique is rather simple; all a developer needs to do is decide which functions he or she would like to protect using a timing attack, and then he or she can simply surround the blocks of code in a timing block and can compare that to a programmer set limit, and can exit the program if the timed section takes too much time to execute. Here is an example:
Collapse
#define SERIAL_THRESHOLD 0x10000 // 10,000h ticks
DWORD GenerateSerial(TCHAR* pName)
{
DWORD LocalSerial = 0;
DWORD RdtscLow = 0; // TSC Low
__asm
{
rdtsc
mov RdtscLow, eax
}
size_t strlen = _tcslen(pName);
// Generate serial
for(unsigned int i = 0; i < strlen; i++)
{
LocalSerial += (DWORD) pName;
LocalSerial ^= 0xDEADBEEF;
}
__asm
{
rdtsc
sub eax, RdtscLow
cmp eax, SERIAL_THRESHOLD
jbe NotDebugged
push 0
call ExitProcess
NotDebugged:
}
return LocalSerial;
}
Win32 Timing Functions
The concepts are exactly the same in this variation except that we have different means of timing our function. In the following example, GetTickCountis used, but as commented, could be replaced with timeGetTime or QueryPerformanceCounter.
Collapse
#define SERIAL_THRESHOLD 0x10000 // 10,000h ticks
GenerateSerialWin32Attack(TCHAR* pName)
{
DWORD LocalSerial = 0;
size_t strlen = _tcslen(pName);
DWORD Counter = GetTickCount(); // Could be replaced with timeGetTime()
// Generate serial
for(unsigned int i = 0; i < strlen; i++)
{
LocalSerial += (DWORD) pName;
LocalSerial ^= 0xDEADBEEF;
}
Counter = GetTickCount() - Counter; // Could be replaced with timeGetTime()
if(Counter >= SERIAL_THRESHOLD)
ExitProcess(0);
return LocalSerial;
}
Windows Internals
The following methods of anti-reverse engineering utilize the peculiarities of the Windows Operating System in order to implement some sort of protection, ranging from hiding a thread from a debugger, to revealing the presence of a debugger. Many of the functions used in the following examples are exported from ntdll.dll, and are not guarenteed by Microsoft to behave consistently in different versions of the Operating System. Therefore, some caution should be taken when using these examples in your own programs. That being said, I have yet to see one of these APIs change drastically in behavior, so do not take the previous statement as a commandment to avoid these implementations.
ProcessDebugFlags
The ProcessDebugFlags (0x1f) is an undocumented class that can be passed to the NtQueryProcessInformation function. WhenNtQueryProcessInformation is called with the ProcessDebugFlags class, the function will return the inverse of EPROCESS->NoDebugInherit, which means that if a debugger is present, then this function will return FALSE if the process is being debugged. Here's the CheckProcessDebugFlags:
Collapse
// CheckProcessDebugFlags will return true if
// the EPROCESS->NoDebugInherit is == FALSE,
// the reason we check for false is because
// the NtQueryProcessInformation function returns the
// inverse of EPROCESS->NoDebugInherit so (!TRUE == FALSE)
inline bool CheckProcessDebugFlags()
{
// Much easier in ASM but C/C++ looks so much better
typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)
(HANDLE ,UINT ,PVOID ,ULONG , PULONG);
DWORD NoDebugInherit = 0;
NTSTATUS Status;
// Get NtQueryInformationProcess
pNtQueryInformationProcess NtQIP = (pNtQueryInformationProcess)
GetProcAddress( GetModuleHandle( TEXT("ntdll.dll") ),
"NtQueryInformationProcess" );
Status = NtQIP(GetCurrentProcess(),
0x1f, // ProcessDebugFlags
&NoDebugInherit, 4, NULL);
if (Status != 0x00000000)
return false;
if(NoDebugInherit == FALSE)
return true;
else
return false;
}
Debug Object Handle
Beginning from Windows XP, when a process is debugged, a debug object would be created for that debugging session. A handle to this object is also created, and can be queried using NtQueryInformationProcess. The presence of this handle shows that the process is being actively debugged, and this information can be quite a pain to SPOILER since it comes from the kernel. Here's the DebugObjectCheck function:
Collapse
// This function uses NtQuerySystemInformation
// to try to retrieve a handle to the current
// process's debug object handle. If the function
// is successful it'll return true which means we're
// being debugged or it'll return false if it fails
// or the process isn't being debugged
inline bool DebugObjectCheck()
{
// Much easier in ASM but C/C++ looks so much better
typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)
(HANDLE ,UINT ,PVOID ,ULONG , PULONG);
HANDLE hDebugObject = NULL;
NTSTATUS Status;
// Get NtQueryInformationProcess
pNtQueryInformationProcess NtQIP = (pNtQueryInformationProcess)
GetProcAddress( GetModuleHandle( TEXT("ntdll.dll") ),
"NtQueryInformationProcess" );
Status = NtQIP(GetCurrentProcess(),
0x1e, // ProcessDebugObjectHandle
&hDebugObject, 4, NULL);
if (Status != 0x00000000)
return false;
if(hDebugObject)
return true;
else
return false;
}
Thread Hiding
In Windows 2000, the guys behind Windows introduced a new class to be passed into NtSetInformationThread, and it was namedHideThreadFromDebugger. It is the first anti-debugging API implemented by Windows, and is very powerful. The class prevents debuggers from receiving events from any thread that has had NtSetInformationThread with the HideThreadFromDebugger class called on it. These events include breakpoints, and the exiting of the program if it is called on the main thread of an application. Here is the HideThread function:
Collapse
// HideThread will attempt to use
// NtSetInformationThread to SPOILER a thread
// from the debugger, Passing NULL for
// hThread will cause the function to SPOILER the thread
// the function is running in. Also, the function returns
// false on failure and true on success
inline bool HideThread(HANDLE hThread)
{
typedef NTSTATUS (NTAPI *pNtSetInformationThread)
(HANDLE, UINT, PVOID, ULONG);
NTSTATUS Status;
// Get NtSetInformationThread
pNtSetInformationThread NtSIT = (pNtSetInformationThread)
GetProcAddress(GetModuleHandle( TEXT("ntdll.dll") ),
"NtSetInformationThread");
// Shouldn't fail
if (NtSIT == NULL)
return false;
// Set the thread info
if (hThread == NULL)
Status = NtSIT(GetCurrentThread(),
0x11, // HideThreadFromDebugger
0, 0);
else
Status = NtSIT(hThread, 0x11, 0, 0);
if (Status != 0x00000000)
return false;
else
return true;
}
BlockInput
This is about as simple as it comes. BlockInput does as the names suggests, and blocks mouse and keyboard messages from reaching the desired application; this technique is effective due to the fact that only the thread that called BlockInput can call it to remove the block ("BlockInput Function"). This isn't really an anti-reverse engineering technique, but more of a way to mess with someone debugging your application. A simple source code looks like:
Collapse
BlockInput(TRUE); // Nice and simple
OutputDebugString
The OutputDebugString technique works by determining if OutputDebugString causes an error. An error will only occur if there is no active debugger for the process to receive the string; therefore, we can conclude that if there is no error (by calling GetLastError) after calling OutputDebugString, then there is a debugger present.
Collapse
// CheckOutputDebugString checks whether or
// OutputDebugString causes an error to occur
// and if the error does occur then we know
// there's no debugger, otherwise if there IS
// a debugger no error will occur
inline bool CheckOutputDebugString(LPCTSTR String)
{
OutputDebugString(String);
if (GetLastError() == 0)
return true;
else
return false;
}
Process Exploitation
These techniques exploit the Windows process environment and management system in order to implement protection. Some of these techniques, especially self-debugging, are widely used by many packers and protectors.
Open Process
This debugger detection technique exploits process privileges in order to determine if a process is currently being run under a debugger. This technique works because when a process is attached to or run under a debugger, if the process privileges are not correctly reset by the attaching debugger, the process receives the SeDebugPrivilege set which allows the process to open a handle to any process ("How To Use the SeDebugPrivilege to Acquire Any Process Handle"). This includes a vital system process like csrss.exe, which we normally wouldn't have access to. Here is some source code to illustrate the technique:
Collapse
// The function will attempt to open csrss.exe with
// PROCESS_ALL_ACCESS rights if it fails we're
// not being debugged however, if its successful we probably are
inline bool CanOpenCsrss()
{
HANDLE Csrss = 0;
// If we're being debugged and the process has
// SeDebugPrivileges privileges then this call
// will be successful, note that this only works
// with PROCESS_ALL_ACCESS.
Csrss = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, GetCsrssProcessId());
if (Csrss != NULL)
{
CloseHandle(Csrss);
return true;
}
else
return false;
}
Parent Process
Normally, Windows users start a process from a window created or provided by the Windows Shell. In this situation, the child process's parent process isExplorer.exe. Therefore, we can retrieve the process ID of Explorer.exe and our parent process and compare them. This is, of course, a somewhat risky process since the parent process of your process isn't guaranteed to be Explorer.exe; nonetheless, it is still an interesting technique, and here is an example:
Collapse
// This function returns true if the parent process of
// the current running process is Explorer.exe
inline bool IsParentExplorerExe()
{
// Both GetParentProcessId and GetExplorerPIDbyShellWindow
// can be found in the attached source
DWORD PPID = GetParentProcessId();
if(PPID == GetExplorerPIDbyShellWindow())
return true;
else
return false;
//return GetParentProcessId() == GetExplorerPIDbyShellWindow()'
}
Self-Debugging
Self-Debugging is a technique where the main process spawns a child process that debugs the process that created the child process, as shown in the diagram. This technique can be very useful as it can be utilized to implement techniques such as Nanomites and others. This also prevents other debuggers from attaching to the same process; however, this can be bypassed be setting the EPROCESS->DebugPort (the EPROCESS structure is a struct returned by the kernel mode function PsGetProcessId) field to 0. This allows another debugger to attach to a process that already has a debugger attached to it. Here's some sample code:
Collapse
// Debug self is a function that uses CreateProcess
// to create an identical copy of the current process
// and debugs it
void DebugSelf()
{
HANDLE hProcess = NULL;
DEBUG_EVENT de;
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&de, sizeof(DEBUG_EVENT));
GetStartupInfo(&si);
// Create the copy of ourself
CreateProcess(NULL, GetCommandLine(), NULL, NULL, FALSE,
DEBUG_PROCESS, NULL, NULL, &si, &pi);
// Continue execution
ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId, DBG_CONTINUE);
// Wait for an event
WaitForDebugEvent(&de, INFINITE);
}
UnhandledExceptionFilter
The UnhandledExceptionFilter is the long name for an exception handler that is called when there are no other handlers to handle the exception. The following diagram shows how Windows propagates exceptions. When utilizing the UnhandledExceptionFilter technique, one needs to be aware that if a debugger is attached, that process will exit instead of resuming execution, which in the context of anti-reverse engineering is quite fine, in my opinion.
Collapse
LONG WINAPI UnhandledExcepFilter(PEXCEPTION_POINTERS pExcepPointers)
{
// Restore old UnhandledExceptionFilter
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)
pExcepPointers->ContextRecord->Eax);
// Skip the exception code
pExcepPointers->ContextRecord->Eip += 2;
return EXCEPTION_CONTINUE_EXECUTION;
}
int main()
{
SetUnhandledExceptionFilter(UnhandledExcepFilter);
__asm{xor eax, eax}
__asm{div eax}
// Execution resumes here if there is no debugger
// or if there is a debugger it will never
// reach this point of execution
}
NtQueryObject
The NtQueryObject function, when called with the ObjectAllTypesInformation class, will return information about the host system and the current process. There is a wealth of information to be mined from this function, but we're most concerned with the information given about the DebugObjects in the environment. In Windows XP and Vista, a DebugObject entry is maintained in this list of objects, and most importantly, the number of objects of each type of object. The object and its related information can be expressed as a OBJECT_INFORMATION_TYPE struct. However, calling the NtQueryObjectfunction with the ObjectAllTypesInformation class actually returns a buffer that begins with a OBJECT_TYPE_INFORMATION struct. However, there is more than one OBJECT_INFORMATION_TYPE entry, and traversing the buffer containing these entries isn't as straightforward as array indexing. The source shows that the next OBJECT_INFORMATION_TYPE struct lies after the previous' UNICODE_STRING.Buffer entry. These structs are also padded and DWORD aligned; refer to the source to examine how to navigate the buffer.
Collapse
typedef struct _OBJECT_TYPE_INFORMATION {
UNICODE_STRING TypeName;
ULONG TotalNumberOfHandles;
ULONG TotalNumberOfObjects;
}OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
typedef struct _OBJECT_TYPE_INFORMATION {
UNICODE_STRING TypeName;
ULONG TotalNumberOfHandles;
ULONG TotalNumberOfObjects;
}OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
// ObjectListCheck uses NtQueryObject to check the environments
// list of objects and more specifically for the number of
// debug objects. This function can cause an exception (although rarely)
// so either surround it in a try catch or __try __except block
// but that shouldn't happen unless one tinkers with the function
inline bool ObjectListCheck()
{
typedef NTSTATUS(NTAPI *pNtQueryObject)
(HANDLE, UINT, PVOID, ULONG, PULONG);
POBJECT_ALL_INFORMATION pObjectAllInfo = NULL;
void *pMemory = NULL;
NTSTATUS Status;
unsigned long Size = 0;
// Get NtQueryObject
pNtQueryObject NtQO = (pNtQueryObject)GetProcAddress(
GetModuleHandle( TEXT( "ntdll.dll" ) ),
"NtQueryObject" );
// Get the size of the list
Status = NtQO(NULL, 3, //ObjectAllTypesInformation
&Size, 4, &Size);
// Allocate room for the list
pMemory = VirtualAlloc(NULL, Size, MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if(pMemory == NULL)
return false;
// Now we can actually retrieve the list
Status = NtQO((HANDLE)-1, 3, pMemory, Size, NULL);
// Status != STATUS_SUCCESS
if (Status != 0x00000000)
{
VirtualFree(pMemory, 0, MEM_RELEASE);
return false;
}
// We have the information we need
pObjectAllInfo = (POBJECT_ALL_INFORMATION)pMemory;
unsigned char *pObjInfoLocation =
(unsigned char*)pObjectAllInfo->ObjectTypeInformation;
ULONG NumObjects = pObjectAllInfo->NumberOfObjects;
for(UINT i = 0; i < NumObjects; i++)
{
POBJECT_TYPE_INFORMATION pObjectTypeInfo =
(POBJECT_TYPE_INFORMATION)pObjInfoLocation;
// The debug object will always be present
if (wcscmp(L"DebugObject", pObjectTypeInfo->TypeName.Buffer) == 0)
{
// Are there any objects?
if (pObjectTypeInfo->TotalNumberOfObjects > 0)
{
VirtualFree(pMemory, 0, MEM_RELEASE);
return true;
}
else
{
VirtualFree(pMemory, 0, MEM_RELEASE);
return false;
}
}
// Get the address of the current entries
// string so we can find the end
pObjInfoLocation =
(unsigned char*)pObjectTypeInfo->TypeName.Buffer;
// Add the size
pObjInfoLocation +=
pObjectTypeInfo->TypeName.Length;
// Skip the trailing null and alignment bytes
ULONG tmp = ((ULONG)pObjInfoLocation) & -4;
// Not pretty but it works
pObjInfoLocation = ((unsigned char*)tmp) +
sizeof(unsigned long);
}
VirtualFree(pMemory, 0, MEM_RELEASE);
return true;
}
inline bool CanOpenCsrss()
{
HANDLE Csrss = 0;
// If we're being debugged and the process has
// SeDebugPrivileges privileges then this call
// will be successful, note that this only works
// with PROCESS_ALL_ACCESS.
Csrss = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, GetCsrssProcessId());
a = Csrss;
if (Csrss != NULL)
{
CloseHandle(Csrss);
return true;
}
else
return false;
}