近期参加了一次山东省,大学生信息安全比赛,里面的逆向让我备受打击呐!233

反调试做的很奇葩,自然也令我深受启发,一些方式,然后顺便打算总结下近期一些简单的反调试激情。赶脚大部分反调试都是依赖于PEB表。

先说说最简单的API检测是否被调试,有两个函数IsDebuggerPresent和CheckRemoteDebuggerPresent,都是Bool的返回值,为真,就是你的程序被调戏了~~

然后是检测进程PEB的BeingDebugged标志位,利用内嵌汇编,通过FS寄存器找到PEB结构,去检测BeingDebugged标志位是否为这。

bool PebIsDebuggedApproach()
{
       char result = 0;
       __asm
       {
		      // 进程的PEB地址放在fs这个寄存器位置上
              mov eax, fs:[30h]
			  // 查询BeingDebugged标志位
              mov al, BYTE PTR [eax + 2] 
              mov result, al
       }
}

后面几反调试还要用到TEB,所以就简单记录下TEB结构:

+0x000 NtTib            : _NT_TIB                     ;SEH链表指针
   +0x01c EnvironmentPointer : Ptr32 Void
   +0x020 ClientId         : _CLIENT_ID                  ;cid
   +0x028 ActiveRpcHandle  : Ptr32 Void
   +0x02c ThreadLocalStoragePointer : Ptr32 Void
   +0x030 ProcessEnvironmentBlock : Ptr32 _PEB           ;进程环境块PEB结构地址
   +0x034 LastErrorValue   : Uint4B
   +0x038 CountOfOwnedCriticalSections : Uint4B
   +0x03c CsrClientThread  : Ptr32 Void
   +0x040 Win32ThreadInfo  : Ptr32 Void
   +0x044 User32Reserved   : [26] Uint4B
   +0x0ac UserReserved     : [5] Uint4B
   +0x0c0 WOW32Reserved    : Ptr32 Void
   +0x0c4 CurrentLocale    : Uint4B
   +0x0c8 FpSoftwareStatusRegister : Uint4B
   +0x0cc SystemReserved1  : [54] Ptr32 Void
   +0x1a4 ExceptionCode    : Int4B
   +0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
   +0x1bc SpareBytes1      : [24] UChar
   +0x1d4 GdiTebBatch      : _GDI_TEB_BATCH
   +0x6b4 RealClientId     : _CLIENT_ID
   +0x6bc GdiCachedProcessHandle : Ptr32 Void
   +0x6c0 GdiClientPID     : Uint4B
   +0x6c4 GdiClientTID     : Uint4B
   +0x6c8 GdiThreadLocalInfo : Ptr32 Void
   +0x6cc Win32ClientInfo  : [62] Uint4B
   +0x7c4 glDispatchTable  : [233] Ptr32 Void
   +0xb68 glReserved1      : [29] Uint4B
   +0xbdc glReserved2      : Ptr32 Void
   +0xbe0 glSectionInfo    : Ptr32 Void
   +0xbe4 glSection        : Ptr32 Void
   +0xbe8 glTable          : Ptr32 Void
   +0xbec glCurrentRC      : Ptr32 Void
   +0xbf0 glContext        : Ptr32 Void
   +0xbf4 LastStatusValue  : Uint4B
   +0xbf8 StaticUnicodeString : _UNICODE_STRING
   +0xc00 StaticUnicodeBuffer : [261] Uint2B
   +0xe0c DeallocationStack : Ptr32 Void
   +0xe10 TlsSlots         : [64] Ptr32 Void
   +0xf10 TlsLinks         : _LIST_ENTRY
   +0xf18 Vdm              : Ptr32 Void
   +0xf1c ReservedForNtRpc : Ptr32 Void
   +0xf20 DbgSsReserved    : [2] Ptr32 Void
   +0xf28 HardErrorsAreDisabled : Uint4B
   +0xf2c Instrumentation  : [16] Ptr32 Void
   +0xf6c WinSockData      : Ptr32 Void
   +0xf70 GdiBatchCount    : Uint4B
   +0xf74 InDbgPrint       : UChar
   +0xf75 FreeStackOnTermination : UChar
   +0xf76 HasFiberData     : UChar
   +0xf77 IdealProcessor   : UChar
   +0xf78 Spare3           : Uint4B
   +0xf7c ReservedForPerf  : Ptr32 Void
   +0xf80 ReservedForOle   : Ptr32 Void
   +0xf84 WaitingOnLoaderLock : Uint4B
   +0xf88 Wx86Thread       : _Wx86ThreadState
   +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
   +0xf98 ImpersonationLocale : Uint4B
   +0xf9c IsImpersonating  : Uint4B
   +0xfa0 NlsCache         : Ptr32 Void
   +0xfa4 pShimData        : Ptr32 Void
   +0xfa8 HeapVirtualAffinity : Uint4B
   +0xfac CurrentTransactionHandle : Ptr32 Void
   +0xfb0 ActiveFrame      : Ptr32 _TEB_ACTIVE_FRAME
   +0xfb4 SafeThunkCall    : UChar
   +0xfb5 BooleanSpare     : [3] UChar

这是线程的TEB结构,TEB是FS指向,其地址+30H就是进程PEB结构:

typedef struct _PEB { // Size: 0x1D8
/*000*/ UCHAR InheritedAddressSpace;
/*001*/ UCHAR ReadImageFileExecOptions;
/*002*/ UCHAR BeingDebugged;
/*003*/ UCHAR SpareBool; // Allocation size
/*004*/ HANDLE Mutant;
/*008*/ HINSTANCE ImageBaseAddress; // Instance
/*00C*/ VOID *DllList;
/*010*/ PPROCESS_PARAMETERS *ProcessParameters;
/*014*/ ULONG SubSystemData;
/*018*/ HANDLE DefaultHeap;
/*01C*/ KSPIN_LOCK FastPebLock;
/*020*/ ULONG FastPebLockRoutine;
/*024*/ ULONG FastPebUnlockRoutine;
/*028*/ ULONG EnvironmentUpdateCount;
/*02C*/ ULONG KernelCallbackTable;
/*030*/ LARGE_INTEGER SystemReserved;
/*038*/ ULONG FreeList;
/*03C*/ ULONG TlsExpansionCounter;
/*040*/ ULONG TlsBitmap;
/*044*/ LARGE_INTEGER TlsBitmapBits;
/*04C*/ ULONG ReadOnlySharedMemoryBase;
/*050*/ ULONG ReadOnlySharedMemoryHeap;
/*054*/ ULONG ReadOnlyStaticServerData;
/*058*/ ULONG AnsiCodePageData;
/*05C*/ ULONG OemCodePageData;
/*060*/ ULONG UnicodeCaseTableData;
/*064*/ ULONG NumberOfProcessors;
/*068*/ LARGE_INTEGER NtGlobalFlag; // Address of a local copy
/*070*/ LARGE_INTEGER CriticalSectionTimeout;
/*078*/ ULONG HeapSegmentReserve;
/*07C*/ ULONG HeapSegmentCommit;
/*080*/ ULONG HeapDeCommitTotalFreeThreshold;
/*084*/ ULONG HeapDeCommitFreeBlockThreshold;
/*088*/ ULONG NumberOfHeaps;
/*08C*/ ULONG MaximumNumberOfHeaps;
/*090*/ ULONG ProcessHeaps;
/*094*/ ULONG GdiSharedHandleTable;
/*098*/ ULONG ProcessStarterHelper;
/*09C*/ ULONG GdiDCAttributeList;
/*0A0*/ KSPIN_LOCK LoaderLock;
/*0A4*/ ULONG OSMajorVersion;
/*0A8*/ ULONG OSMinorVersion;
/*0AC*/ USHORT OSBuildNumber;
/*0AE*/ USHORT OSCSDVersion;
/*0B0*/ ULONG OSPlatformId;
/*0B4*/ ULONG ImageSubsystem;
/*0B8*/ ULONG ImageSubsystemMajorVersion;
/*0BC*/ ULONG ImageSubsystemMinorVersion;
/*0C0*/ ULONG ImageProcessAffinityMask;
/*0C4*/ ULONG GdiHandleBuffer[0x22];
/*14C*/ ULONG PostProcessInitRoutine;
/*150*/ ULONG TlsExpansionBitmap;
/*154*/ UCHAR TlsExpansionBitmapBits[0x80];
/*1D4*/ ULONG SessionId;
} PEB, *PPEB;

看到BeingDebuged了没,正好在+2位置。

还有一个是NtGlobal标志位也能判断是否被调试,它同样也在PEB中68位置

bool PebNtGlobalFlagsApproach()
{
       int result = 0;

       __asm
       {
                      // 进程的PEB
              mov eax, fs:[30h]
                          // 控制堆操作函数的工作方式的标志位
              mov eax, [eax + 68h]
                          // 操作系统会加上这些标志位FLG_HEAP_ENABLE_TAIL_CHECK, 
                          // FLG_HEAP_ENABLE_FREE_CHECK and FLG_HEAP_VALIDATE_PARAMETERS,
                          // 它们的并集就是x70
                          //
                          // 下面的代码相当于C/C++的
                          //     eax = eax & 0x70
              and eax, 0x70
              mov result, eax
       }
}

还有一种是检测堆中ForceFlag标志位,目前还没有研究懂,网上提供代码还没有成功实现,再研究下再说吧。

今天的最后一种是Anti int3,这个大家使用调试器的时候,经常遇到的简单的终端就是他了,修改函数的最前面的代码为0xCC,这样运行到该代码就会遇到异常,然后会被调试器接管~~,而我们就可以检测搜索关键函数头部是否被修改,(突然感觉很像HOOK)目前检测前0x100字节吧。

bool DetectFuncBreakpoints()
{
	BOOL bFoundOD;
	bFoundOD = FALSE;
	DWORD dwAddr;
	dwAddr = (DWORD)GetProcAddress(LoadLibrary(L"user32.dll"),L"MessageBoxA"); //将FARPROC类型转换成DWORD
	__asm
	{
			cld   //检测代码开始
			mov     edi, dwAddr
			mov     ecx, 100 // 100bytes
			mov     al, 0CCH  // 字母前面必须有0
			repne   scasb
			jnz     ODNotFound
			mov bFoundOD, 1
		ODNotFound:
	}
	return bFoundOD;
}

昨日在大赛中遇到的一个貌似利用SetTimer调用SetWindowText,将获取到文本框的内容放在全局变量中。这样一旦下断,程序就会不断的断在那里,根本跑步起来,自己稍微写了下,感觉还存在些逻辑漏洞。

void CCrackMeDlg::OnEnChangeEdit1()
{
	// TODO:  如果该控件是 RICHEDIT 控件,它将不
	// 发送此通知,除非重写 CDialogEx::OnInitDialog()
	// 函数并调用 CRichEditCtrl().SetEventMask(),
	// 同时将 ENM_CHANGE 标志“或”运算到掩码中。

	// TODO:  在此添加控件通知处理程序代码
	SetTimer(myTimer, 300, NULL);
}


void CCrackMeDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	if (nIDEvent == myTimer)
	{
		GetDlgItem(IDC_EDIT1)->GetWindowTextW(myChekKey);
	}
	CDialogEx::OnTimer(nIDEvent);
}


void CCrackMeDlg::OnEnKillfocusEdit1()
{
	// TODO:  在此添加控件通知处理程序代码
	KillTimer(myTimer);
}

只要文本框内容变动,计时器就被激活,失去焦点就会被杀死,突然感觉这样貌似就创建了N个Timer了,杀死的是最后一个,有空还得整理下—

至于硬件断点和其他诡异的调戏方式,等待第二篇文章放出吧~~ 2333 时候不早了,明天还要回学校呢~

页面下部广告

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

鲁ICP备17018668号-1