2018. 3. 13. 19:01ㆍ0x03 Reversing Theory
ATTACKER
#include <tchar.h> // use _tprintf
#include <iostream> // 입출력
#include <Windows.h> // 식별자, Read/Write ProcessMemory
#include <psapi.h> // EnumProcessModulesEx
//#include <stdlib.h> // malloc
#include <tlhelp32.h>
#define BASE_OF_CODE (0x1000)
#define TARGET "BYPASSTESTGAME.exe"
#define x86 1
#define x64 2
using namespace std;
BOOL GetStart();
void PrintProcessMemory(HANDLE, LPCVOID, SIZE_T);
void ModifyProcessMemory(HANDLE, LPVOID, LPCVOID, SIZE_T);
LPCVOID GetBaseAddress(DWORD);
void PrintError(const TCHAR*);
int main(int, char *[])
{
cout << "main function start" << endl;
GetStart();
cout << "\Mrealrudaganya?!?!?!?" << endl;
return 0;
}
BOOL GetStart()
{
SIZE_T lpOffset_R = 0x1e; // 계속 유동적으로 리버싱으로 찾아야 함
LPCVOID lpBaseAddress_R; // read용
SIZE_T nSize_R = 0x7; // n바이트를 읽어올 예정 (여기선 7바이트)
SIZE_T lpOffset_W;
LPVOID lpBaseAddress_W; // write용
SIZE_T nSize_W; // 변경할 사이즈
LPCVOID lpBuffer_W; // nSize_W만큼 할당할 것임
// The portions of the system to be included in the snapshot.
DWORD dwFlags = TH32CS_SNAPPROCESS;
// The process identifier of the process to be included in the snapshot.
DWORD th32ProcessID = NULL;
bool exit = false;
do
{
cout << "\nPlease choose a method to modify the value of memory.";
cout << "\n1. Change the value of the operand.";
cout << "\n2. Change conditional branch statement.";
char choice = 0;
do
{
cout << "\nInput: ";
cin >> choice;
} while (choice != '1' && choice != '2');
//조건분기에 따라 주소가 다르므로 분기문으로 해두었는데
// 더 자동화 시키려면 어떻게 해야하는지 연구해보기
if (choice == '1')
{
lpOffset_W = 0xCC;
nSize_W = 0x1;
lpBuffer_W = new char[nSize_W] { 0x01 }; // 특정 오프셋에 0x01대입
}
else
{
lpOffset_W = 0xE4;
nSize_W = 0x1;
lpBuffer_W = new unsigned char[nSize_W] { 0xEB }; // 특정 오프셋에 0xEB 대입 (0XEB = OPCODE = JMP)
}
cout << "\nFinding processes...";
bool wasFound = false;
do
{
/* CreateToolhelp32Snapshot function
특정 프로세스들뿐 아니라 이러한 프로세스들에 의해 사용되는 힙, 모듈 및 스레드의 스냅 샷을 가져옵니다. */
HANDLE hSnapshot = CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
if (hSnapshot == INVALID_HANDLE_VALUE)
{
PrintError(TEXT("CreateToolhelp32Snapshot"));
return(FALSE);
}
PROCESSENTRY32 pe;
/* PROCESSENTRY32 구조체에 대한 포인터.
실행 파일의 이름, 프로세스 식별자 및 상위 프로세스의 프로세스 식별자와 같은 프로세스 정보를 포함합니다. */
//LPPROCESSENTRY32 lppe = &pe;
pe.dwSize = sizeof(PROCESSENTRY32);
/* Process32First function
시스템 스냅샷에서 마주친 첫 번째 프로세스에 대한 정보를 가져옵니다. */
if (!Process32First(hSnapshot, &pe))
{
PrintError(TEXT("Process32First"));
CloseHandle(hSnapshot);
return(FALSE);
}
HANDLE hProcess;
DWORD dwPriorityClass;
do
{
if (wcscmp(pe.szExeFile, TEXT(TARGET)) == 0) // 스냅샷정보와 현재 타겟 바이너리 비교
{
wasFound = true;
//dwPriorityClass = 0;
// The access to the process object.
DWORD dwDesiredAccess = PROCESS_ALL_ACCESS; // POSSIBLE READ + WRITE
/* If this value is TRUE, processes created by this process will inherit the handle.
Otherwise, the processes do not inherit this handle. */
BOOL bInheritHandle = FALSE;
// The identifier of the local process to be opened.
DWORD dwProcessId = pe.th32ProcessID;
/* OpenProcess function
Opens an existing local process object. */
hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
// 프로세스가 열리면 null이 아님
if (hProcess == NULL)
{
PrintError(TEXT("OpenProcess"));
continue;
}
_tprintf(TEXT("\n\nhProcess: 0x%p"), hProcess);
_tprintf(TEXT("\n\nhProcess: 0x%d"), hProcess);
LPCVOID lpBaseAddress = GetBaseAddress(dwProcessId); // 베이스어드레스 얻음
printf("GET BASEADDR : %p\n", lpBaseAddress);
int addr = (int)lpBaseAddress;
printf("RVA : %p\n", (int)lpBaseAddress + BASE_OF_CODE);
// LPCVOID -> BYTE* 로 쓰이는 듯
lpBaseAddress_R = (LPCVOID)((const BYTE *)lpBaseAddress + BASE_OF_CODE + lpOffset_R);
printf("lpbaseaddr read = %p\n", lpBaseAddress_R);
lpBaseAddress_W = (LPVOID)((const BYTE *)lpBaseAddress + + BASE_OF_CODE+ lpOffset_W);
printf("lpbaseaddr write = %p\n", lpBaseAddress_W);
/* GetPriorityClass function
지정된 프로세스의 우선 순위 클래스를 가져옵니다.
이 값은 프로세스의 각 스레드의 우선 순위 값과 함께 각 스레드의 기본 우선 순위 수준을 결정합니다. */
_tprintf(TEXT("\n\tdwSize = %lu"), pe.dwSize);
_tprintf(TEXT("\n\tdwProcessId = 0x%p"), dwProcessId);
_tprintf(TEXT("\n\tcntThreads = %lu"), pe.cntThreads);
_tprintf(TEXT("\n\tth32ParentProcessID = 0x%p"), pe.th32ParentProcessID);
_tprintf(TEXT("\n\tPriority base = %d"), pe.pcPriClassBase);
dwPriorityClass = GetPriorityClass(hProcess);
if (!dwPriorityClass)
PrintError(TEXT("GetPriorityClass"));
else
_tprintf(TEXT("\n\tdwPriorityClass = 0x%08X"), dwPriorityClass);
PrintProcessMemory(hProcess, lpBaseAddress_R, nSize_R);
ModifyProcessMemory(hProcess, lpBaseAddress_W, lpBuffer_W, nSize_W);
PrintProcessMemory(hProcess, lpBaseAddress_R, nSize_R);
/*PrintProcessMemory(hProcess, (LPVOID)0x401080, 0x11);
ModifyProcessMemory(hProcess, (LPVOID)0x40108E, new char[nSize_W] { 0x01 }, 0x1);
PrintProcessMemory(hProcess, (LPVOID)0x401080, 0x11);*/
CloseHandle(hProcess);
}
} while (
/* Process32Next function
스냅샷에 기록 된 첫 번째 프로세스에 대한 정보를 가져오기 위해 Process32First 함수를 사용합니다. */
Process32Next(hSnapshot, &pe));
CloseHandle(hSnapshot);
} while (!wasFound);
cout << "\n\nPlease choose what you want to do.";
cout << "\n1. Memory modification.";
cout << "\n2. Exit the program.";
do
{
cout << "\nInput: ";
cin >> choice;
if (choice == '2')
{
exit = true;
break;
}
} while (choice != '1');
} while (!exit);
return(TRUE);
}
void PrintProcessMemory(
/* A handle to the process with memory that is being read.
The handle must have PROCESS_VM_READ access to the process. */
HANDLE hProcess,
/* A pointer to the base address in the specified process from which to read.
Before any data transfer occurs, the system verifies that all data in the base address and memory of the specified size is accessible for read access, and if it is not accessible the function fails.
0x401080 */
LPCVOID lpBaseAddress,
// The number of bytes to be read from the specified process.
SIZE_T nSize
)
{
// A pointer to a buffer that receives the contents from the address space of the specified process.
LPVOID buffer = operator new(nSize);
// A pointer to a variable that receives the number of bytes transferred into the specified buffer. If lpNumberOfBytesRead is NULL, the parameter is ignored.
SIZE_T *lpNumberOfBytesRead = NULL;
printf("READING...buffer = %p\n", (INT)lpBaseAddress+nSize);
if (ReadProcessMemory(hProcess, lpBaseAddress, buffer, nSize, lpNumberOfBytesRead)) {
cout << "\n\nbuffer =>" << endl;
SIZE_T i = 0;
while (i < nSize) {
printf("%02X ", ((unsigned char *)buffer)[i++]);
}
}
else
PrintError(TEXT("ReadProcessMemory"));
}
void ModifyProcessMemory(
HANDLE hProcess,
/* A pointer to the base address in the specified process to which data is written.
Before data transfer occurs, the system verifies that all data in the base address and memory of the specified size is accessible for write access, and if it is not accessible, the function fails.
*/
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize
)
{
SIZE_T *lpNumberOfBytesWritten = NULL;
// lpBuffer에 사이즈만큼 삽입
if (!WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten))
PrintError(TEXT("WriteProcessMemory"));
}
LPCVOID GetBaseAddress(DWORD th32ProcessID)
{
DWORD dwFlags = TH32CS_SNAPMODULE;
// 가져온 프로세스 아이디로 핸들 값 획득
HANDLE hModuleSnap = CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
if (hModuleSnap == INVALID_HANDLE_VALUE)
{
PrintError(TEXT("CreateToolhelp32Snapshot(INVALID_HANDLE_VALUE, th32ProcessID)"));
return(FALSE);
}
MODULEENTRY32 me;
//LPMODULEENTRY32 lpme = &me;
me.dwSize = sizeof(MODULEENTRY32); // 사이즈를 구함
if (!Module32First(hModuleSnap, &me))
{
PrintError(TEXT("Module32First"));
CloseHandle(hModuleSnap);
return(FALSE);
}
do
{
// 모듈명과 바이너리 명 비교
if (wcscmp(me.szModule, TEXT(TARGET)) == 0)
{
CloseHandle(hModuleSnap);
return me.modBaseAddr;
}
} while (Module32Next(hModuleSnap, &me)); // 다음 모듈 반복하다가 모듈이 더이상 없을 때 종료
CloseHandle(hModuleSnap); // 핸들 해제
return(FALSE);
}
void PrintError(const TCHAR* msg)
{
DWORD eNum;
TCHAR sysMsg[256];
TCHAR* p;
eNum = GetLastError();
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
eNum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
sysMsg, 256, NULL);
p = sysMsg;
while ((*p > 31) || (*p == 9))
++p;
do { *p-- = 0; } while ((p >= sysMsg) && ((*p == '.') || (*p < 33)));
_tprintf(TEXT("\n\tWARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg);
}
TARGET
#include <stdio.h>
#include <Windows.h>
// ASLR
void playgame()
{
int a = 1000; // health
// 특정 오프셋에서 read byte 하여서 그 read byte와 tmp배열에 read byte에 쓰일 만한거 적어두고
// 맞으면 패스
printf("charater's health = %d\n", a);
if (a == 0) // a의 주소를 알아내거나, 분기문을 교체하거나
{
MessageBoxA(NULL, "Congratulations", "MODULATION", MB_OK);
}
}
int main(int argc, char*argv[])
{
int n = 0; // n의 주소를 알아내거나, case문 분기문을 교체하거나
// case 1 : n이 위치한 주소 ASLR 걸려있지만, 이제 베이스주소를 얻었기 때문에 그 만큼 빼면 된다.
// 일단 진행햇을 때 BASE Addr : 0x01010000 + 0x1000 = 0x01011000
// n이 위치한 주소 : 0x010110c6 하지만, 정확한 offset은 0x010110cc
// 그렇다면, 0x010110cc - 0x01011000 => 0xcc 만큼 차이가 남
// case 2 : n 값을 변경하지 않고, 분기를 변경
// 원래 opcode및 instruction
/*
010110E0 837D FC 00 CMP DWORD PTR SS:[EBP-4],0
010110E4 74 08 JE SHORT BYPASSTE.010110EE
여기의 74(JE를 JMP로 패치할 거임) -> JMP는 EB, JE는 74임
정확한 주소는 010110E4 , base addr은 0x0101000 + 0X1000 => 0x01011000(RVA)
0x10110E4 - 0x01011000 = 변조 주소는 oep에서 0xE4 만큼 뒤에 있음
*/
/*
좀 더 제대로 해보기위해 메인함수 시작부터 쭉 읽어오다가 내가 정해둔 offset에서 멈추게 해보자.
메인함수 주소 : 0x3B10C0 (3B는 유동) 0x1e바이트
조작할 주소 : 0x3B10DD
*/
printf("play game\n");
switch (n)
{
case 0:
while (1) {
printf("End Game\n");
Sleep(1000);
}
case 1:
printf("Oh.. you can modulation..\n");
exit(0);
system("pause");
playgame();
}
return 0;
}
'0x03 Reversing Theory' 카테고리의 다른 글
64비트 간단한 예제로 다루어보는 최적화 모드들에 대한 분석 (0) | 2018.03.15 |
---|---|
우연히 삽질 후 알게 된 메인함수의 뒷 이야기..? (0) | 2018.03.14 |
프로세스 명 가져오기 (0) | 2018.03.12 |
Self HomeWork (0) | 2018.03.11 |
Hookcing API Practice..[1]NtOpenProcess (0) | 2018.03.11 |