6. DLL Injection
2) DLL (Dynamic Linked Library)
▣ LoadLibrary & FreeLibrary
- 두 함수 모두 Kernel32.DLL 안에 들어있음.
- HINSTANCE LoadLibrary(LPCTSTR lpLibFileName);
☞ 해당 dll 파일을 메모리 상으로 로드.
☞ HINSTANCE : 로딩한 DLL의 핸들값.
☞ LPCTSTR lpLibFileName : DLL 경로(상대, 절대 경로 모두 가능)
- BOOL FreeLibrary(HMODULE hLibModule);
☞ 해당 dll 파일을 메모리 상에서 해제.
☞ HMODULE hLibModule : 핸들 값
- Load된 수 만큼 메모리 상에서 개수만 중첩되기 때문에 Free또한 load한 만큼 해야함.
▣ DLL 파일 로드하는 함수 만들기
#include <windows.h> int main(int argc, char *argv[]) { HMODULE hModule; hModule = LoadLibrary("C:\\Documents and Settings\\student\\바탕 화면\\LoadMain\\DLL\\Debug\\DLL.dll"); if(hModule) OutputDebugString("로드성공"); else OutputDebugString("로드실패"); if(hModule) FreeLibrary(hModule); return 0; }
- 8줄 LoadLibrary : 로딩한 dll의 핸들값 리턴. (로딩실패시 NULL 리턴)
- 16줄 FreeLibrary : 메모리상에서 제거 전 dll의 main함수가 한번 더 돌면서 메시지박스가 한번 더 뜬다.
▣ 메모리 분석하기
- 앞서 만든 DLL 파일을 PEview로 열어보면 IMAGE BASE가 10000000임.
- 해당 DLL을 불러오는 프로그램을 디버깅모드로 실행하여 DLL 파일의 IMAGE BASE인 10000000번지를 보면 LoadLibrary 전에는 아무 값이 없음.
- LoadLibrary함수를 실행하게 되면 dll 파일이 해당 메모리의 주소에 올라옴을 확인할 수 있음.
- hModule 변수에는 1000000이 들어감.
3) DLL (Dynamic Linked Library) -- 함수 불러오기
--> DLL 의 가장 큰 사용 목적은 해당 DLL 파일안의 함수를 쓰기위한 것이다.
▣ DLL 파일 속 함수 참조 원리
- DLL 파일 속 함수를 쓰려면 해당 함수의 주소가 필요함.
- 프로그램은 DLL 파일의 EAT를 참조하여 IAT를 완성하고 IAT를 참조하여 주소를 가지고 해당 함수를 사용.
- 직접 IAT와 비슷하게 만들기 위해 DLL 파일 속 함수의 주소를 담을 함수 포인터 변수를 사용할 것임.
▣ DLL 파일 만들기
#include <windows.h> extern "C" void _declspec(dllexport) Test() { MessageBox(NULL, "export 함수 호출 테스트", "", MB_OK); } BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch(ul_reason_for_call) { case DLL_PROCESS_ATTACH: OutputDebugString("ATTACH"); break; case DLL_PROCESS_DETACH: OutputDebugString("DETACH"); break; } return TRUE; }
- 3줄 extern "C" : 다음 함수를 C컴파일러로 컴파일한다는 뜻.
☞ dll파일은 cpp로 작성되는데 cpp 컴파일러의 경우 함수 이름 앞뒤에 랜덤 문자열을 붙임.
☞ extern "C"를 사용하지 않았을 때 ( == C++컴파일러 사용 시)
☞ extern "C"를 사용했을 때 ( == C컴파일러 사용 시)
- 4줄 _declspec(dllexport) : export함수로 사용한다고 명시적으로 표시를 해주어야 EAT에 반영됨.
▣ DLL 속 Test 함수를 불러올 프로그램 만들기
[함수 포인터]
- 기본형태 : return_type (*function_name)(arg1, arg2 ....);
- 함수 이름 앞에 포인터 * 붙이고 ( )로 감싸주기.
☞ ( ) 로 감싸주지 않으면 리턴형이 return_type* 로 컴파일러가 착각함.
- function_name = 함수이름;
☞ 함수이름의 함수 주소가 function_name 변수에 들어감.
#include <windows.h> typedef void (*PFNFUNC)(); PFNFUNC fnTest; int main(int argc, char *argv[]) { HMODULE hModule; hModule = LoadLibrary("C:\\Documents and Settings\\student\\바탕 화면\\LoadMain\\DLL\\Debug\\DLL.dll"); if(hModule) { // PFNFUNC로 자료형을 설정하지 않았다면 // fnTest = (void(*)())GetProcAddress(hModule, "Test"); fnTest = (PFNFUNC)GetProcAddress(hModule, "Test"); if(fnTest) fnTest(); // 함수호출 다른방법 // 1. // ((PFNFUNC)GetProcAddress(hModule, "Test"))(); // 2. // GetProcAddress(hModule, "Test"); // _asm call eax } if(hModule) FreeLibrary(hModule); return 0; }
- 3줄 typedef : 함수 포인터 자료형을 설정해줌. (캐스팅할거라서 사용)
- 5줄 PFNFUNC fnTest : 함수 포인터 변수 선언.
- 16줄 GetProcAddress : 핸들의 모듈값으로 해당 dll의 EAT를 찾아가서 2번째 인자와 일치하는 이름의 함수 주소를 가져옴.
4) DLL Injector 만들기
▣ CreateRemoteThread
- 타 프로세스에서 다른 프로세스에 스레드 생성후 어떤 행동을 어떤 인자를 가지고 할지도 결정 가능.
- 대상 프로세스에 스레드 생성 --> LoadLibrary 함수 호출 가능.
- CreateRemoteThread(대상 프로세스의 핸들값, 인자(주소), 함수주소);
☞ 대상 프로세스 핸들값 : OpenProcess 함수로 구함.
☞ 인자(주소) : 대상 메모리에 인자가 존재해야함. WriteProcessMemory 함수 사용.
☞ 함수 주소 : GetProcAddress(모듈핸들, 함수이름) 사용
→ 모듈핸들은 GetModuleHandle(모듈이름) 로 구함.
→ 하지만 함수 주소는 현재 프로세스의 메모리 안에서 구하니까 현재 프로세스 메모리 속 해당 모듈의 함수주소를 찾아옴.
진짜 구해야하는 함수주소는 dll을 주입할 프로세스의 메모리에서 찾아와야함.
그래서 가정을 함. 현재 프로세스와 주입당할 프로세스의 메모리가 같은 위치에 해당 모듈이 있을거라고..
▣ OpenProcess
- 주입할 대상 프로세스에 대한 핸들 얻음.
- OpenProcess(pid);
☞ pid를 구하기 위해서 툴을 이용하여 알아내거나 함수로 구하거나.
▣ WriteProcessMemory
- 대상 프로세스 내에 내가 원하는 위치에 원하는 값 기록.
- 할당된 메모리에 DLL 경로를 기록.
- WriteProcessMemory(대상프로세스 핸들값, 위치, 쓸 내용, 쓸 크기);
☞ 위치 : VirtualAllocEx 함수로 구함.
☞ 쓸 내용, 쓸 크기 : dll 경로.
▣ VirtualAllocEx
- 대상 프로세스 내에서 DLL 경로를 넣기 위한 메모리를 할당.
- 새로운 공간 생성 (원하는 위치에 원하는 크기로 공간확보)
- VirtualAllocEx(프로세스 핸들값, 위치, 크기)
☞ 0 (NULL 값) 넣으면 알아서 비어있는 공간을 자동으로 할당해줌.
[Thread Injection]
- DLL Injection : LoadLibrary함수로 실행 (이미 해당 메모리에 존재하는 함수 실행)
- Code Injection : 본인이 직접 짠 코드 실행.
★☆ String 주소랑 함수 주소를 어떻게 독립된 다른 메모리로 보낼까? ★☆
- 레지스터를 쓴다!
- 그래서 가장 먼저 EBX와 EAX를 사용하여 자신의 메모리 스택으로 온전히 불러와서 씀.
- 나중에 해당 스택에 접근하여 PUSH와 CALL을 통해 함수를 실행함.
i2sec 대구지점 23기 수료생.
'해킹&보안 > 리버싱' 카테고리의 다른 글
[D+14] 악성코드 분석 (0) | 2017.04.21 |
---|---|
[D+12] DLL Injection (1) (0) | 2017.04.18 |
[D+11] 안티 디버깅 (0) | 2017.04.17 |
[D+10] 패킹&언패킹 (2) (0) | 2017.04.12 |
[D+9] 패킹&언패킹 (1) (0) | 2017.04.11 |