2022. 3. 22. 20:02ㆍ리버싱 핵심 원리
InjectDll.cpp
#include "windows.h"
#include "tchar.h"
// LP Long Pointer 4byte
// C constant 상수 함수의 내부에서 값을 바꿀 수 없음
// STR string자료가 될 것이라는 뜻으로 내부적으로는 char형 배열에 null값 종료를 의미함.
// W wide char -> unicode 2byte형
// TCHAR, t_char 컴파일 환경에 따라 문자열을 처리할 수 있게된다.
BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) // SE_DEBUG_NAME, TRUE
// SE_DEBUG_NAME : 다른 계정이 소유한 프로세스의 메모리를 디버그하고 저장하는 사용
{
/*
typedef struct _TOKEN_PRIVILEGES {
DWORD PrivilegeCount; // Privilege 배열의 항목 수를 설정
LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; // LUID_AND_ATTRIBUTES 구조의 배열로 각 구조에는 권한의 LUID와 속성이 포함되어 있다.
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
*/
/*
typedef struct _LUID_AND_ATTRIBUTES {
LUID Luid; // LUID 값
ULONG Attributes; // LUID의 속성을 지정
} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;
*/
// LUID는 8byte의 값으로 시스템 시동 시 그 운영 체계에 부여되는 유일한 식별자로 윈도우 NT에서는 보안 객체를 지시하는 식별자로
// 파일, 디렉터리, 서비스 등 객체에 고유 변호와 같은 운영 체계 변호를 제공한다.
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
/*
BOOL WINAPI OpenProcessToken(
_In_ HANDLE ProcessHandle, // 권한을 상승하고자 하는 프로세스의 핸들
_In_ DWORD DesiredAccess, // 토큰에 접근하기 위한 접근 권한
_Out_ PHANDLE TokenHandle // 토큰 핸들
*/
// 엑세스 토큰은 프로세스나 스레드의 보안 컨텍스트를 설명하는 개체입니다.
// 토큰 정보에는 프로세스 또는 스레드와 연결된 사용자 계정의 id와 권한이 포함되어 있습니다.
// 프로세스의 토큰 핸들을 흭득하고 흭득한 토큰의 권한을 설정할 수 있다.
if( !OpenProcessToken(GetCurrentProcess(), // 현재 실행되는 프로세스의 핸들을 반환
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, // 액세스 토큰의 권한을 활성화하거나 비활성화하는데 필요 | 액세스 토큰을 조회하는데 필요
&hToken) )
{
_tprintf(L"OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}
/*
BOOL LookupPrivilegeValueA(
[in, optional] LPCSTR lpSystemName, // 명시된 특권을 찾기 위한 시스템 이름
[in] LPCSTR lpName, // 특권 이름을 나타내는 문자열 포인터
[out] PLUID lpLuid // 검색된 LUID
);
*/
if( !LookupPrivilegeValue(NULL, // NULL로 입력하면 자동으로 시스템에서 특권 이름을 찾는다
lpszPrivilege,
&luid) )
{
_tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 권한을 활성화
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
/*
BOOL AdjustTokenPrivileges(
[in] HANDLE TokenHandle,
[in] BOOL DisableAllPrivileges,
[in, optional] PTOKEN_PRIVILEGES NewState,
[in] DWORD BufferLength,
[out, optional] PTOKEN_PRIVILEGES PreviousState,
[out, optional] PDWORD ReturnLength
);
*/
// 권한을 조정하는 함수
if( !AdjustTokenPrivileges(hToken, // 토큰 핸들
FALSE, // 명시된 특권을 결정 1: disable, 0: enable
&tp, // 새로 설정할 권한
sizeof(TOKEN_PRIVILEGES), // 토큰 사이즈
(PTOKEN_PRIVILEGES) NULL, // 이전 권한을 저장
(PDWORD) NULL) ) // 이전 권한의 사이즈 저장
{
_tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
_tprintf(L"The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
HANDLE hProcess = NULL, hThread = NULL;
HMODULE hMod = NULL;
LPVOID pRemoteBuf = NULL;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;
// #1. dwPID 를 이용하여 대상 프로세스(notepad.exe)의 HANDLE을 구한다.
if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
_tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
return FALSE;
}
// #2. 대상 프로세스(notepad.exe) 메모리에 szDllName 크기만큼 메모리를 할당한다.
/*
LPVOID VirtualAllocEx(
[in] HANDLE hProcess, // 특정 프로세스의 핸들
[in, optional] LPVOID lpAddress, // 할당된 페이지 영역에 대해 시작 주소를 지정하는 포인터, NULL일 경우 자동으로 할당
[in] SIZE_T dwSize, // 할당할 메모리 영역의 BYTE 단위 크기
[in] DWORD flAllocationType, // 메모리 할당 타입
[in] DWORD flProtect // 할당된 페이지에 대한 메모리 보호
);
*/
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
// MEM_COMMIT 예약된 주소 공간과 물리적 저장소를 매핑하는 역할을 한다.
// #3. 할당 받은 메모리에 myhack.dll 경로("c:\\myhack.dll")를 쓴다.
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);
// #4. LoadLibraryA() API 주소를 구한다.
hMod = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
// #5. notepad.exe 프로세스에 스레드를 실행
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
int _tmain(int argc, TCHAR *argv[])
{
if( argc != 3)
{
_tprintf(L"USAGE : %s <pid> <dll_path>\n", argv[0]);
return 1;
}
// change privilege
//
if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
return 1;
// inject dll
if( InjectDll((DWORD)_tstol(argv[1]), argv[2]) )
_tprintf(L"InjectDll(\"%s\") success!!!\n", argv[2]);
else
_tprintf(L"InjectDll(\"%s\") failed!!!\n", argv[2]);
return 0;
}
myhack.dll
#include "windows.h"
#include "tchar.h"
#pragma comment(lib, "urlmon.lib")
#define DEF_URL (L"http://www.naver.com/index.html")
#define DEF_FILE_NAME (L"index.html")
HMODULE g_hMod = NULL;
DWORD WINAPI ThreadProc(LPVOID lParam)
{
TCHAR szPath[_MAX_PATH] = {0,};
if( !GetModuleFileName( g_hMod, szPath, MAX_PATH ) )
return FALSE;
TCHAR *p = _tcsrchr( szPath, '\\' );
if( !p )
return FALSE;
_tcscpy_s(p+1, _MAX_PATH, DEF_FILE_NAME);
URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL);
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
HANDLE hThread = NULL;
g_hMod = (HMODULE)hinstDLL;
switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
OutputDebugString(L"<myhack.dll> Injection!!!");
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
CloseHandle(hThread);
break;
}
return TRUE;
}
Injectdll.exe (Notepad PID) (myhack.dll address)를 입력하게 되면 메모장에 권한을 상승시키고 myhack.dll에 있는 프로시저 동작한다. 해당 프로시저는 naver.com의 index.html 파일을 다운로드합니다.
x32dbg를 통해 분석을 해보겠습니다.
먼저 Injectdll.exe PID myhack.dll를 입력합니다. 메모장에 myhack.dll이 로딩되기 때문에 x32dbg가 해당 진입점에 멈춥니다. F9로 진행하다 보면 myhack.dll의 EP에서 멈추게 됩니다.
스레드가 생성되기 전 "<myhack.dll> Injection!!!"라는 문자열이 출력됩니다.
따라서 해당 문자열을 찾고 해당 시작점에 BP를 걸고 진행하였습니다.
진행하다 보면 CreateThread라는 API가 실행되는데 3 번째 인자가 실행되는 함수를 나타내서 해당 부분에 BP를 걸었습니다.
진행하다 보면 myhack_sub10001000의 시작점에 도착하게 됩니다.
진행하다 보면 GetModuleaFileNameW API가 호출됩니다.
다음 myhack.sub_10001100 함수가 호출되는데 리턴 값이 \myhack.dll 문자열의 시작 주소입니다.
다음은 myhack.sub_100112E 함수가 실행됩니다. 주소 중 파일명이 myhack.dll이 아닌 index.html로 변경되었습니다.
다음 실행되는 함수는 URLDownloadToFileW 함수인데 해당 함수는 인터넷에서 다운 받은 데이터를 파일에 저장합니다.
다운로드 할 URL은 http://www.naver.com/index.html이고 저장될 경로는 myhack.dll이 있는 경로/index.html입니다.
해당 API가 실행되니 다음과 같이 파일이 생겼습니다.
'리버싱 핵심 원리' 카테고리의 다른 글
DLL Injection AppInit_DLLs (0) | 2022.03.22 |
---|---|
Windows 메시지 후킹 분석 (0) | 2022.03.22 |