리버스 엔지니어가 사용하는 BP 의 종류에는 보통 세가지 - 하드웨어 / 메모리 / INT 3h - 가 있다.
동적 분석에 있어 리버서에게 필수적인 BP 는 실행 중인 코드 어디든 설치할 수 있으며 또한 실행을 멈출 수 있다.
BP를 적절히 활용함으로써 리버서는 API 를 분석할 수 있고 또한 원하는 메시지를 훨씬 쉽게 찾을 수 있다.
BP는 리버싱 과정에서 문자열을 검색하는 것 만큼이나 가장 많이 활용되며, MessageBox / VirtualAlloc, CreateDialog 와 같은 중요도가 높은 API 함수를 체크하는 데 활용할 수 있다.
INT 3
BP를 가장 흔히 표현하는 방법이며 IA-32 명령어에서 0xCC opcode로 표현하고 있다.
0xCC 를 0xCD 0x03 으로도 표현 가능한데 이 방법은 트러블이 발생할 수 있다.
아래의 소스를 통해 비교적 간단한 방법으로 이 BP 를 탐지할 수 있는데 오탐(false positive)이 발생할 수 있다.
#include <stdio.h>
#include <process.h>
#include <Windows.h>
unsigned __stdcall checkBP(void* );
int main()
{
HANDLE pThread;
unsigned int threadID;
pThread = (HANDLE) _beginthreadex(NULL, 0, &checkBP, NULL, 0 , &threadID);
while(1)
{
printf("BP test...\n");
Sleep(1000);
}
}
unsigned __stdcall checkBP(void* )
{
int k = 0;
unsigned char *pTmp = (unsigned char*)main;
while(1)
{
for (size_t i = 0; i < 0x10; i++)
{
if(pTmp[i] == 0xCC)
{
printf("BP detected at %d\n", i);
Sleep(1000);
}
}
}
}
뭐 눈에 너무 잘 띄니까 이렇게도 할 수 있다
if(pTmp[i] == 0xCC) ==> if(0x99 == (tmpchar ^ 0x55) )
의문점::
둘 다 while 문을 돌고 있는 상태에서 main 의 while 내부에 bp 를 걸면 탐지를 못 한다.
왜 그럴까
메모리 브레이크포인트
디버거는 GUARD PAGE 를 특정 지점에 설치하는 방식으로 메모리 BP를 형성한다.
그리고 이 BP 는 해당 메모리 페이지에 최초 1회 접근에 한해 동작한다.
예를 들면 PAGE_GUARD 가 설정되어 있는 특정 메모리의 페이지에 접근 했을 때에, 프로그램에 의해 컨트롤될 수 있는 STATUS_GUARD_PAGE_VIOLATION 예외가 발생한다. 메모리 BP 가 발생하는 정확히 순간을 체크할 수는 없지만, 디버거가 메모리 BP 를 생성하는 방식을 이용해서 현재 프로세스가 디버깅 중인지 아닌지를 판단할 수 있다.
먼저 동적 버퍼를 할당하고 버퍼에 RET 를 쓴다고 하자. 그리고 guard page 를 체크해 주고, 잠재적 리턴 어드레스를 스택에 push 한다. 우리가 체크한 페이지로 점프를 해 들어오게 되는데, 올리와 같은 디버거 아래에서 돌아가고 있다면 우리는 RET 명령어를 건드릴 것이고 페이지로 점프하기 전에 스택에 push 해 두었던 주소로 리턴될 것이다. 만약 그렇지 않았다면 STATUS_GUARD_PAGE_VIOLATION 예외가 발생될 것이며, 올리에 의해 디버깅 상태가 아니었음을 알 수 있다.
#include <Windows.h>
#include <stdio.h>
int main()
{
VOID* pAllocation=NULL;
char* pMem=NULL;
DWORD OldProtect;
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
pAllocation = VirtualAlloc(NULL, sysinfo.dwPageSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if(pAllocation ==NULL)
printf("VirtualAlloc failed\n");
pMem = (char*) pAllocation;
*pMem = 0xC3;
if(VirtualProtect(pAllocation, sysinfo.dwPageSize,
PAGE_EXECUTE_READWRITE | PAGE_GUARD,
&OldProtect) == 0)
{
printf("Error Code : %d", GetLastError());
printf("VirtualProtect failed\n");
return false;
}
__try
{
__asm
{
mov eax, pAllocation
push debugged
jmp eax
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
printf("exception occured and no debugger was detected\n");
VirtualFree(pAllocation, NULL, MEM_RESERVE);
return false;
}
__asm{debugged:}
printf("debugger detected\n");
return false;
}
하드웨어 BP
하드웨어 브레이크포인트는 인텔 프로세서 아키텍처에서 지원하는 기술이며, Dr0 - Dr7 이라고 이름 붙어있는 레지스터에 의해 컨트롤된다. Dr0 부터 Dr3 는 실제 BP를 동작하도록 하는 32비트의 레지스터이며, Dr4/5 는 다른 레지스터를 디버깅하기 위해서 예약되어 있는 레지스터이고, Dr6 과 7은 브레이크포인트의 행동을 컨트롤하기 위해 사용되는 레지스터이다. Dr6 과 7 레지스터가 어떻게 BP 의 행위에 영향을 미치는지 설명한 문서는 굉장히 많지만, 흥미가 있는 사람이라면 IA32 매뉴얼 Volume 3B (System programming guide for an in-depth explanation of how the registers work) 는 반드시 읽어보아야 한다.
하드웨어 BP를 탐지하고 제거하기 위해 GetThreadContext 와 SetThreadContext 함수를 활용할 수 있다. 그게 아니면 SEH 를 사용할 수도 있다.
#include <Windows.h>
#include <process.h>
#include <stdio.h>
unsigned __stdcall hwbpdetect(void*);
int main()
{
HANDLE pThread=NULL;
unsigned int threadID;
pThread = (HANDLE) _beginthreadex(NULL, 0, &hwbpdetect, NULL, 0, &threadID);
while(1)
{
printf("main thread\n");
Sleep(1000);
}
}
unsigned __stdcall hwbpdetect(void* arg)
{
unsigned int Numbp;
CONTEXT ctx;
while(1)
{
Numbp = 0;
ZeroMemory(&ctx, sizeof(CONTEXT));
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
HANDLE hThread = GetCurrentThread();
if(GetThreadContext(hThread, &ctx)==0){
printf("GetThreadContext Error\n");
}
if(ctx.Dr0 != 0) Numbp++;
if(ctx.Dr1 != 0) Numbp++;
if(ctx.Dr2 != 0) Numbp++;
if(ctx.Dr3 != 0) Numbp++;
printf("hwbpdetect thread - numbp : %d\n", Numbp);
Sleep(300);
}
}
디버그 레지스터를 조작할 수 있는 SEH 는 안티 리버싱 프로그램에서 흔히 볼 수 있다. 간단한 ASM 으로 만들 수 있다.
; 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.
VS 08 릴리즈 빌드 컴파일 시에는 SEH에서 쉘코드 등에 의해 발생할 수 있는 익스플로잇을 방지하기 위해 SAFESEH 라는 플래그가 세팅되어 나간다.
; 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
이런 몇몇 정보들은 SEH 핸들러가 안전한 예외 처리 핸들러를 가진 테이블을 추가한다는 것을 의미한다. 릴리즈 빌드에서 우리가 만든 핸들러가 호출되지 않는다면 이런 문제들이 있을 수 있음을 참고하라.
타이밍 어택
RDTSC
Timing Consuming
'Research > Reverse' 카테고리의 다른 글
About overflow flag (0) | 2016.02.16 |
---|---|
intager <-> string 변환과 속도 (0) | 2015.08.10 |
ARM 에서는 current instruction 과 PC 가 같지 않다. (0) | 2015.01.09 |
olly/ida에서의 HW Bp 처리방식 차이 (0) | 2014.07.03 |
[써볼만한 주제] unlikly 결과 어셈블리는 ? (0) | 2014.03.31 |