실전 STL 플밍.. 소스 참고..
'C/C++언어 > STL' 카테고리의 다른 글
STL std::string에서 쓸 수 있는 Replace All 함수 (0) | 2010.07.04 |
---|---|
STL 컨테이너(map, multimap) (0) | 2010.07.04 |
STL 강좌입니다. (0) | 2007.09.06 |
"C++ STL 실전 프로그래밍 예제 소스"중 일부 (0) | 2007.03.20 |
STL std::string에서 쓸 수 있는 Replace All 함수 (0) | 2010.07.04 |
---|---|
STL 컨테이너(map, multimap) (0) | 2010.07.04 |
STL 강좌입니다. (0) | 2007.09.06 |
"C++ STL 실전 프로그래밍 예제 소스"중 일부 (0) | 2007.03.20 |
쉬프트 연산과 곱셈
비트를 이동시키는 쉬프트 연산은 곱셈과 나눗셈의 대용으로 사용할 수 있다. 다음 예제를 실행해 보아라.
예 제 : shiftmulti |
#include <Turboc.h>
void main()
{
int i;
printf("정수를 입력하세요 : ");
scanf("%d",&i);
printf("결과=%d\n",i << 1);
}
정수 i를 입력받은 후 왼쪽으로 한칸 쉬프트한 값을 출력했다. 실행해 보면 알겠지만 입력한 수의 정확하게 2배되는 값이 출력된다. 5를 입력하면 10, 100을 입력하면 200이 출력될 것이다. 정수를 왼쪽으로 한칸 쉬프트하면 두배가 되며 오른쪽으로 한칸 쉬프트하면 절반이 되는데 어째서 그런지 보자. 입력값이 5였다고 가정하면 이 값은 이진수로 0101인데 이 값을 10진수로 바꾸는 공식은 다음과 같다.
1*22+0*21+1*20 = 5
이진수의 각 자리수는 2의 거듭승에 해당하는 값을 표현한다. 그런데 비트를 왼쪽으로 한칸 이동시키면 모든 자리수의 지수가 1 올라가기 때문에 2배가 된다. 쉬프트한 결과는 1010이며 이 값은 다음과 같이 10진수로 바꿀 수 있다.
1*23+0*22+1*21+0*20 = 10
그래서 왼쪽으로 한칸 쉬프트하면 두배가 되는 것이다. 단, 용량 한계를 넘어서는 값은 잘려 나가는데 이는 어쩔 수 없다. 예를 들어 16비트 정수 40000을 왼쪽으로 쉬프트하면 80000이 되는데 16비트로는 이 값을 표현할 수 없으므로 제일 오른쪽의 1이 밀려나고 결과는 14464가 된다. 이 값은 80000-65536인데 밀려나서 버려진 오른쪽 비트의 값이 65536이기 때문이다. 32비트의 int형을 쓰면 웬만큼 큰 수라도 용량의 한계를 넘지 않을 것이다.
왼쪽으로 쉬프트하는 연산이 값을 두배로 만드는데 비해 오른쪽으로 쉬프트하는 연산은 값을 절반으로 만든다. 모든 자리수의 지수가 1 감소하기 때문이다. 이진수 1100을 오른쪽으로 쉬프트하면 0110이 되는데 이 값은 처음값 12의 절반인 6이다. 동일한 원리이므로 따로 그림까지 그릴 필요는 없을 것 같다.
오른쪽 쉬프트는 자리 넘침이 발생하지 않지만 오른쪽으로 밀려나 사라지는 값만큼 오차가 생길 수는 있다. 짝수를 밀면 정확하게 절반이 되지만 홀수를 밀면 제일 오른쪽에 있던 1이 밀려 나므로 실제값보다 0.5더 작은 값이 계산된다. 예를 들어 이진수 0111(7)을 오른쪽으로 밀면 b0가 밀려나고 0011(3)이 된다. 정확하게 계산하자면 3.5가 되어야 하지만 비트는 정수의 세계이기 때문에 밀려난 0.5는 사라지게 된다.
좌우 쉬프트 연산이 곱셈과 나눗셈 대용으로 사용될 수 있다는 것이 도저히 이해가 안간다면 아마도 2진수에 익숙하지 않아서 그럴 것이다. 그렇다면 10진수로 이 현상을 설명해 보자. 10진수 1234에서 각 자리수는 10의 거듭승에 해당하는 자리값을 가지고 있다. 1은 103인 1000자리에 있고 2는 102인 100자리에 있고 3은 101인 10자리에 있는 셈이다.
1234=1*103+2*102+3*101+4*100
이 값을 왼쪽으로 쉬프트하면 각 자리수의 지수가 1증가한 12340이 된다.
12340=1*104+2*103+3*102+4*101+0*100
천자리는 만자리로 올라가고 백자리는 천자리로 올라가므로 모든 값이 10배가 되어 전체값도 10배가 되는 것이다. 반대로 오른쪽으로 밀면 모든 자리수가 10배 감소하므로 10분의 1로 값이 줄어든다. 1234를 오른쪽으로 밀면 123이 되고 일자리에 있던 4는 밀려나 사라진다. 물론 실수 차원이라면 123.4가 되겠지만 말이다.
좀 더 쉽게 설명하자면 임의의 십진수가 있을 때 뒤에 0을 하나 더 붙이면 10배가 되고 제일 뒷자리를 제거해 버리면 약간의 오차를 제외하고 1/10로 그 값이 줄어든다. 초등학생들도(심지어 일부 유치원 아동들까지도) 아는 간단한 원리이며 일상 생활에서도 흔히 있는 연산이므로 너무 너무 당연하게 생각될 것이다. 10진수에서 쉬프트 연산이 10배씩 증감하는 것처럼 2진수에서는 쉬프트 연산이 2배씩 증감하는 것이다.
왼쪽으로 한칸 밀면 두 배가 된다. 그럼 이 수를 다시 왼쪽으로 한칸 밀면 2배의 2배, 즉 4배가 될 것이다. 다시 한 번 더 밀면 8배가 된다. 자, 그럼 이제 쉬프트 연산과 곱셈 연산의 관계를 일반화해 보자.
a << b == a * 2b
a를 b만큼 왼쪽으로 민다는 것은 a를 2의 b승만큼 곱하는 것과 같다. 이 공식은 b가 음수일 때도 똑같이 적용된다. a를 -1만큼 왼쪽으로(즉 오른쪽으로 한칸) 밀면 2-1를 곱하는(즉 2로 나누는)것과 같다. 그래서 곱셈 대신에 쉬프트 연산을 사용할 수 있는데 이 두 연산은 엄청난 속도 차이가 있다. 비트를 이동시키는 것과 일정 횟수 더하기를 반복하는 것은 CPU 입장에서 보면 완전히 다른 작업이기 때문에 속도차가 무려 10배 정도 난다. 쉬프트 연산은 전혀 논리적이지 않으며 기계적이므로 기계가 하기에는 아주 쉬운 연산인 것이다.
즉 a*2를 한 번 할 시간이면 a << 1을 10번 정도 할 수 있다는 얘기다. 이렇게 속도차가 나기 때문에 핵심 게임 엔진이나 시스템 프로그래머들은 곱셈 대신 쉬프트 연산을 즐겨 사용한다. 중요한 루프에서 곱셈을 하느냐 쉬프트 연산을 하느냐에 따라 프로그램의 성능에 확연한 차이가 나기 때문에 쉬프트 연산은 도저히 피할 수 없는 달콤한 유혹인 것이다.
쉬프트 연산이 곱셈에 비해 불리한 점은 2의 거듭승에 대해서만 곱셈이 가능하다는 점이다. 2배, 4배, 8배, 16배 등만 할 수 있으며 3배, 17배 이런 연산은 할 수 없다. 그러나 쉬프트 연산과 덧셈, 뺄셈을 잘 조합하면 이런 연산이 가능해지기도 한다.
3배 : a << 1 + a;
9배 : a << 3 + a;
15배 : a << 4 - a;
60배 : a << 6 - a << 2;
특히 제일 마지막의 쉬프트 연산으로 60배를 하는 코드는 아주 기발한 응용예이며 감탄을 금하기 어려운 예술 코드라고 할 수 있다. 정밀하게 측정해 보면 이런 연산들이 곱셈보다 수배~수십배 더 빠르다. 보통의 경우라면 일반적인 곱셈을 하는 것이 소스를 유지하기에 편리하지만 속도가 지극히 중요하다면 곱셈보다는 가급적이면 쉬프트 연산을 사용하는 것이 좋다.
[출처] 쉬프트 연산과 곱셈|작성자 청주콜라
고정 소수점 (0) | 2008.05.09 |
---|---|
각 언어별 3중 포인터 (0) | 2008.05.06 |
[리눅스프로그래밍] makefile (1) | 2008.03.07 |
ASSERT(), VERIFY(), TRACE() (0) | 2008.03.04 |
템플릿 사용한 max 만들기 (0) | 2008.03.03 |
VC++은 파일 하나를 컴파일 할때 INCLUDE해야 할 파일이 너무 많아서 컴파일이 늦어지는 것을 막기위해 precompiled header라는 것을 만들었습니다.
이 헤더 파일(보통 stdafx.h)을 include한 .cpp는 미리 만들어진 precompiled header를 기본으로 사용하게 되어 있습니다.
하지만 다른 컴파일러로 만들어진 라이브러리(소스)에는 이런 것이 없겠죠.
그럼에도 VC++ 컴파일러가 자기는 precompiled header가 필요하다고 저런 이상한 메시지를 냅니다.
이런 경우.. 해당하는 모든 .cpp의 맨 첫줄에 #include "stdafx.h"를 넣어주거나...
해당하는 파일의 precomiled header 플래그를 꺼주면 됩니다.
끄는 방법은 project/setting 에서 c/c++ tab의 precomiled header 관련 category에서 사용하지 않음을 선택하면 됩니다
출처 : http://cafe.naver.com/hopistudy/71
소스 코드 분석 솔루션, 파수닷컴 Sparrow (0) | 2010.07.04 |
---|---|
Vista 에서 Visual Basic 6으로 작성된 프로그램을 돌릴때 comdlg32.ocx 에러 뜰때 (0) | 2008.09.21 |
Visaul Studio .net의 간간히 나오는 스크립트 에러메시지 잡기 (0) | 2008.01.29 |
다중패턴검색 알고리즘 (0) | 2014.07.14 |
---|---|
NPC 인공지능 처리 기본 구성 (0) | 2010.07.04 |
작성자 : Dual5651 (dual@null2root.org) in Null@Root 소개 이번 글에서는 그 시점에서 알게된 Anti GH 프로그램이 갖추어야할 기능이 무엇 이며, 그 기능의 구현에 필요한 코드와 그 기능의 존재하는 약점등에 대해서 다루어 보고자 합니다. 이 글의 구성은 다음과 같습니다. 1. Anti Game Hacking 프로그램에 필요한 기능들.
2. Anti Game Hacking Program 구현에 필요한 코드들. 1. Anti Game Hacking 프로그램에 필요한 기능들. Anti GH 프로그램은 어떤 기능들이 필요할까요? 1. (Binary /Data File) Packing / Encrypting 2. GH Program Detecting 3. AutoPlay Blocking 4. Message Hooking Blocking 5. Unauthorized memory access Blocking 6. Debugging Blocking 7. SpeedHack Blocking 8. Integrity Checking 9. System descriptor restoring 첫번째로 필요한 기능으로써, 실행파일 및 데이터 파일의 압축 또는 암호화를 들 수 있습니다. 이 부분에 대해선 다른 의견을 가지고 있는 분들도 많은거 같습니다. 이런 분들의 주장에 근거로는 패킹 및 인크라입팅에 의존하면, 프로그램 자체의 Secure Coding이 약해지고, 대부분 이러한 패킹 및 인크라입팅은 범용적으로 사용되는 상용프로그램일 가능성이 높음으로, 풀릴(뚫릴 - 그만큼 공략하는 사람도 많기 때문에) 가능성도 높으며, 이로 인한 결과는 상상이상으로 치명적일 것이기 떄문입니다. 그럼에도 불구하고, 최근 실행파일의 패킹을 하지 않은 프로그램을 더 찾기 힘들만큼 많이 사용되어 지고 있으며, 또 어느정도 첫번째 방어선으로써 기대되어 지는 기능을 하여주고 있습니다. 그렇다면 게임의 데이터 파일들에 대한 패킹 및 인크라입팅은 왜 필요한 것일까요? 최근 Anti GH 프로그램의 도입으로 Memory Hacking이 힘들어지자, 최근 공격자들은 게임의 데이터파일을 조작하는 방식의(고전적이라면 고전적인 방법의) 해킹을 다시 시도하기 시작하였습니다. 실제 2007년도 국내의 어떤 게임에서는 해당 방식에 의한 GH이 이루어지기도 하였습니다. 두번째로 필요한 기능으로써, 게임해킹 프로그램의 감지를 들 수 있습니다. 최근 가장 많이 쓰이는 GH방식으로 Memory Hacking이 있습니다. 이러한 MH(Memory Hacking)은 공격자 입장에서 Generic Game Hacking Tool (ex: CheatEngine,Tsearch and so on...)로 우선 대상 Game에 대한 분석을 하고 그 후 그 게임만을 대상으로 하는 게임 트레이너가 나오게 됩니다. 즉, 범용 게임 해킹툴의 완전한 차단은 특정 게임 대상 트레이너의 제작을 막는 방법 이기도 합니다. 물런 이는 반드시 그러한 것은 아니며, 거의 근접한 %의 역활을 할 수 있습니다. 감지하는 방법으로써는 첫번째로 패턴 매칭이 있습니다. 패턴 매칭이란 특정한 GH 프로그램에서 발견되는 문자열이나, 코드배열등을 Anti GH프로그램에서 모든 프로세스에서 찾아 보고, 있다면 GH 프로그램으로 간주하는 방식입니다. 두번째로 Finding Named Object기법이 있습니다. 예를들어, hFileMap = CreateFileMapping((HANDLE)INVALID_HANDLE_VALUE,NULL,
PAGE_READWRITE,0,dwSize,"DUALMEM"); 의 경우 처럼, GH 프로그램이 사용되는 시스템 전역에 공유되는 파일맵의 이름이라던지, BOOL PatternSearchFromAllProcesses()
{ char StringDataBase[][30] = {"CheatEngine", "AutoPlay", "GameHack", "Memory Search", "TSearch"}; char szProcess[MAX_PATH] = {0,}; DWORD ProcessesID[1024]; char WarningString[MAX_PATH] = "게임 해킹 프로그램 발견 되었습니다.\n게임이 종료 됩니다.\n"; MEMORY_BASIC_INFORMATION struct_mbi; SYSTEM_INFO struct_si; HMODULE hMod; DWORD dbNeeded,dbNeeded2; HANDLE hProcess; LPVOID newbuf; LPVOID p; ULONG ret; int cnt; GetSystemInfo(&struct_si); //Getting Information to get max,min address printf("StartAdr : 0x%x, EndAdr : 0x%x\n", for(cnt=0; ;cnt++) //Counting Pattern
char szWindowDataBase[][30] = {"CheatEngine",
"AutoPlay", "GameHack", "Memory Search", "TSearch"}; char szClassDataBase[][30] = {"CheatEngine", int cnt,cnt2; BOOL CALLBACK EnumWinProc(HWND hwnd,LPARAM lparam) for(i = 0; i < cnt; i++) BOOL FindBadWindowOrClass() for(cnt2 = 0; ;cnt2++) //Counting Pattern EnumWindows(EnumWinProc,NULL); 프로세스 이름의 경우 EnumWindow 콜백에서 GetThreadProcessId() 함수를 한 후,
먼저 위와 같이 Lower panel view를 Handles로 해줍니다. 위 그림에서 보면, TSearch는 3가지의 독특한 Event를 만드는 것을 알 수 있습니다. 즉, 다음 3가지 중 하나를 선택하여 해당 이벤트가 존재하는지를 확인한다면, TSearch의 실행여부를 확인할 수 있습니다. 코드는 다음과 같습니다. BOOL CheckByEventName()
{ char szEventDataBase[][30] = { "User stopped search", "Debugger Loaded", "TSearch.ServerLoaded" }; HANDLE hEvent; for(int cnt = 0; ;cnt++) //Counting Pattern for(int i = 0; i < cnt; i++) return TRUE; } 뮤텍스를 이용한 확인 방법의 코드는 다음과 같습니다. BOOL CheckByMutexName()
{ char szMutexDataBase[][30] = { "TSearch", "CheatEngine", "GameHack" }; HANDLE hEvent; for(int cnt = 0; ;cnt++) //Counting Pattern for(int i = 0; i < cnt; i++) return TRUE; } 파일맵을 이용한 방법의 코드는 다음과 같습니다. BOOL CheckByFileMapName()
{ char szFileMapDataBase[][30] = {"DUALMEM", "TSearch", "CheatEngine", "GameHack" }; HANDLE hFileMap; LPVOID pMapFile; for(int cnt = 0; ;cnt++) //Counting Pattern for(int i = 0; i < cnt; i++) return TRUE; } 레지스트리, 파일로 확인하는 방법은 어떤 레지스트리나 파일을 쓰는지는 Process Explorer를 확인하는 것으로 동일하고, 확인은 레지스트리나 파일을 열 떄, 이미 존재하는지 여부만 확인해주면 됨으로 별도의 코드는 첨부하지 않겠습니다. 윈도우즈에 로드되어 있는 드라이버의 목록은 ntdll.dll의 ZwQuerySystemInformation() 를 이용하여 구할 수 있습니다. 참고적으로 위의 핸들목록을 출력하는 기능은 정덕영님의 저서 'Windows 구조와 원리 그리고 Codes'에 나온 코드인 ListHandles라는 코드도 똑같은 기능을 합니다. 코드는 다음과 같습니다. #include "stdafx.h"
#include "windows.h" #include <stdio.h> #include "nativeAPI.h" BOOL EnablePrivilege(PCSTR name) HANDLE hToken; AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof priv, 0, 0); CloseHandle(hToken);
//2. Get process handle //3. Get Handle's Information if(DuplicateHandle(hProcess, (HANDLE)aHandles[i].Handle, ZwQueryObject(hObject, ObjectBasicInformation, &obi, sizeof(obi), &n); ZwQueryObject(hObject, ObjectTypeInformation, pOti, nTypeName, &nTypeName); pOni = (POBJECT_NAME_INFORMATION)malloc(nObjectName); printf("\n"); return 0; } 세번쨰로 자동 플레이 차단은 에플리케이션 방식의 오토플레이라면 유저레벨에서 막는방법과 커널레벨에서 막는 방법으로 나뉠 수 있습니다. 사실 후킹하는 시점이 다를 뿐, 실제적으로 차단해야할 API들은 같습니다. 유저레벨에서는 모든 프로세스에 Dll을 인젝션 한 후, 해당 Dll에서 GetPixel(), PostMessageA(), PostMessageW(), SendInput(), SendMessageA(), SendMessageW(), SetCursorPos(), keybd_event(), mouse_event() 등의 함수를 차단하여 주어야 합니다. 커널레벨에서는 KeServiceDescriptorTableShadow에 있는 SendInput(), NtUserQueryWindow(), NtUserBuildHwndList(), NtUserFindWindowEx(), NtUserGetForegroundWindow(), GetDC(), GetWindowDC() 등의 함수를 후킹해서 처리해 주어야 합니다. 그래픽 관련 함수를 후킹하는 이유는 최근 오토플레이들은 단순한 메크로가 아닌, 픽셀 정보를 읽어온 후, 그에 interact하여 작동하는 방식을 많이 취하기 떄문입니다. 위의 함수들을 모두 후킹하여 처리하여 주면, 에플리케이션 방식의 메크로는 거의 차단된다고 보시면 됩니다. (커널레벨에서의 irp 발생 및 pesudo 코드 사용등 예외는 존재합니다.) 네번쨰로 메시지 후킹 차단은 커널단에서 비교적 강력하게 구현될 수 있는데, 메시지 후킹은 SetWindowHookEx()를 이용해서 이루어 지는데, 운영체제 내에서는 다음과 같은 구조체로 관리 되어 집니다. typedef struct _HOOK { ULONG hHook; ULONG cLockObj; PTHREADINFO pti; //THREADINFO of CurrentThread ULONG rpdesk; ULONG pSelf; struct _HOOK *phkNext; int iHook; //HOOK_TYPE ULONG offPfn; //Proc unsigned int flags; //Flags int ihmod; PTHREADINFO ptiHooked; //THREADINFO of TargetThread (if Flags=HF_GLOBAL,=0) PDESKTOP rpdesk; } HOOK, *PHOOK; TEB의 Win32ThreadInfo 구조체의 pDeskInfo에 aphkStart라는 배열이 훅을 관리하는데, 즉 이 배열을 주기적으로 0으로 초기화 시킴으로써 설치된 훅을 제거할수도 있으며 메시지 후킹을 차단할수도 있습니다. 다섯번쨰로 필요한 기능으로써, 허용되지 않은 메모리 접근차단은 유저레벨에서는 각 프로세스에 Dll을 인젝션 한 후, NtOpenProcess(), NtProtectVirtualMemory(), NtReadVirtualMemory(), NtWriteVirtualMemory(), ZwOpenProcess(), ZwProtect VirtualMemory(), ZwReadVirtualMemory(), ZwWriteVirtualMemory(), OpenProcess(), ReadProcessMemory(), VirtualProtect(), VirtualProtectEx(), WriteProcessMemory(), GetWindowThreadProcessId()등의 API를 후킹하여 주어야 합니다. 유저레벨에서의 후킹코드는 앞에서 다뤘던 코드에 조금의 수정을 가하면 되는 것임으로, 생략하도록 하겠습니다. 커널레벨에서의 후킹코드들은 다음과 같습니다. ZwOpenProcess Hook : NTSTATUS NewZwOpenProcess(
OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL ) { NTSTATUS rc; NTSTATUS rc2; CHAR Caller_Process_Name[PROCNAMELEN]; CHAR Target_Process_Name[PROCNAMELEN]; PEPROCESS Process; char *nameptr; GetProcessName( Caller_Process_Name ); //Get Process Name rc = ((ZWOPENPROCESS)(OldZwOpenProcess)) ( if(NT_SUCCESS(rc)) { rc2 = ObReferenceObjectByHandle( *ProcessHandle, PROCESS_ALL_ACCESS, NULL, KernelMode, (void *)&Process, NULL); if(NT_SUCCESS(rc2)) { nameptr = (PCHAR)Process + gProcessNameOffset; strncpy(Target_Process_Name, nameptr, NT_PROCNAMELEN); Target_Process_Name[NT_PROCNAMELEN] = 0; if(!strncmp(Target_Process_Name,MYPROCESS,strlen(MYPROCESS))) { ObDereferenceObject(Process); ZwClose(ProcessHandle); rc = STATUS_INVALID_HANDLE; ProcessHandle = 0; } } } } return rc; } ZwOpenProcess() 함수를 후킹하고 있다가, 만약 핸들을 오픈하고자 하는 프로세스가, 보호하는 프로세스라면 핸들을 닫고 잘못된 핸들이라고 값을 리턴합니다. ZwWriteVirtualMemory Hook : NTSTATUS NTAPI NewZwWriteVirtualMemory(
IN HANDLE hProcess, IN PVOID BaseAddress, IN PVOID Buffer, IN ULONG BytesToWrite, OUT PULONG BytesWritten ) { NTSTATUS rc; NTSTATUS rc2; CHAR Attack_Process_Name[PROCNAMELEN]; CHAR Target_Process_Name[PROCNAMELEN]; PEPROCESS Process; char *nameptr; GetProcessName( Caller_Process_Name ); rc2 = ObReferenceObjectByHandle( rc = ((ZWWRITEVIRTUALMEMORY)(OldZwWriteVirtualMemory)) (
NTSTATUS NTAPI NewZwReadVirtualMemory(
IN HANDLE hProcess, IN PVOID BaseAddress, OUT PVOID Buffer, IN ULONG BytesToRead, OUT PULONG BytesRead ) { NTSTATUS rc; NTSTATUS rc2; CHAR Caller_Process_Name[PROCNAMELEN]; CHAR Target_Process_Name[PROCNAMELEN]; PEPROCESS Process; char *nameptr; GetProcessName( Caller_Process_Name ); rc2 = ObReferenceObjectByHandle( 여섯번쨰로 디버깅 차단은 코드에 안티 디버깅 루틴을 삽입과 후킹을 사용합니다. if(IsDebuggerPresent())
{ OutputDebugString("Debugeed!!"); //디버깅 당하는 중일떄의 어떠한 처리 } 두번쨰 방법으로 PEB 구조체의 BeingDebuggged 값을 직접 조작하는 방법이 있다. BOOL Pesudo_IsDebuggerPresent()
{ BOOL Retval = 0; __asm { push eax mov eax,dword ptr fs:{0x18] mov eax,dword ptr ds:[eax+0x30] movzx eax,byte ptr ds:[eax+0x2] mov Retval,eax pop eax } return Retval; } 세번쨰 방법으로 CheckRemoteDebuggerPresent() 함수를 이용하는 방법이 있습니다. BOOL CheckDebugger(HANDLE hProcess)
{ * CheckRemoteDebuggerPresent()는 NTAPI의 ZwQueryInformationProcess()로 NTSTATUS NTAPI ZwQueryInformationProcess(HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength); 첫번쨰는 질의 하고자 하는 대상 프로세스의 핸들. BOOL CheckNtGlobalFlag()
{ BOOL Retval = 0; __asm { push eax mov eax,dword ptr fs:[0x30] mov eax,0x68 mov eax,dword ptr ds:[eax] cmp eax,0x70 pop eax jne NotDebuged mov Retval,1 NotDebugged : nop } return Retval; } 다섯번쨰 방법으로 Heap flags를 이용하는 방법이 있습니다. Heap의 상태가 디버그 mov eax, fs:[30h]
mov eax, [eax+18h] ;process heap mov eax, [eax+10h] ;heap flags test eax, eax jne @DebuggerDetected 여섯번쨰 방법으로 UnhandledExceptionFilter를 이용하는 방법이 있습니다. push @not_debugged
call SetUnhandledExceptionFilter xor eax, eax mov eax, dword [eax] ; trigger exception ;program terminated if debugged ;... @not_debugged: ;process the exception ;continue the execution ;... 일곱번째로 NtSetInformationThread를 이용하는 방법이 있습니다. ThreadInformationClass 가 0x11 (ThreadHideFromDebugger 상수)로 지정되고 Example:
push 0 push 0 push 11h ;ThreadHideFromDebugger push -2 call NtSetInformationThread ;thread detached if debugged ;... 여덟번쨰로 kernel32!CloseHandle and NtClose 를 이용하는 방법이 있습니다. push offset @not_debugged
push dword fs:[0] mov fs:[0], esp push 1234h ;invalid handle call CloseHandle ; if fall here, process is debugged ;... @not_debugged: ;... 아홉번쨰로 OutputDebugStringA를 이용하는 간단한 방법도 있습니다. xor eax, eax
push offset szHello call OutputDebugStringA cmp eax, 1 jne @DebuggerDetected ... 앞서 말한 방법들을 모두 TLS Callback으로써 등록하여 작동시킬수도 있습니다. #include "stdafx.h"
#include <windows.h> int main(int argc, char* argv[]) mov esi,offset StartAddressOfCheck Check_Loop: StartAddressOfCheck 로 부터 EndAddressOfCheck 까지의 명령어들의 합산값을 PMODULE_LIST GetListOfModules(PNTSTATUS pns)
{ ULONG ul_NeededSize; ULONG *pul_ModuleListAddress = NULL; NTSTATUS ns; PMODULE_LIST pml = NULL;
// Call it the first time to determine the size required // to store the information. ZwQuerySystemInformation(SystemModuleInformation, &ul_NeededSize, 0, &ul_NeededSize); pul_ModuleListAddress = (ULONG *) ExAllocatePool(PagedPool, ul_NeededSize);
if (!pul_ModuleListAddress) // ExAllocatePool failed. { if (pns != NULL)
*pns = STATUS_INSUFFICIENT_RESOURCES;
return (PMODULE_LIST) pul_ModuleListAddress; }
ns = ZwQuerySystemInformation(SystemModuleInformation, pul_ModuleListAddress, ul_NeededSize, 0); if (ns != STATUS_SUCCESS)// ZwQuerySystemInformation failed. { // Free allocated paged kernel memory. ExFreePool((PVOID) pul_ModuleListAddress); if (pns != NULL)
*pns = ns; return NULL; } pml = (PMODULE_LIST) pul_ModuleListAddress;
if (pns != NULL)
*pns = ns; return pml; } 위의 코드로 ntoskrnl.exe의 시작주소와 끝주소를 알 수 있으며, SSDT와 IDT를 NTSTATUS NTAPI NewZwQuerySystemInformation(
IN ULONG SystemInformationClass, IN PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength ) { NTSTATUS rc; CHAR Attack_Process_Name[PROCNAMELEN]; GetProcessName( Attack_Process_Name ); rc = ((ZWQUERYSYSTEMINFORMATION)(OldZwQuerySystemInformation)) ( if( NT_SUCCESS( rc ) && strncmp(Attack_Process_Name,ProcessName,NT_PROCNAMELEN-1)) if(5 == SystemInformationClass) while(curr) NtUserQueryWindow(), NtUserBuildHwndList(), NtUserFindWindowEx(), UINT_PTR NewNtUserQueryWindow(IN ULONG WindowHandle,IN ULONG TypeInformation)
{ ULONG WindowHandleProcessID; CHAR Attack_Process_Name[PROCNAMELEN]; CHAR Target_Process_Name[PROCNAMELEN]; PEPROCESS Process; char *nameptr; GetProcessName(Attack_Process_Name); if(strncmp(Attack_Process_Name,ProcessName,NT_PROCNAMELEN-1)) { WindowHandleProcessID = ((NTUSERQUERYWINDOW)(WINDOWSERVICEIDX(483)))(WindowHandle,0); if(PsLookupProcessByProcessId((HANDLE)WindowHandleProcessID,&Process) == STATUS_SUCCESS) { ObDereferenceObject(Process); nameptr = (PCHAR)Process + gProcessNameOffset; strncpy(Target_Process_Name, nameptr, NT_PROCNAMELEN); Target_Process_Name[NT_PROCNAMELEN] = 0; if(!strncmp(Target_Process_Name,ProcessName,NT_PROCNAMELEN-1) || WindowHandleProcessID == SaruenProcessID) { return 0; } } } return OldNtUserQueryWindow(WindowHandle,TypeInformation); } NTSTATUS NewNtUserBuildHwndList(IN HDESK hdesk, IN HWND hwndNext, IN ULONG fEnumChildren, IN DWORD idThread, IN UINT cHwndMax, OUT HWND *phwndFirst, OUT ULONG* pcHwndNeeded)
{ NTSTATUS result; CHAR Attack_Process_Name[PROCNAMELEN]; CHAR Target_Process_Name[PROCNAMELEN]; ULONG ProcessID; PEPROCESS Process; char *nameptr; ULONG i = 0,j; GetProcessName(Attack_Process_Name); if(strncmp(Attack_Process_Name,ProcessName,NT_PROCNAMELEN-1)) { if(fEnumChildren == 1) { ProcessID = OldNtUserQueryWindow((ULONG)hwndNext,0); if(PsLookupProcessByProcessId((HANDLE)ProcessID,&Process) == STATUS_SUCCESS) { ObDereferenceObject(Process); nameptr = (PCHAR)Process + gProcessNameOffset; strncpy(Target_Process_Name, nameptr, NT_PROCNAMELEN); Target_Process_Name[NT_PROCNAMELEN] = 0; if(!strncmp(Target_Process_Name,ProcessName,NT_PROCNAMELEN-1) || ProcessID == SaruenProcessID) { return STATUS_UNSUCCESSFUL; } } } result=((NTUSERBUILDHWNDLIST)(WINDOWSERVICEIDX(312)))(hdesk,hwndNext,fEnumChildren,idThread,cHwndMax,phwndFirst,pcHwndNeeded); if (result == STATUS_SUCCESS) { while (i<*pcHwndNeeded) { ProcessID=OldNtUserQueryWindow((ULONG)phwndFirst[i],0); if(PsLookupProcessByProcessId((HANDLE)ProcessID,&Process) == STATUS_SUCCESS) { nameptr = (PCHAR)Process + gProcessNameOffset; strncpy(Target_Process_Name, nameptr, NT_PROCNAMELEN); Target_Process_Name[NT_PROCNAMELEN] = 0; ObDereferenceObject(Process); if(!strncmp(Target_Process_Name,ProcessName,NT_PROCNAMELEN)) { for (j=i; j<(*pcHwndNeeded)-1; j++) phwndFirst[j]=phwndFirst[j+1]; phwndFirst[*pcHwndNeeded-1]=0; (*pcHwndNeeded)--; continue; } } i++; } } return result; } return OldNtUserBuildHwndList(hdesk,hwndNext,fEnumChildren,idThread,cHwndMax,phwndFirst,pcHwndNeeded); } ULONG NewNtUserFindWindowEx(IN HWND hwndParent, IN HWND hwndChild, IN PUNICODE_STRING pstrClassName OPTIONAL, IN PUNICODE_STRING pstrWindowName OPTIONAL, IN DWORD dwType) { ULONG result; ULONG ProcessID; PEPROCESS Process; char *nameptr; CHAR Attack_Process_Name[PROCNAMELEN]; CHAR Target_Process_Name[PROCNAMELEN]; GetProcessName(Attack_Process_Name); if(!strncmp(Attack_Process_Name,ProcessName,NT_PROCNAMELEN-1)) { result=OldNtUserFindWindowEx(hwndParent,hwndChild,pstrClassName,pstrWindowName,dwType); return result; } result = ((NTUSERFINDWINDOWEX)(WINDOWSERVICEIDX(378)))(hwndParent,hwndChild,pstrClassName,pstrWindowName,dwType); if(strncmp(Attack_Process_Name,ProcessName,NT_PROCNAMELEN-1)) { ProcessID = OldNtUserQueryWindow(result,0); if(PsLookupProcessByProcessId((HANDLE)ProcessID,&Process) == STATUS_SUCCESS) { nameptr = (PCHAR)Process + gProcessNameOffset; strncpy(Target_Process_Name, nameptr, NT_PROCNAMELEN); Target_Process_Name[NT_PROCNAMELEN] = 0; if(!strncmp(Target_Process_Name,ProcessName,NT_PROCNAMELEN-1) || ProcessID == SaruenProcessID) { result=0; } ObDereferenceObject(Process); } } return result; } ULONG NewNtUserGetForegroundWindow(VOID)
{ ULONG result; ULONG ProcessID; PEPROCESS Process; char *nameptr; CHAR Attack_Process_Name[PROCNAMELEN]; CHAR Target_Process_Name[PROCNAMELEN]; GetProcessName(Attack_Process_Name); if(!strncmp(Attack_Process_Name,ProcessName,NT_PROCNAMELEN-1)) { result=OldNtUserGetForegroundWindow(); return result; } result = ((NTUSERGETFOREGROUNDWINDOW)(WINDOWSERVICEIDX(404)))(); if(strncmp(Attack_Process_Name,ProcessName,NT_PROCNAMELEN-1)) { ProcessID = OldNtUserQueryWindow(result,0); if(PsLookupProcessByProcessId((HANDLE)ProcessID,&Process) == STATUS_SUCCESS) { nameptr = (PCHAR)Process + gProcessNameOffset; strncpy(Target_Process_Name, nameptr, NT_PROCNAMELEN); Target_Process_Name[NT_PROCNAMELEN] = 0; if(!strncmp(Target_Process_Name,ProcessName,NT_PROCNAMELEN-1) || ProcessID == SaruenProcessID) { result=LastForegroundWindow; } else { LastForegroundWindow=result; } ObDereferenceObject(Process); } } return result; } 두번쨰로 사용할 수 있는게 DKOM입니다. DKOM은 예외경우가 많이 발생할 수 있음으로, PEPROCESS
FindProcessByName(char *Name) { PLIST_ENTRY start_plist,plist_hTable = NULL; ULONG *d_pid; PEPROCESS eproc; char *nameptr; CHAR Process_Name[NT_PROCNAMELEN]; NTSTATUS rc; plist_hTable = (PLIST_ENTRY)((*(ULONG*)
return 0; 그후에는 다음과 같은 코드로 숨킬 수 있습니다. PEPROCESS MyProcess;
PLIST_ENTRY plist_active_procs; MyProcess = FindProcessByName(ProcessName); if(MyProcess) { plist_active_procs = (LIST_ENTRY *)((DWORD)MyProcess + 0x88); *((DWORD *)plist_active_procs->Blink) = (DWORD)plist_active_procs->Flink; *((DWORD *)plist_active_procs->Flink+1) = (DWORD)plist_active_procs->Blink; plist_active_procs->Flink = (LIST_ENTRY *) &(plist_active_procs->Flink); plist_active_procs->Blink = (LIST_ENTRY *) &(plist_active_procs->Flink); } 위의 코드에 다음과 같은 한줄을 붙여넣음으로서, 보호하고자 하는 프로세스의 *((DWORD*)((DWORD)MyProcess+0x84)) = 0;
csrss.exe 에서 핸들을 지우는 코드 입니다. PEPROCESS gpeproc_csrss; gpeproc_csrss = (PEPROCESS)FindProcessByName("csrss.exe"); if(!gpeproc_csrss) gpeproc_csrss = (PEPROCESS)FindProcessByName ("CSRSS.EXE"); if(gpeproc_csrss) EraseHandle((PEPROCESS)gpeproc_csrss, (PVOID)MyProcess); void EraseHandle(PEPROCESS eproc, PVOID tarHandle)
{ PTABLE_ENTRY orig_tableEntry, p_tableEntry, *pp_tableEntry, **ppp_tableEntry; int a, b, c; int i_numHandles, i_hperPage, i_numTables; int i_handle; //DbgPrint("Hiding %x from %s process handle table.\n", tarHandle, (DWORD)eproc+gul_ProcessNameOffset); i_hperPage = PAGE_SIZE/sizeof(TABLE_ENTRY); for (b = 0; b < i_hperPage; b++) for (b = 0; b < i_hperPage; b++) for (c = 0; c < i_hperPage; c++) PspCidTable에서 오브젝트를 지우는 함수 입니다. DWORD gcid_table;
gcid_table = GetPspCidTable(); DWORD GetPspCidTable() { PVOID pPspCidTable = NULL; ULONG i; UNICODE_STRING usPsLookup; PUCHAR Buff; RtlInitUnicodeString( &usPsLookup, L"PsLookupProcessByProcessId" ); Buff = MmGetSystemRoutineAddress( &usPsLookup ); if( Buff != NULL ) return (DWORD)pPspCidTable ? *(DWORD*)pPspCidTable : (DWORD)NULL; } EraseObjectFromPspCidTable(gcid_table, (PVOID)MyProcess, 0,*((DWORD*)((DWORD)MyProcess+0x84)), 0 ); void EraseObjectFromPspCidTable(DWORD handle_table, PVOID tarHandle, enum ObjectType obj_type, DWORD pid, DWORD tid)
{ PTABLE_ENTRY orig_tableEntry, p_tableEntry, *pp_tableEntry, **ppp_tableEntry; int a, b, c; int i_numHandles, i_hperPage, i_numTables; int i_handle; i_numHandles = *(int*)(handle_table + 0x3c); i_hperPage = PAGE_SIZE/sizeof(TABLE_ENTRY); for (b = 0; b < i_hperPage; b++) pp_tableEntry[a][b].object = 0; for (b = 0; b < i_hperPage; b++) for (c = 0; c < i_hperPage; c++) ppp_tableEntry[a][b][c].object = 0; HandleListEntry 해제하는 코드 입니다. UnHookHandleListEntry((PEPROCESS)MyProcess);
void UnHookHandleListEntry(PEPROCESS eproc) { PLIST_ENTRY plist_hTable = NULL; plist_hTable = (PLIST_ENTRY)((*(PDWORD)((DWORD) eproc + 0xc4)) + 0x1c); //DbgPrint("Unhooking the handle table of Process: %s\n", (DWORD)eproc+gul_ProcessNameOffset); //plist_hTable->Flink = (LIST_ENTRY *) &(plist_hTable->Flink); // Change the current LIST_ENTRY } 프로세스에 속해 있는 스레드를 숨기는 코드 입니다. if(gpeproc_csrss) HideThreadsInTargetProcess((PEPROCESS)MyProcess, gpeproc_csrss);
void HideThreadsInTargetProcess(PEPROCESS eproc, PEPROCESS target_eproc) { PETHREAD start, walk; DWORD check1, check2; if (eproc == NULL) check1 = *(DWORD *)((DWORD)eproc + 0x50); start = *(PETHREAD *)((DWORD)eproc + 0x50);
이벤트,뮤텍스,파일맵 이름 검사 : CRC 검사 : 참고 문헌 (?) - 추후 추가 예정. | |
============================================================================================================== 출처 : 데브피아 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=7806&page=2 | |
Hooking 을 사용하는 프로그램 내의 구현 (0) | 2007.11.11 |
---|---|
Global Hooking in Win32 (0) | 2007.11.11 |
[본문스크랩] 메세지 후킹 (0) | 2007.09.06 |
[본문스크랩] Knowledge Base API (0) | 2007.09.06 |
API Hooking Revealed (0) | 2007.09.06 |