//若返回值为TRUE则表示处于调试状态
bool CheckDebug()
{
bool bDebuged = false;
_asm push eax;
_asm mov eax, fs:[0x30];// eax保存PEB首地址
_asm mov al,byte ptr ds:[eax + 2];//BeginDebug字段的值
_asm mov bDebuged, al;
_asm pop eax
return bDebuged;
}
原理和上面一个一样;如:
// IsDebuggerPresent()返回值为True则表示处于调试状态
bool bDebug = IsDebuggerPresent();//它使用的方式,和上一个是一样的
if (bDebug)
{
MessageBox(NULL, L"正在被调试", L"注意", 0);
}
else
{
MessageBox(NULL, L"现在很安全", L"恭喜", 0);
}
在PEB+0x68处为peb的global字段,当其值=0x70时表示处于调试环境。
bool CheckDebug()
{
DWORD dwSign = 0;
_asm push eax;
_asm mov eax, fs:[0x30];
_asm mov eax, [eax + 0x68];
_asm mov dwSign, eax;
_asm pop eax
return dwSign==0x70;
}
API在ntdll.lib
中,要使用的话必须导入库文件。
#pragma comment(lib,"ntdll.lib")
NtQueryInformationProcess
源码实现:
//没注释,要我自己跟,我还没来得及跟。。。先就这样吧。
NTSTATUS NTAPI NtMyQueryInformationProcess(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
)
{
_asm {
mov eax, 16h
xor ecx, ecx
lea edx, [esp + 4]
call dword ptr fs : [0C0h]
add esp, 4
ret 14h
mov eax, 17h
mov ecx, 1Eh
lea edx, [esp + 4]
call dword ptr fs : [0C0h]
add esp, 4
ret 14h
}
}
原型:
__kernel_entry NTSTATUS NtQueryInformationProcess(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength
);
第2个参数是个枚举类型. 主要有3个值与调试有关:
ProcessDebugPort = 7
ProcessDebugObjectHandle = 30
ProcessDebugFlags = 31
当ProcessInformationClass 参数为7时,ProcessInformation返回调试状态.
即当处于非调试状态将传入变量置0,否则置0xffffffff
同理,当ProcessInformationClass 参数为30时,ProcessInformation返回调试对象句柄
即当调试状态时将传入变量置句柄值,否则置0
此外,API CheckRemoteDebuggerPresent就是通过这个方式来检测是否存在调试器
当ProcessInformationClass 参数为31时,ProcessInformation返回调试标记
即当调试状态时将传入变量置0,否则置1
bool CheckDebug()
{
int nDebugPort = 0;
NtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugPort,
&nDebugPort,
sizeof(nDebugPort),
NULL);
//当nDebugPort的值为-1时表示处于调试状态
return nDebugPort==-1;
}
bool CheckDebug()
{
HANDLE nDebugPort = 0;
NtQueryInformationProcess(
GetCurrentProcess(),
(PROCESSINFOCLASS)0x1E,
&nDebugPort,
sizeof(nDebugPort),
NULL);
//当nDebugPort返回值不为空时表示进程处于调试状态
return nDebugPort == NULL?false:true;
}
bool CheckDebug()
{
DWORD nDebugFlag = 0;
NtQueryInformationProcess(
GetCurrentProcess(),
(PROCESSINFOCLASS)0x1F,//DebugFlag
&nDebugFlag,
sizeof(nDebugFlag),
NULL);
///当nDebugFlag返回值为0时表示进程处于调试状态
return nDebugFlag == 0 ? true : false;
}
因为双击打开程序的话,父进程都是资源管理器,窗口名为Progman
.
只要比对本进程的父进程ID和窗口名为Progman
的进程ID是否相同即可。
bool NQIP_CheckParentProcess()
{
struct PROCESS_BASIC_INFORMATION {
ULONG ExitStatus; // 进程返回码
PPEB PebBaseAddress; // PEB地址
ULONG AffinityMask; // CPU亲和性掩码
LONG BasePriority; // 基本优先级
ULONG UniqueProcessId; // 本进程PID
ULONG InheritedFromUniqueProcessId; // 父进程PID
}stcProcInfo;
NtQueryInformationProcess(
GetCurrentProcess(),
ProcessBasicInformation, //查看进程的基本信息,其中能够查看到父进程的PID
&stcProcInfo,
sizeof(stcProcInfo), NULL
);
DWORD ExplorerPID = 0;
//获取本进程中的父进程ID
DWORD CurrentPID = stcProcInfo.InheritedFromUniqueProcessId;
//获取窗口名为"Progman"的进程ID
GetWindowThreadProcessId(FindWindow(L"Progman", NULL), &ExplorerPID);
printf("%d", ExplorerPID);
return ExplorerPID == CurrentPID ? false : true;
}
SystemKernelDebuggerInformation = 35 既(0x23);
使用这个字段(SYSTEM_INFORMATION_CLASS)SystemKernelDebuggerInformation ;
当操作系统开启内核调试模式时,会有一定特征,可以通过NtQuerySystemInformation API来获取
NTSTATUS NtQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
SYSTEM_INFORMATION_CLASS
是个枚举类型:
当为0x23时为获取系统是否在被调试信息存放到第2个参数指向的结构中,该结构是2个1字节的结构,当处于调试时这2字节都会被写入1.
bool CheckDebug()
{
struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION {
BOOLEAN DebuggerEanbled;
BOOLEAN DebuggerNotPresent;
}DebuggerInfo = { 0 };
NtQuerySystemInformation(
(SYSTEM_INFORMATION_CLASS)0x23,
&DebuggerInfo,
sizeof(DebuggerInfo),
NULL);
//能够检测当前操作系统是否处于调试模式,
//处于调试模式,可能当前正在进行内核调试(Windbg);
return DebuggerInfo.DebuggerEanbled;
}
遍历系统内是否存在调试对象,若存在调试对象,则说明很可能被调试。
bool NQO__NtQueryObject()
{
typedef struct _OBJECT_TYPE_INFORMATION {
UNICODE_STRING TypeName;
ULONG TotalNumberOfHanders;
ULONG TotalNumberOfObjects;
}OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
typedef struct _OBJECT_ALL_INFORMATION {
ULONG NumberOfObjectsTypes;
OBJECT_TYPE_INFORMATION ObjectTypeInfo[1];
}OBJECT_ALL_INFORMATION, *POBJECT_ALL_INFORMATION;
// 1. 获取欲查询信息大小
ULONG uSize = 0;
NtQueryObject(NULL,
(OBJECT_INFORMATION_CLASS)0x03,
&uSize,
sizeof(uSize),
&uSize);
// 2. 获取对象信息
POBJECT_ALL_INFORMATION pObjectAllInfo = (POBJECT_ALL_INFORMATION)new BYTE[uSize + 200];
NtQueryObject(NULL,
(OBJECT_INFORMATION_CLASS)0x03,
pObjectAllInfo,
uSize,
&uSize);
// 3. 循环遍历并处理对象信息
POBJECT_TYPE_INFORMATION pObjTypeInfo = pObjectAllInfo->ObjectTypeInfo;
for (int i = 0;
i < pObjectAllInfo->NumberOfObjectsTypes;
i++)
{
// 3.1 查看此对象的类型是否为DebugObject,还需要判断对象的数量,大于0则说明有调试对象
if (!wcscmp(L"DebugObject", pObjTypeInfo->TypeName.Buffer))
return true;
// 3.2 获取对象名占用空间的大小(考虑到了结构体对齐问题)
ULONG uNameLength = pObjTypeInfo->TypeName.Length;
ULONG uDataLength = uNameLength - uNameLength%sizeof(ULONG) + sizeof(ULONG);
// 3.3 指向下一个对象信息
pObjTypeInfo = (POBJECT_TYPE_INFORMATION)pObjTypeInfo->TypeName.Buffer;
pObjTypeInfo = (POBJECT_TYPE_INFORMATION)((PBYTE)pObjTypeInfo + uDataLength);
}
delete[] pObjectAllInfo;
return false;
}
通过调用ntdll.dll
中的ZwSetInformationThread()
,使其第二个参数为ThreadHideFromDebugger,则不会将异常分发给调试器,做到对调试器的隐藏。
typedef enum THREAD_INFO_CLASS{
ThreadHideFromDebugger = 17
};
typedef NTSTATUS(NTAPI *ZW_SET_INFORMATION_THREAD)(
IN HANDLE ThreadHandle,
IN THREAD_INFO_CLASS ThreadInformaitonClass,
IN PVOID ThreadInformation,
IN ULONG ThreadInformationLength);
void ZSIT_DetachDebug()
{
ZW_SET_INFORMATION_THREAD Func;
Func = (ZW_SET_INFORMATION_THREAD)GetProcAddress(
LoadLibrary(L"ntdll.dll"), "ZwSetInformationThread");
//攻击调试器,将本进程和调试器分离。
Func(GetCurrentThread(), ThreadHideFromDebugger, NULL, NULL);
}
通过加入一大堆无用代码来拉伸、混淆代码,例如:
void _declspec(naked) fun()
{
_asm push 0; // 6A 00
_asm jmp hehe; // EB 02
_asm __emit 0xE8;// E8
_asm __emit 0x12;// 12
hehe:
_asm push 0; // 6A 00
_asm jmp haha;// EB 02
_asm __emit 0x0f;// 0F
_asm __emit 0x15;// 15
haha:
_asm push 0;
_asm jmp heihei;
_asm __emit 0x56;
_asm __emit 0x78;
heihei:
_asm push 0;
_asm call MessageBoxA;
_asm ret;
}