정보보호 공부

DLL Ingection 실습

모카롤 2023. 10. 30. 22:48

DLL Ingection 실습 코드

아래 실습 코드는 Windows11, Visual Studio 2017, process explorer 기준으로 테스트했습니다
아래 코드를 실행했는데 인젝션이 안된다면 아래 주의할 점을 참고 부탁드립니다 (저도 처음에 안 되었어요...)

 

DLL Ingection 코드

Visual Studio에서 아래 방식으로 새 프로젝트 생성 후 아래 코드를 실행(ctrl+F5)하면 인젝션이 됩니다.

인젝션 코드 전용 프로젝트 생성창

#include "pch.h"
#include <Windows.h>
#include <TlHelp32.h>
#include <tchar.h>
#include <string>
 
bool process_name_to_pid(
	__out DWORD& pid,
	__in const std::wstring& process_name
);
 
bool dll_injection(
	__in DWORD pid,
	__in const std::wstring& dll_name
);
 
int main()
{
	DWORD pid = 0;
	std::wstring process_name = L"notepad.exe";
	std::wstring dll_name = L"C:\\my_dll.dll";
	bool ret_flag = false;
 
	if (process_name_to_pid(pid, process_name)) {
		ret_flag = dll_injection(pid, dll_name);
		if (ret_flag){
			printf("dll inject success");
		}
	}
 
	return 0;
}
 
bool process_name_to_pid(
	__out DWORD& pid,
	__in const std::wstring& process_name
) {
	bool result = false;
	HANDLE snapshot = nullptr;
	PROCESSENTRY32 entry = {};
 
	entry.dwSize = sizeof(PROCESSENTRY32);
	snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
 
	if (snapshot != INVALID_HANDLE_VALUE) {
		Process32First(snapshot, &entry);
		do {
			if (!_tcsicmp(process_name.c_str(), entry.szExeFile)) {
				pid = entry.th32ProcessID;
				result = true;
				break;
			}
		} while (Process32Next(snapshot, &entry));
 
		CloseHandle(snapshot);
	}
 
	return result;
}
 
bool dll_injection(
	__in DWORD pid,
	__in const std::wstring& dll_name
) {
	bool result = false;
 
	HANDLE process_handle = nullptr;
	HANDLE thread_handle = nullptr;
	LPVOID remote_buffer = nullptr;
	HMODULE module = {};
	DWORD ret;
 
	LPTHREAD_START_ROUTINE thread_start_routine = nullptr;
 
	do {
		process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
		if (process_handle == nullptr) {
			break;
		}
 
		remote_buffer = VirtualAllocEx(
			process_handle,
			nullptr,
			dll_name.size(),
			MEM_COMMIT,
			PAGE_READWRITE
		);
		if (!remote_buffer) {
			break;
		}
 
		if (!WriteProcessMemory(
			process_handle,
			remote_buffer,
			dll_name.c_str(),
			dll_name.size() * sizeof(wchar_t),
			nullptr
		)) {
			break;
		}
 
		module = GetModuleHandle(L"kernel32.dll");
		thread_start_routine = (LPTHREAD_START_ROUTINE)GetProcAddress(module, "LoadLibraryW");
 
		thread_handle = CreateRemoteThread(
			process_handle,
			nullptr,
			0,
			thread_start_routine,
			remote_buffer,
			0,
			nullptr
		);
 
		if (thread_handle == NULL) {
			printf("handle is null\n");
		}
		ret = WaitForSingleObject(thread_handle, INFINITE);
		if (ret == WAIT_FAILED) {
			printf("wait failed\n");
		}
		else if (ret == WAIT_ABANDONED) {
			printf("wait abandoned\n");
		}
		else if (WAIT_TIMEOUT) {
			printf("timeout");
		}
		else {
			result = true;
		}
	} while (false);
 
	CloseHandle(process_handle);
	CloseHandle(thread_handle);
 
	return result;
}

 

DLL 코드

Visual Studio에서 아래 방식으로 dll 프로젝트를 생성하여 아래 코드 작성 후 빌드해주면 debug 폴더에서 파일을 확인할 수 있습니다. (상세 빌드 설명은 참고자료 참고 부탁 드립니다.)

DLL 생성 전용 프로젝트 생성창

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
 
BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH: {
		MessageBox(nullptr, L"Injection Success!", L"DLL injection", MB_OK);
	}
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

 

DLL Ingection 실습 코드(dll_injection 함수) 설명

  1. OpenProcess 
    1. 타겟 프로세스의 핸들을 가져옵니다
  2. VirtualAllocEx
    1. 타겟 프로세스의 가상 공간에 메모리를 할당합니다. 메모리에 직접 써야하므로 MEM_COMMIT 옵션을 넣습니다. 
    2. 인젝션 타겟 프로세스의 핸들, 인젝션할 dll의 경로(dll_name)의 크기를 기반으로 메모리 할당
  3. WriteProcessMemory
    1. 타겟 프로세스 메모리에 Load될 dll의 경로를 입력
  4. GetModuleHandle / GetProcAddress
    1. Windows에서 주요 dll은 프로세스마다 같은 주소에 로드되기 때문에, notepad.exe에 로딩된 kernel32.dll의 주소를 굳이 notepad.exe 프로세스로 부터 가져올 필요가 없습니다.
  5.  CreateRemoteThread
    1. 타겟 프로세스읜 notepad.ede가 쓰레드를 통해 kernel32.dll 로드 및 LoadLibraryW API를 호출합니다. (DLL Load)
    2. 해당 API는 인자인 remote buffer에 저장된 경로의 dll을 실행합니다. (DLL Injection)

 

 

주의할 점

위 코드는 kernel32.dll의 LoadLibrary API를 활용해 notepad에 dll injection을 시도합니다. kernel32.dll의 경우 같은 32bit 프로세스들끼리, 같은 64bit 프로세스들끼리 같은 주소 공간(Virtual Address Space)에 로드가 됩니다. 하지만 32bit와 64bit의 프로세스들은 호환되지 않습니다.

그렇기에 만약 위 코드를 실행했으나 dll이 뜨지 않는다면 아래 현상을 기반으로 문제를 해결하시면 됩니다.

 

1. handle is null 혹은 wait falied가 콘솔 창에 뜨며 제대로 인젝션되지 않는 경우

더보기

notepad와 DLL 인젝션 코드의 버전이 다릅니다. Windows10 이상의 경우 notepad는 64bit일 확률이 높습니다. (임의로 바꾸지 않은 경우) DLL 인젝션의 코드가 32bit로 실행 혹은 빌드되었을 경우에 notepad의 kernel32.dll의 주소가 달라 해당 주소의 핸들을 얻지 못해 null이 반환이 됩니다.

따라서 notepad의 버전을 확인 후, 해당 버전으로 코드를 재실행해주세요.

코드의 버전은 아래와 같이 Visual Studio 상단에서 확인 및 변경이 가능합니다.

2. timeout이 콘솔 창에 뜨며 제대로 인젝션되지 않는 경우

더보기

notepad와 DLL 의 버전이 다릅니다. Windows10 이상의 경우 notepad는 64bit일 확률이 높습니다. (임의로 바꾸지 않은 경우) DLL 이 32bit로 빌드되었을 경우에 notepad의 kernel32.dll의 주소가 달라 해당 주소의 핸들을 얻지 못해 null이 반환이 됩니다.

따라서 notepad의 버전을 확인 후, 해당 버전으로 DLL을 재빌드해주세요.

코드의 버전은 아래와 같이 Visual Studio 상단에서 확인 및 변경이 가능합니다.

 

DLL 실습 결과

Injection 코드 실행 후 notepad

 

DLL 인젝션 전

notepad의 dll 리스트

인젝션 전 dll 리스트

notepad의 thread 리스트

인젝션 전 thread 리스트

 

DLL 인젝션 후 

notepad의 dll리스트

인젝션 후 dll 리스트 (my_dll.dll이 로드됨을 확인)

notepad의 thread 리스트

인젝션 후 thread 리스트 (15616번 Kernel32.dll!LoadlibraryW API 확인)

 

 

DLL 인젝션 MessageBox 종료 후

DLL은 인젝션되어 있으나, 해당 MessageBox를 띄우는 쓰레드는 창을 종료함으로써 종료되었음을 알 수 있다. 따라서 해당 DLL에 MessageBox를 띄우는 것 외의 다른 작업을 진행한다면 해당 작업도 같이 쓰레드로 뜰 수 있음을 보여준다. 

인젝션 후 notepad에 뜬 MessageBox를 종료한 후의 dll과 thread 리스트

 

 

 

참고자료

기본 토대

https://gomguk.tistory.com/47

 

DLL Injection 실습

[분석환경] Windows 10 Pro 64bit [분석도구] PEiD, Stud_PE, BinText, PEView, OllyDbg 1. DLL(Dynamic Load Library)? 동적 연결 라이브러리의 약자이다. 한번 로딩된 DLL의 코드, 리소스는 Memory Mapping 기술로 여러 Process에

gomguk.tistory.com

https://wendys.tistory.com/23

 

[C/C++] DLL injection. 다른 Process에 내 DLL Load 하기

DLL Injection기본적으로 내가 만든 Process에 DLL을 Load하는 방법은 간단합니다. 그냥 일반적으로 사용하면 되죠 하지만 다른 Process에 내가 원하는 기능을 동작하게 하고싶은 경우엔 어떻게 해야할까

wendys.tistory.com

이슈 트래킹

https://stackoverflow.com/questions/5544957/tcscmp-error-in-c-dll

 

_tcscmp error in C++ dll

I am writing a DLL function called ADAuthenticate that will authenticate a user against Active Directory and check a certain attribute of the user if he/she exists. If the administrator is

stackoverflow.com

https://luckygg.tistory.com/278

 

[C++ DLL] Visual Studio C++ DLL 생성하기 (예제 포함)

지난 포스팅에서 외부 라이브러리 및 동적/정적 링킹에 대한 이론적인 소개를 했었습니다. [C++ DLL] 외부 라이브러리에 대한 이해 [C++ DLL] 외부 라이브러리에 대한 이해 외부 라이브러리란? 코딩

luckygg.tistory.com

https://tjstory.tistory.com/18

 

WaitForSingleObject 관련 스레드 다루기

WaitForSingleObject() - 보통 FindFirstChangeNotification, FindNext..., FindClose... 등과 쓰임 1. 용도 Thread가 특정 signal이 발생할 때까지 정지해 있다가, signal을 받으면, 작업을 수행하고 다시 정지 상태로 돌아가

tjstory.tistory.com

https://stackoverflow.com/questions/60687458/createremotethread-returns-null-while-trying-to-use-it-to-inject-dll

 

CreateRemoteThread returns NULL while trying to use it to inject dll

Here is some code this is supposed to inject my DLL and run it in notepad.exe but as the title states the CreateRemoteThread call returns null MyGetProcessId works just fine I made it and checked its

stackoverflow.com