API 检测
1 2 3 4 5 6
| PBYTE pCC = (PBYTE)MessageBoxW; if (*pCC == 0xCC) { return true; } MessageBoxW(0, L"未发现调试器!\n", 0, 0);
|
在调试器中下断点时,会将目标地址的首字节替换为0xCC,上述代码以此进行判断会否处于调试状态
破解思路:避免在函数的首地址下断点,可以在函数的其他代码部分下断,通常在ret指令处下断
PEB BeingDebugged
1 2 3 4 5
| __asm { MOV EAX, DWORD PTR FS : [0x30] ;获取PEB MOV AL, BYTE PTR DS : [EAX + 2] MOV bDebugged, AL }
|
当进程被调试时,AL的值为1,这个值取自PEB的BeingDebugged成员.
代码校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| bool CheckDebug_Checksum() { BOOL bDebugging = FALSE; __asm { call CHECKBEGIN CHECKBEGIN: pop esi mov ecx, 0x15 xor eax, eax xor ebx, ebx
_CALC_CHECKSUM : movzx ebx, byte ptr ds : [esi] add eax, ebx rol eax, 1 inc esi loop _CALC_CHECKSUM cmp eax, 0x1859a602 je _NOT_DEBUGGING mov bDebugging, 1 _NOT_DEBUGGING: } return bDebugging; }
|
调试信号判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| int main() { HANDLE hMutex = OpenMutex(MUTEX_MODIFY_STATE, FALSE, L"Global\\MyMutex"); if (hMutex) { printf("正被调试运行!\n"); getchar(); } else { CreateMutex(NULL, FALSE, L"Global\\MyMutex"); TCHAR szPath[MAX_PATH] = {}; GetModuleFileName(NULL, szPath, MAX_PATH); STARTUPINFO si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi = {}; BOOL bStatus = CreateProcess(szPath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); if (!bStatus) { printf("创建进程失败!\n"); return 0; } if (!DebugActiveProcess(pi.dwProcessId)) { printf("附加进程失败!\n"); return 0; } DEBUG_EVENT DbgEvent = { 0 }; DWORD dwState = DBG_EXCEPTION_NOT_HANDLED; BOOL bExit = FALSE; while (!bExit) { WaitForDebugEvent(&DbgEvent, INFINITE); if (DbgEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) { bExit = TRUE; } ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadId, dwState); } return 0; } return 0; }
|
以调试状态启动自身,等待调试事件触发,通过调试事件Code判断当前是否是调试状态,由于创建了互斥体,所以自身创建的调试进程启动后就退出了,不会出现两个软件同时运行的情况
破解思路: 跳转patch
窗口遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| PROCESSENTRY32 pe32 = { sizeof(pe32) }; HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { return FALSE; } Process32First(hProcessSnap, &pe32); do { if (_tcsicmp(pe32.szExeFile, TEXT("OllyDbg.exe")) == 0) { CloseHandle(hProcessSnap); return TRUE; } } while (Process32Next(hProcessSnap, &pe32)); CloseHandle(hProcessSnap);
|
通过遍历当前所有的窗口列表,以此判断是否有进程名为调试器名字的软件
父进程判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| struct PROCESS_BASIC_INFORMATION { DWORD ExitStatus; DWORD PebBaseAddress; DWORD AffinityMask; DWORD BasePriority; ULONG UniqueProcessId; ULONG InheritedFromUniqueProcessId; }pbi = {}; NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, (PVOID)&pbi, sizeof(pbi), NULL); PROCESSENTRY32 pe32 = { sizeof(pe32) }; HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { return FALSE; } Process32First(hProcessSnap, &pe32); do { if (pbi.InheritedFromUniqueProcessId == pe32.th32ProcessID) { if (_tcsicmp(pe32.szExeFile, TEXT("explorer.exe")) == 0) { CloseHandle(hProcessSnap); return FALSE; } else { CloseHandle(hProcessSnap); return TRUE; } } } while (Process32Next(hProcessSnap, &pe32)); CloseHandle(hProcessSnap); return FALSE;
|
NtQueryInformationProcess这个函数是未被公开的一个函数,在ntdll中,可以通过getprocaddress来调用,函数原型:
1 2 3 4 5 6 7
| NTSTATUS WINAPI NtQueryInformationProcess( _In_ HANDLE ProcessHandle, _In_ PROCESSINFOCLASS ProcessInformationClass, _Out_ PVOID ProcessInformation, _In_ ULONG ProcessInformationLength, _Out_opt_ PULONG ReturnLength );
|
ProcessHandle:查询进程的句柄
ProcessInformationClass: 查找信息的标识符,可以取以下值:
1 2 3 4 5
| ProcessBasicInformation 0 ProcessDebugPort 7 ProcessWow64Information 26 ProcessImageFileName 27 ProcessBreakOnTermination 29
|
ProcessInformation:要存放查询结果的缓冲区,这个结构要根据第二个参数来决定,
ProcessInformationLength:缓冲区大小
ReturnLength:实际返回的写入缓冲区的字节数
当调用此函数,传入当前进程的句柄,参数2传入ProcessBasicInfomation,在参数3 就会返回PROCESS_BASIC_INFORMATION这个结构体,文中有定义,这个结构体中就包含了父进程的句柄,在通过遍历的方式判断父进程的名字是不是explorer即可
FindWindow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| bool CheckDebug_FindWindow() { if (FindWindow(TEXT("OLLYDBG"), NULL)) return true; return false; } BOOL CALLBACK EnumWindowProc(HWND hWnd, LPARAM lParam) { TCHAR winTitle[0x100] = {}; GetWindowText(hWnd, winTitle, 0x100); if (_tcsstr(winTitle, TEXT("OllyDbg"))) { *((int*)lParam) = true; return false; } return true; } bool CheckDebug_EnumWindow() { int nFind = false; EnumWindows(EnumWindowProc, (LPARAM)&nFind); return nFind != 0; }
|
这个和之前的窗口遍历差不多,不过实现方式不同
硬件断点 & 异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
bool CheckDebug_HB() { CONTEXT context; HANDLE hThread = GetCurrentThread(); context.ContextFlags = CONTEXT_DEBUG_REGISTERS; GetThreadContext(hThread, &context); if (context.Dr0 != 0 || context.Dr1 != 0 || context.Dr2 != 0 || context.Dr3 != 0) { return TRUE; } return FALSE; }
bool CheckDebug_HB_EXCEPTION() { BOOL bDebugging = FALSE; __asm { push handler push DWORD ptr fs : [0] mov DWORD ptr fs : [0], esp __emit(0xcc) mov bDebugging, 1 jmp normal_code handler : mov eax, dword ptr ss : [esp + 0xc]; mov dword ptr ds : [eax + 0xb8], offset normal_code; mov ecx, [eax + 4]; or ecx, [eax + 8]; or ecx, [eax + 0x0C]; or ecx, [eax + 0x10]; je NoDebugger; mov ecx, [eax + 0xb4]; mov [ecx-0x0c],1 NoDebugger: xor eax, eax retn normal_code : pop dword ptr fs : [0] add esp, 4 } return bDebugging; }
|
CheckDebug_HB函数,通过判断是否有硬件断点,如果有则为调试状态
CheckDebug_HB_EXCEPTION函数,设置SEH异常处理函数后触发异常,如果未在调试状态,则会走到自己的函数中,否则在调试状态下
INT 2D
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| bool CheckDebug_INT_2D() { BOOL bDebugging = FALSE; __asm { push handler push DWORD ptr fs : [0] mov DWORD ptr fs : [0], esp int 0x2d nop mov bDebugging, 1 jmp normal_code handler : mov eax, dword ptr ss : [esp + 0xc] mov dword ptr ds : [eax + 0xb8], offset normal_code mov bDebugging, 0 xor eax, eax retn normal_code : pop dword ptr fs : [0] add esp, 4 } return bDebugging; }
|
此方法和CheckDebug_HB_EXCEPTION差不多,只不过触发异常的方式不同
NtGlobalFlag
1 2 3 4 5 6 7
| int nNtFlag = 0; __asm { MOV EAX, DWORD PTR FS : [0x30] MOV EAX, DWORD PTR DS : [EAX + 0x68] MOV nNtFlag, EAX } return nNtFlag==0x70;
|
在32位机器上, NtGlobalFlag
字段位于PEB
(进程环境块)0x68
的偏移处, 64位机器则是在偏移0xBC
位置. 该字段的默认值为0. 当调试器正在运行时, 该字段会被设置为一个特定的值:
1 2 3
| FLG_HEAP_ENABLE_TAIL_CHECK (0x10) FLG_HEAP_ENABLE_FREE_CHECK (0x20) FLG_HEAP_VALIDATE_PARAMETERS (0x40)
|
可以通过内存访问断点来定位反调试代码
或者修改注册表,来替换GlobalFlag的值:
注册表HKLM\System\CurrentControlSet\Control\SessionManager
的GlobalFlag
的值会替换进行NtGlobalFlag
字段. 尽管它随后还可能由Windows改变(以下会介绍), 注册表键值会对系统中所有进程产生影响并在重启后生效.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| bool CheckDebug_DebugPort() { DWORD dwDebugPort = 0; NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, &dwDebugPort, 4, 0); return dwDebugPort == -1; }
bool CheckDebug_DebugHandle() { DWORD dwDebugHandle = 0; NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)0x1E, &dwDebugHandle, 4, 0); return dwDebugHandle != 0; }
bool CheckDebug_DebugFlags() { bool bDebugFlags = 0; NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)0x1F, &bDebugFlags, 1, 0); printf("%d\n", bDebugFlags); return bDebugFlags == 0; }
|
参数二 = ProcessDebugPort
非调试状态下debugport == 0
参数二 = ProcessDebugObjectHandle
第三个参数返回为被调试对象句柄,当返回NULL时说明进程处于非调试状态
参数二 = ProcessDebugFlags
rocessDebugFlags参数用于获取调试标识,DebugFlag的值若为0则处于调试状态,若为1则处于非调试状态
NtQueryObject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| bool CheckDebug_QueryObject() { typedef struct _OBJECT_TYPE_INFORMATION { UNICODE_STRING TypeNames; ULONG TotalNumberOfHandles; 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; ULONG uSize = 0; NtQueryObject(NULL, (OBJECT_INFORMATION_CLASS)0x03, &uSize, sizeof(uSize), &uSize); POBJECT_ALL_INFORMATION pObjectAllInfo = (POBJECT_ALL_INFORMATION) new BYTE[uSize+4]; NtQueryObject(NULL, (OBJECT_INFORMATION_CLASS)0x03, pObjectAllInfo, uSize, &uSize); POBJECT_TYPE_INFORMATION pObjectTypeInfo = pObjectAllInfo->ObjectTypeInfo; for (int i = 0; i < pObjectAllInfo->NumberOfObjectsTypes; i++) { if (!wcscmp(L"DebugObject", pObjectTypeInfo->TypeNames.Buffer)) { delete[] pObjectAllInfo; return true; } ULONG uNameLength = pObjectTypeInfo->TypeNames.Length; ULONG uDataLength = uNameLength - uNameLength % sizeof(ULONG) + sizeof(ULONG); pObjectTypeInfo = (POBJECT_TYPE_INFORMATION)pObjectTypeInfo->TypeNames.Buffer; pObjectTypeInfo = (POBJECT_TYPE_INFORMATION)((PBYTE)pObjectTypeInfo + uDataLength); } delete[] pObjectAllInfo; return false; }
|
系统中某个调试器调试进程时,会创建一个调试对象类型的内核对象。检测该对象是否存在即可判断是否有进程正在被调试
ntdll!NtQueryObject可获得系统各种内核对象信息
1 2 3 4 5 6 7
| NTSTATUS NtQueryObject( _In_opt_ HANDLE Handle, _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, _Out_opt_ PVOID ObjectInformation, _In_ ULONG ObjectInformationLength, _Out_opt_ PULONG ReturnLength );
|
该API与上面讲过的API使用方法类似
1 2 3 4 5 6 7
| typedef enum _OBJECT_INFORMATION_CLASS { ObjectBasicInformation, ObjectTypeInformation, ObjectNameInformation, ObjectAllInformation, //3 ObjectHandleInformation } OBJECT_INFORMATION_CLASS;
|
ObjectAllInformation(0x3)
使用ObjectAllInformation可获得系统所有对象信息,然后从中检测是否存在调试对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| bool CheckDebug_KernelDebug() { struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION { BOOLEAN DebuggerEnabled; BOOLEAN DebuggerNotPresent; }DebuggerInfo = { 0 }; NtQuerySystemInformation( (SYSTEM_INFORMATION_CLASS)0x23, //查询信息类型 &DebuggerInfo, //输出查询信息 sizeof(DebuggerInfo), //查询类型大小 NULL); //实际返回数据大小 return DebuggerInfo.DebuggerEnabled; }
|
NtQuerySystemInformation这个函数用来获取当前的运行信息
当调用NtQuerySystemInfromation()API的时候,第一个参数SystemInformationClass的值被设置为systemKernelDebuggerInfromation(0x32的时候,函数调用返回的时候,若系统处于调试状态下,DebuggerEnable的值设置为1
ss寄存器
1 2 3 4 5 6 7
| __asm { push ss pop ss mov eax, 0xC000C1EE // This line will be traced over by debugger xor edx, edx // Debugger will step to this line }
|
当使用ss堆栈段寄存器进行操作时,调试器将跳过指令跟踪。如下图所示,调试器将立即移至xor edx,edx指令,同时执行上一条指令
代码运行时长
1 2 3 4 5 6 7 8
| bool CheckDebug_QueryPerformanceCounter() { LARGE_INTEGER startTime , endTime ; QueryPerformanceCounter(&startTime); printf("我是核心代码\n也可以是核心代码前的反调试时间检测代码\n"); QueryPerformanceCounter(&endTime); printf("%llx\n", endTime.QuadPart - startTime.QuadPart); return endTime.QuadPart - startTime.QuadPart > 0x500; }
|
代码运行时长检测不止这一种方法,像GetTickCount等函数都可以实现
rdtsc指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| bool CheckDebug_RDTSC() { int64_t t1=0, t2=0; int lo=0, hi=0; __asm { rdtsc mov [lo], eax mov [hi], edx } t1 = ((int64_t)lo) | ((int64_t)hi << 32); __asm{ rdtsc mov[lo], eax mov[hi], edx } t2 = ((int64_t)lo) | ((int64_t)hi << 32); printf("t2-t1=%llx\n", t2 - t1); return t2 - t1 > 0x100; }
|
此方法与代码运行时长类似
注册表查找调试器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| bool CheckDebug_Registry() { BOOL b64 = FALSE; IsWow64Process(GetCurrentProcess(), &b64); HKEY hkey = NULL; TCHAR *reg = b64 ? TEXT("SOFTWARE\\Wow6432Node\\Microsoft\\WindowsNT\\CurrentVersion\\AeDebug") : TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"); DWORD ret = RegCreateKey(HKEY_LOCAL_MACHINE, reg, &hkey); if (ret != ERROR_SUCCESS) return FALSE; TCHAR *subkey = TEXT("Debugger"); TCHAR value[256] = {}; DWORD len = 256; ret = RegQueryValueEx(hkey, subkey, NULL,NULL,(LPBYTE)value, &len); RegCloseKey(hkey); if (_tcsstr(value, TEXT("OLLYDBG")) != NULL) return TRUE; return FALSE; }
|
通过注册表查询是否包含调试器
rep指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| bool CheckDebug_CheckRepCC() { BOOL bDebugging = FALSE; __asm { xor eax,eax xor ecx,ecx inc ecx lea esi,key rep lodsb key: cmp al,0xcc je debuging jmp over debuging: mov bDebugging,1 over : } return bDebugging; }
|
执行到rep指令时,key处地址会被下断,从而与0xCC判断,是否处于调试状态
权限判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| bool CheckDebug_SeDebugPrivilege() { HMODULE hMod = GetModuleHandle(L"ntdll.dll"); typedef int(*CSRGETPROCESSID)(); CSRGETPROCESSID CsrGetProcessId = (CSRGETPROCESSID)GetProcAddress(hMod, "CsrGetProcessId"); DWORD pid = CsrGetProcessId(); HANDLE hCsr = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if (!hCsr) { return false; } CloseHandle(hCsr); return true; }
BOOL CheckDebug_EnumProcess_Csrss() { DWORD pid=0; DWORD ret = 0; PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { return FALSE; } Process32First(hProcessSnap, &pe32); do { if (wcscmp(pe32.szExeFile, L"csrss.exe") == 0) { pid = pe32.th32ProcessID; break; } } while (Process32Next(hProcessSnap, &pe32)); CloseHandle(hProcessSnap); HANDLE hCss = OpenProcess(PROCESS_QUERY_INFORMATION, NULL, pid); if (!hCss) { return FALSE; } CloseHandle(hCss); return FALSE; }
|
上述两个函数的实现不同,但作用是相同的,通过当前权限来判断是否处于调试状态
SEH异常1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| bool CheckDebug_SEH() { BOOL bDebugging = FALSE; __asm { push handler push DWORD ptr fs : [0] mov DWORD ptr fs : [0], esp __emit(0xcc) mov bDebugging, 1 jmp normal_code handler : mov eax, dword ptr ss : [esp + 0xc]; mov dword ptr ds : [eax + 0xb8], offset normal_code xor eax, eax retn normal_code : pop dword ptr fs : [0] add esp, 4 } return bDebugging; }
|
如果不在调试器中,则会走到代码中设置的的seh handler,否则将会继续执行
SEH异常2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| LONG WINAPI Fun( _In_ struct _EXCEPTION_POINTERS *ExceptionInfo ) { ExceptionInfo->ContextRecord->Eip += 5; return EXCEPTION_CONTINUE_EXECUTION; } bool CheckDebug_SetUnhandledExceptionFilter() { bool bDebug = false; __asm { __emit(0xCC); mov bDebug, 1 } return bDebug; }
|
在调用CheckDebug_SetUnhandledExceptionFilter之前要先调用SetUnhandledExceptionFilter将Fun设置为SEH的handler,此方法与SEH异常处理1相似
单步检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| bool CheckDebug_SingleStep() { bool bDebugged = false; __asm { push handler push DWORD ptr fs : [0] mov DWORD ptr fs : [0], esp pushfd or dword ptr ss : [esp], 0x100 popfd nop mov bDebugged,1 jmp normal_code handler : mov bDebugged, 1 mov eax, dword ptr ss : [esp + 0xc] mov ebx, normal_code mov dword ptr ds : [eax + 0xb8], ebx xor eax, eax retn normal_code : pop dword ptr fs : [0] add esp, 4 } return bDebugged; }
|
TF=1 的时候,会触发单步异常。该方法属于异常处理,不过比较特殊:未修改的 OD 无论
是 F9 还是 F8 都不能处理异常,有插件的 OD 在 F9 时能正确处理,F8 时不能正确处理。
StartInfo检测
1 2 3 4 5 6 7 8 9 10
| bool CheckDebug_STARTUPINFO() { STARTUPINFO si = {}; GetStartupInfo(&si); if (si.dwX || si.dwY || si.dwXSize || si.dwYSize) { printf("%x %x %x %x\n", si.dwX, si.dwY, si.dwXSize, si.dwYSize); return true; } return false; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| bool CheckDebug_SetInformationThread() { enum THREAD_INFO_CLASS { ThreadHideFromDebugger = 17 }; typedef NTSTATUS(NTAPI *ZW_SET_INFORMATION_THREAD)( IN HANDLE ThreadHandle, IN THREAD_INFO_CLASS ThreadInformationClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength); ZW_SET_INFORMATION_THREAD ZwSetInformationThread; ZwSetInformationThread = (ZW_SET_INFORMATION_THREAD)GetProcAddress(LoadLibrary(L"ntdll.dll"), "ZwSetInformationThread"); ZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, NULL); return true; }bool CheckDebug_STARTUPINFO() { STARTUPINFO si = {}; GetStartupInfo(&si); if (si.dwX || si.dwY || si.dwXSize || si.dwYSize) { printf("%x %x %x %x\n", si.dwX, si.dwY, si.dwXSize, si.dwYSize); return true; } return false; }
|
ZwSetInformationThread是一个未公开的函数,该API被调试者可将自身从调试器中分离出来(使调试进程终止运行,同时终止自身进程),该API不会对正常运行的程序(非调试运行)产生任何影响
TLS反调试
TLS相关知识:https://bbs.pediy.com/thread-249572.htm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| #include "stdafx.h" #include <Windows.h> #include <iostream> #include <iomanip> using namespace std;
#pragma comment(linker, "/INCLUDE:__tls_used") void lookupprocess(void); void Debugger(void); void NTAPI tls_callback(PVOID h, DWORD reason, PVOID pv) { lookupprocess(); Debugger(); MessageBox(NULL,"Not Main!","Test1",MB_OK); return; }
#pragma data_seg(".CRT$XLT")
PIMAGE_TLS_CALLBACK p_thread_callback = tls_callback; #pragma data_seg()
__declspec(thread) int value =0xcccccccc;
DWORD WINAPI NewThread ( LPVOID lParam ) { value = *((int*)lParam);
while(1) { printf("%d\n",value); Sleep(1000); }
return 0 ; }
#define THREAD_NUM 3
int main(int argc, char* argv[]) { int arry[3]={1,2,3}; value = 5 ; HANDLE hThread[THREAD_NUM];
for (int loop = 0; loop < THREAD_NUM; loop++) { hThread[loop] = CreateThread ( NULL, 0, NewThread, &arry[loop], 0, NULL ); Sleep(20000); }
WaitForMultipleObjects(THREAD_NUM, hThread, TRUE, INFINITE);
return 0; }
void lookupprocess() { PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); BOOL bMore = ::Process32First(hProcessSnap,&pe32); while(bMore) { strlwr(pe32.szExeFile); if (!strcmp(pe32.szExeFile,"ollyice.exe")) { exit(0); } if (!strcmp(pe32.szExeFile,"ollydbg.exe")) { exit(0); } if (!strcmp(pe32.szExeFile,"peid.exe")) { exit(0); } if (!strcmp(pe32.szExeFile,"idaq.exe")) { exit(0); } bMore = ::Process32Next(hProcessSnap,&pe32); } ::CloseHandle(hProcessSnap); }
void Debugger(void) { int result=0; __asm { mov eax, dword ptr fs:[30h] movzx eax, byte ptr ds:[eax+2h] mov result,eax } if (result) exit(0); }
|