반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- windows kernel debugging
- 윈도우 커널 드라이버
- 네트워크 바이트 오더
- HackCTF
- 윈도우 커널 디버깅
- arudino
- Windows Kernel Debug
- vcruntime.dll
- pwnable
- Network Byte Order
- ucrtbase.dll
- 시스템해킹
- 포너블
- Windows
- 해킹
- apphelp.dll
- 바이트 오더
- 윈도우 커널
- packet capture
- Windows Kernel
- Msvcrt.dll
- 개발하기
- Windows Kernel Driver
- IAT Hooking
- C언어 패킷캡쳐
- hacking
- pcap packet capture
- pcap packet
- vcruntime140.dll
- 개발 환경 준비
Archives
- Today
- Total
미친해커
[Reversing] Code Injection + IAT Hooking 본문
반응형
코드 인젝션과 IAT 후킹의 조합
코드 인젝션은 Fileless 형식의 임의의 코드 주입 공격이다. 그리고 IAT 후킹은 프로세스의 Import Address Table을 조작하여 특정 API의 제어권을 가로채는 기법이다.
이 두개의 기법을 적절히 활용하면 Fileless 형식의 IAT 후킹을 할수 있다.
예제 코드
Code Injector
소스코드는 아래 더보기에 있다.
더보기
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
#define IFREE(HeapBase) (RtlFreeHeap(_get_heap_handle(), 0, (HeapBase)) || 1)
#define Upper(s1) (s1 >= 65 && s1 <= 90 ? (char)s1 + 32 : s1)
WINBOOL CodePatch(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize)
{
SIZE_T lpNumberOfBytesWritten = 0;
return WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, &lpNumberOfBytesWritten) && nSize == lpNumberOfBytesWritten;
}
LPVOID FindIATAddress(HANDLE hProcess, LPCSTR lpModuleName, LPCSTR lpProcName)
{
PROCESS_BASIC_INFORMATION pbi = { 0, };
// NtQueryInformationProcess 함수로 타겟 프로세스의 PEB 주소를 구한다.
NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL);
PVOID BaseAddress;
// PEB 구조체에서 ImageBase를 읽어온다.
if (ReadProcessMemory(hProcess, (ULONG_PTR)pbi.PebBaseAddress + 0x10, &BaseAddress, sizeof(ULONG_PTR), NULL) == FALSE)
{
return NULL;
}
IMAGE_DOS_HEADER *DOS = malloc(sizeof(IMAGE_DOS_HEADER));
// 타겟 프로세스의 IMAGE_DOS_HEADER를 읽어온다.
if (ReadProcessMemory(hProcess, BaseAddress, DOS, sizeof(IMAGE_DOS_HEADER), NULL) == FALSE && IFREE(DOS))
{
return NULL;
}
IMAGE_NT_HEADERS *NT = malloc(sizeof(IMAGE_NT_HEADERS));
// 타겟 프로세스의 IMAGE_NT_HEADER를 읽어온다.
if (ReadProcessMemory(hProcess, (ULONG_PTR)BaseAddress + DOS->e_lfanew, NT, sizeof(IMAGE_NT_HEADERS), NULL) == FALSE && IFREE(DOS) && IFREE(NT))
{
return NULL;
}
free(DOS);
IMAGE_IMPORT_DESCRIPTOR *IMPORT = malloc(NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size);
PVOID *FirstImport = IMPORT;
// NT Header에서 IMAGE_IMPORT_DESCRIPTOR를 읽어온다.
if (ReadProcessMemory(hProcess, (ULONG_PTR)BaseAddress + NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, IMPORT, NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size, NULL) == FALSE && IFREE(NT) && IFREE(IMPORT))
{
return NULL;
}
free(NT);
// 타겟 DLL의 IMAGE_IMPORT_DESCRIPTOR를 찾는다.
for (; IMPORT->OriginalFirstThunk != NULL; IMPORT++)
{
// 현재 IMAGE_IMPORT_DESCRIPTOR의 DLL 이름과 타겟 DLL이름을 비교
for (char ch, i = 0; ReadProcessMemory(hProcess, (ULONG_PTR)BaseAddress + IMPORT->Name + i, &ch, 1, NULL); i++)
{
if (Upper(ch) != Upper(lpModuleName[i]))
break;
else if (ch == NULL && lpModuleName[i] == NULL)
goto FOUND_MODULE;
}
continue;
FOUND_MODULE:
break;
}
if (IMPORT->OriginalFirstThunk == NULL)
return NULL;
PVOID IATAddress = NULL;
IMAGE_THUNK_DATA *THUNK = malloc(sizeof(IMAGE_THUNK_DATA));
// 타겟 API의 IAT 주소를 구한다.
for (int i = 0; ReadProcessMemory(hProcess, (ULONG_PTR)BaseAddress + IMPORT->OriginalFirstThunk + i * sizeof(void *), THUNK, sizeof(IMAGE_THUNK_DATA), NULL) && THUNK->u1.AddressOfData != NULL; i++)
{
if (THUNK->u1.Ordinal >= 0x80000000)
continue;
// Compare api name of current IMPORT->OriginalFirstThunk to target api name
for (char ch, j = 0; ReadProcessMemory(hProcess, (ULONG_PTR)BaseAddress + THUNK->u1.AddressOfData + j + 2, &ch, 1, NULL); j++)
{
if (ch != lpProcName[j])
break;
else if (ch == NULL && lpProcName[j] == NULL)
{
IATAddress = (ULONG_PTR)BaseAddress + IMPORT->FirstThunk + i * sizeof(void *);
goto FOUND_IAT;
}
}
}
FOUND_IAT:
free(THUNK);
free(FirstImport);
return IATAddress;
}
PVOID CodeInjection(HANDLE hProcess, PVOID lpBuffer, SIZE_T nSize)
{
// 타겟 프로세스의 공간에 메모리 할당
PVOID BufferAddress = VirtualAllocEx(hProcess, NULL, nSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (BufferAddress == NULL)
{
return NULL;
}
SIZE_T NumberOfBytesWritten;
// 할당한 공간에 lpBuffer의 데이터를 작성
if (WriteProcessMemory(hProcess, BufferAddress, lpBuffer, nSize, &NumberOfBytesWritten) == FALSE || nSize > NumberOfBytesWritten)
{
VirtualFreeEx(hProcess, BufferAddress, 0, MEM_DECOMMIT);
VirtualFreeEx(hProcess, BufferAddress, 0, MEM_RELEASE);
return NULL;
}
return BufferAddress;
}
DWORD SearchCodePatchOffset(PVOID lpFunctionAddress, SIZE_T FunctionSize)
{
for (int i = 0; i < FunctionSize - 8; i++)
{
// x86
if (__SIZEOF_POINTER__ == 4 && *(ULONG_PTR *)((ULONG_PTR)lpFunctionAddress + i) == 0x11223344)
return i;
// x64
else if (__SIZEOF_POINTER__ == 8 && *(ULONG_PTR *)((ULONG_PTR)lpFunctionAddress + i) == 0x1122334455667788)
return i;
}
return -1;
}
int WINAPI NewMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
/*
다음과 같은 방법으로 호출하면 에러가 발생한다.
return OriMessageBox(hWnd, "Hooked..!", lpCaption. uType);
해당 코드가 컴파일 될때 "Hooked..!" 문자열은 포인터이기 때문에 다른 섹션에 저장되고
해당 섹션에서 포인터로 파라미터를 전달하기 때문에 다른 프로세스에서
해당 코드가 실행되면 다음과 같은 오류가 발생한다. EXCEPTION_ACCESS_VIOLATION.
그렇기 때문에 모든 문자열을 스택에 저장되도록 프로그래밍 해야한다.
*/
// 모든 문자열은 다음과 같은 방식으로 선언해야 한다.
char Text[] = "Hooked..!";
// 0x1122334455667788은 코드 인젝션 후에 원본 MessageBoxA 주소로 패치된다.
volatile int (WINAPI *OriMessageBox)(HWND, LPCSTR, LPCSTR, UINT) = 0x1122334455667788;
// x86 시스템에서 사용한다면 다음과 같이 선언한다.
// volatile int (WINAPI *OriMessageBox)(HWND, LPCSTR, LPCSTR, UINT) = 0x11223344;
// lpText 변수를 조작하여 함수 실행
return OriMessageBox(hWnd, Text, lpCaption, uType);
}
BOOL IAT_Hook(HANDLE hProcess, LPCSTR lpModuleName, LPCSTR lpProcName, PVOID lpNewApiAddress, SIZE_T NewApiSize)
{
SIZE_T NumberOfBytesRead;
// Code Injection of NewAPI(HOOK Function)
PVOID NewApiAddress = CodeInjection(hProcess, lpNewApiAddress, NewApiSize);
if (NewApiAddress == NULL)
return FALSE;
// Find IAT address of target api
PVOID IATAddress = FindIATAddress(hProcess, lpModuleName, lpProcName);
if (IATAddress == NULL)
{
VirtualFreeEx(hProcess, NewApiAddress, 0, MEM_DECOMMIT);
VirtualFreeEx(hProcess, NewApiAddress, 0, MEM_RELEASE);
return FALSE;
}
PVOID OriAPIAddress = NULL;
// Read original api address from IAT
if (ReadProcessMemory(hProcess, IATAddress, &OriAPIAddress, sizeof(PVOID), &NumberOfBytesRead) == FALSE || NumberOfBytesRead != 8)
{
VirtualFreeEx(hProcess, NewApiAddress, 0, MEM_DECOMMIT);
VirtualFreeEx(hProcess, NewApiAddress, 0, MEM_RELEASE);
return FALSE;
}
// Code patch 0xFFFFFFFFFFFFFFFF in new api
if (CodePatch(hProcess, (ULONG_PTR)NewApiAddress + SearchCodePatchOffset(NewMessageBoxA, NewApiSize), &OriAPIAddress, sizeof(ULONG_PTR)) == FALSE)
{
VirtualFreeEx(hProcess, NewApiAddress, 0, MEM_DECOMMIT);
VirtualFreeEx(hProcess, NewApiAddress, 0, MEM_RELEASE);
return FALSE;
}
// IAT code patch of target process
if (CodePatch(hProcess, IATAddress, &NewApiAddress, sizeof(ULONG_PTR)) == FALSE)
{
VirtualFreeEx(hProcess, NewApiAddress, 0, MEM_DECOMMIT);
VirtualFreeEx(hProcess, NewApiAddress, 0, MEM_RELEASE);
return FALSE;
}
// IAT Hooking success!!
return TRUE;
}
int main(int argc, char *argv[])
{
DWORD PID = 0;
printf("PID : ");
scanf("%d", &PID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if (IAT_Hook(hProcess, "user32.dll", "MessageBoxA", NewMessageBoxA, (ULONG_PTR)IAT_Hook - (ULONG_PTR)NewMessageBoxA))
{
printf("IAT Hooking Success\n");
}
else
{
printf("IAT Hooking Failed\n");
}
}
타겟 프로그램
소스코드는 아래 더보기에 있다.
더보기
#include <windows.h>
#define SHOW_MESSAGEBOX 1
VOID AddButton(HWND hWnd);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
RECT wr = { 0, 0, 500, 500};
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
WNDCLASSA wc = { 0, };
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
wc.hInstance = hInstance;
wc.lpszClassName = "WindowsClass";
wc.lpfnWndProc = WndProc;
if (RegisterClassA(&wc) == 0)
return -1;
HWND hWnd = CreateWindowA("WindowsClass", "Code Injection", WS_OVERLAPPEDWINDOW | WS_VISIBLE, GetSystemMetrics(SM_CXSCREEN) / 2 - 250, GetSystemMetrics(SM_CYSCREEN) / 2 - 250, wr.right - wr.left, wr.bottom - wr.top, NULL, NULL, NULL, NULL);
MSG msg = { 0, };
while (GetMessageA(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_COMMAND:
if (wParam == SHOW_MESSAGEBOX)
MessageBoxA(hWnd, "Not Hooked..!", "Click Me!", 0);
case WM_CREATE:
AddButton(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcA(hWnd, Msg, wParam, lParam);
}
}
VOID AddButton(HWND hWnd)
{
CreateWindowA("Button", "Click Me!", WS_VISIBLE | WS_CHILD, 200, 225, 100, 50, hWnd, SHOW_MESSAGEBOX, NULL, NULL);
}
결과 확인
반응형
'Reversing' 카테고리의 다른 글
[Reversing] Code Injection + Trampoline(Inline) Hooking x86 (0) | 2022.04.25 |
---|---|
[Reversing] DLL Injection + IAT Hooking (0) | 2022.02.14 |
Comments