티스토리 뷰
Anti Debug
https://www.apriorit.com/dev-blog/367-anti-reverse-engineering-protection-techniques-to-use-before-releasing-software
해당 안티 디버깅 관련 자료들을 읽고 한글로 번역하고 도움될만한 내용들 끄적여본다.
Who this article is intended for
안티 리버싱 기술과 안티 디버깅 기술에 관심있는 소프트웨어 개발자, 리버스 엔지니어들을 위한 자료.
어셈 지식, windbg
사용법, api
를 이용한 윈도우 개발 경험이 필요할 수 도있다.
explanatory notes
잘 알지못하는 개념들을 적어두었다.
PEB (Process Environment Block)
PEB
는 Windows operating system
내부에서 사용되는 구조체이다.
x64 / x32
에 따라 구조체 포인터를 가져오는 방법이 다르다.
// Current PEB for 64bit and 32bit processes accordingly
PVOID GetPEB()
{
#ifdef _WIN64
return (PVOID)__readgsqword(0x0C * sizeof(PVOID));
#else
return (PVOID)__readfsdword(0x0C * sizeof(PVOID));
#endif
}
-
windbg
에서!peb
명령어를 통해서PEB
의 내용을 복사해올 수 있다. -
PEB는 전역 문맥, 시작 파라미터, 프로그램 이미지 로더를 위한 데이터 구조체, 프로그램 이미지 베이스 주소 그리고 동기화 객체들을 포함하는, 프로세스 전체에 적용되는 데이터 구조체를 담고 있다.
- wiki 백과
WOW64
WOW64
는 Windows on Windows 64-bit
. 즉 운영체제의 하위시스템으로, 모든 64bit
버전의 Windows
에서 32bit
응용 프로그램들이 돌아가도록 도와주는 매커니즘?? 이다.
Anti-debugging method introduction
안티디버깅에 관한 다양한 기술들을 설명.
소프트웨어를 reverse engineering
으로부터 완전히 보호하는것은 불가능.
안티리버싱의 목표는 가능한 다양한 작업, 프로세스들을 최대한 복잡하게 만드는것.
IsDebuggerPresent
가장 간단한 안티디버깅 기법으로는 IsDebuggerPresent
함수를 호출하는 것.
해당 함수는 함수를 호출하는 프로세스가 user-mode
디버거에 의해 디버깅 되고있다면 보호하는 역할을 함.
Example
int main()
{
if (IsDebuggerPresent())
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
return 0;
}
IsDebuggerPresent
함수안에는 다음과 같은 루틴이 존재하는데, PEB (Process Environment Block)
구조체에서 BeingDebugged
필드 ( offset +2 )를 검사한다. ( debugged : 1 / not : 0
)
#######################################################################################################
# x32
0:000< u kernelbase!IsDebuggerPresent L3
KERNELBASE!IsDebuggerPresent:
751ca8d0 64a130000000 mov eax,dword ptr fs:[00000030h]
751ca8d6 0fb64002 movzx eax,byte ptr [eax+2]
751ca8da c3 ret
#######################################################################################################
# x64
0:000< u kernelbase!IsDebuggerPresent L3
KERNELBASE!IsDebuggerPresent:
00007ffc ab6c1aa0 65488b042560000000 mov rax,qword ptr gs:[60h]
00007ffc ab6c1aa9 0fb64002 movzx eax,byte ptr [rax+2]
00007ffc ab6c1aad c3 ret
PEB structure
0:000< dt _PEB
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
How to bypass the IsDebuggerPresent
check
단순하게 BeingDebugged
검사코드가 실행되기전에 return
값을 0으로 설정하도록 함.
DLL
주입을 통해서도 가능함.
- 그냥 직접 분기문 가기전에 패치해서 우회하면 될듯?
TLS Callback
main
함수에 안티 디버깅 루틴을 작성하는 것은 좋지 않음. 우회하기 매우 쉽기 때문임.
CRT Library
를 사용한다면 main Thread
는 main
함수를 실행하기전에 특정 Call Stack
을 형성하기때문에
TLS callback
안에서 디버깅 체크를 하는것이 매우 좋음.
Example
#pragma section(".CRT$XLY", long, read)
__declspec(thread) int var = 0xDEADBEEF;
VOID NTAnopPI TlsCallback(PVOID DllHandle, DWORD Reason, VOID Reserved)
{
var = 0xB15BADB0; // Required for TLS Callback call
if (IsDebuggerPresent())
{
MessageBoxA(NULL, "Stop debugging program!", "Error", MB_OK | MB_ICONERROR);
TerminateProcess(GetCurrentProcess(), 0xBABEFACE);
}
}
__declspec(allocate(".CRT$XLY"))PIMAGE_TLS_CALLBACK g_tlsCallback = TlsCallback;
TLS callback
위치에서 디버깅 체크를 하는 예제이다.
NtGlobalFlag
Windows NT
에서, 전역변수 NtGlobalFLag
에 플래그집합이 위치해있다.
부팅시, NtGlobalFlag
전역 시스템변수는 시스템 레지스트리 키값으로 초기화된다.
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\GlobalFlag]
이 변수값은 System Trace
, Debugging
그리고 제어에 사용된다.
SDK
에는 전역 플래그 값을 편집 할 수 있는 gflags
유틸리티가 포함되어 있다.
PEB
구조체 또한 NtGlobalFlag
영역을 포함하는데, 해당 비트 구조는 NtGlobalFlag
전역변수에 해당하지 않는다.
- 쉽게말해서, 기본적으로
NtGlobalFlag
는windows NT
에서 사용되는 플래그 집합이며. PEB
구조체 또한NtGlobalFLag
필드가 존재하지만 필드값은 해당 프로세스에만 영향을 줄뿐, 윈도우 전역에는 영향 X- 디버깅 중이라면
NtFlobalFlag
필드에 필드갑이 설정되고, SDK
에서 해당 플래그 값을 편집할 수 있는gflags
유틸리티가 포함되어 있다.
틀린 내용이 있을 수 도있습니다. 지적 환영입니다 :)
즉 디버깅중에는, NtGlobalFlag
필드에 플래그가 설정된다
FLG_HEAP_ENABLE_TAIL_CHECK (0x10)
FLG_HEAP_ENABLE_FREE_CHECK (0x20)
FLG_HEAP_VALIDATE_PARAMETERS (0x40)
NtGlobalFlag
필드를 확인하면 디버깅중인지 확인할 수 있다.
오프셋은 다음과 같다.
#######################################################################################################
# x32
0:000> dt _PEB NtGlobalFlag @$peb
ntdll!_PEB
+0x068 NtGlobalFlag : 0x70
#######################################################################################################
# x64
0:000> dt _PEB NtGlobalFlag @$peb
ntdll!_PEB
+0x0bc NtGlobalFlag : 0x70
Example
#define FLG_HEAP_ENABLE_TAIL_CHECK 0x10
#define FLG_HEAP_ENABLE_FREE_CHECK 0x20
#define FLG_HEAP_VALIDATE_PARAMETERS 0x40
#define NT_GLOBAL_FLAG_DEBUGGED (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS)
void CheckNtGlobalFlag()
{
PVOID pPeb = GetPEB();
PVOID pPeb64 = GetPEB64();
DWORD offsetNtGlobalFlag = 0;
#ifdef _WIN64
offsetNtGlobalFlag = 0xBC;
#else
offsetNtGlobalFlag = 0x68;
#endif
DWORD NtGlobalFlag = *(PDWORD)((PBYTE)pPeb + offsetNtGlobalFlag);
if (NtGlobalFlag & NT_GLOBAL_FLAG_DEBUGGED)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
if (pPeb64)
{
DWORD NtGlobalFlagWow64 = *(PDWORD)((PBYTE)pPeb64 + 0xBC);
if (NtGlobalFlagWow64 & NT_GLOBAL_FLAG_DEBUGGED)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
}
NtGlobalFlag
를 검사해서 안티디버깅을 하는 예제이다.
How to bypass the NtGlobalFlag check
이거도 마찬가지로 안티디버깅 루틴을 거치기전에 PEB
구조필드를 0으로 설정하면 된다.