[BOB] 2016 BISC 후기

Posted by MrBIN on February 16, 2017

BISC 참가

img1
2016 BISC 컨퍼런스에 발표자로 참가했었는데 BOB 마무리를 하고 뒤늦은 후기를 올린다. BISC는 BOB 수료 인원을 포함하여 관계자만 참여하는 보안 컨퍼런스인데 기술적인 발표와 함께 할로윈 파티(?) 느낌으로 진행되어서 비교적 가벼운 분위기였다. 100명 정도 참여한거 같은데 발표 준비하느라 기억이 가물가물하다..

컨퍼런스는 총 6개의 발표 주제가 있었고 다른 발표자들과는 달리 팀으로 참가하였다. 우리 팀은 마지막 순서였다. 주제는 BOB에서 진행 중인 NES 에뮬레이터 분석 프로젝트와 관련한 발표를 준비하였다. 우리 팀은 BISC 컨퍼런스의 취지에 맞게 무거운 설명은 대부분 덜어내고 재미를 주는 내용을 추가하기로 하였다. 그래서 에뮬레이터 취약점과 아키텍쳐에 대한 내용은 거의 포함하지 않았다. 대신 재미를 위해 NES 에뮬레이터에 슈퍼 마리오 브라더스 게임을 실행시킨 뒤 메모리 분석한 내용을 추가로 넣었다.


난 마리오가 버섯을 먹었을 때 캐릭터가 커지는 것과 같이 마리오 상태 변화에 관한 메모리를 분석하였다. 어렸을 때 엄마한테 등짝 맞으면서까지 닌텐도, PSP 게임을 많이 해왔지만 마리오 상태가 6개나 있다는 사실을 메모리 분석하면서 처음 알았다. 기본적인 형태인 꼬마리오와 버섯을 먹었을 때 커지는 슈퍼마리오까지는 대부분은 알듯하다(조금 열심히 했다면 너구리 마리오까지). 내가 알던 마리오를 제외하고 개구리, 스님(?), 해머 마리오는 처음 봤다. 나도 겜덕은 아닌가보다.

img2 마리오 변신 6단계


여튼 내 미션은 메모리를 분석하여 마리오 상태를 자유자재로 바꿔야 했다. BOB 프로젝트의 연장선이었기 때문에 에뮬레이터에 대한 virtual cpu memory를 보고 바꾸기로 하였다. NES에서 제공하는 CPU Memory Map을 보고 데이터가 바뀔 만한 곳을 삽질해서 알아보았고 Zero Page 영역에서 해당 값을 발견하였다. Zero Page는 $0000~$00FF 영역에 있고 빠르게 메모리에 접근 가능한 영역이다. NES 에뮬레이터에서 Zero Page 영역에는 스테이지 정보, 캐릭터의 좌표, 캐릭터 속성 등 게임에 관한 데이터가 저장되었다. 마리오의 상태를 나타내는 Player Status Offeset은 0xED 위치에 있었고 이 친구를 변경해주면 마리오 상태가 바뀌었다.

img3 CPU Memory Map

마리오 상태를 변경하는 방법을 찾았고 이를 프로그램으로 자동화해야 했다. 우선 ollydbg와 NES 에뮬레이터 내의 Memory Viewer 기능을 사용해 시작 부분을 패턴서치 했고 base address가 0x0059E3B0 인걸 알게 되었다.

img4 OllyDBG & NES Mem viewer Pattern Search

결과적으로 메모리 변경해야 할 위치(0xED)와 NES 에뮬레이터의 base address를 알기 때문에 프로그램 제작이 가능했다.

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#include <stdlib.h>
#include <TCHAR.h>
#include <iostream>
#include <thread>

using namespace std;

int PrintProcessList()
{
	int VirtuaNES_pid = 0;	
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);		//TH32CS_SNAPPROCESS = > 프로세스 모듈이 아닌 리스트

	ShellExecute(NULL, _T("open"), _T("C:\\Users\\mrbin\\Desktop\\virtuanes097e\\VirtuaNES.exe"),
	 _T("C:\\Users\\mrbin\\Desktop\\virtuanes097e\\Smario3.nes"), NULL, SW_SHOW);

	if (hSnapshot)
	{
		PROCESSENTRY32 ProcessEntry32;		// PROCESSENTRY32 structure 참고
		BOOL bProcessFound;
		ProcessEntry32.dwSize = sizeof(PROCESSENTRY32);
		bProcessFound = Process32First(hSnapshot, &ProcessEntry32);

		while (bProcessFound)
		{
			//printf("%s [%d]\n", ProcessEntry32.szExeFile, ProcessEntry32.th32ProcessID); // print process id list
			bProcessFound = Process32Next(hSnapshot, &ProcessEntry32);

			if (!_tcscmp(L"VirtuaNES.exe", ProcessEntry32.szExeFile))	// compare string
			{
				VirtuaNES_pid = ProcessEntry32.th32ProcessID;
			}
		}
		CloseHandle(hSnapshot);
	}
	return VirtuaNES_pid; 
}


int main(int argc, char * argv[])
{
	int i;
	char array_byte[2] = { 0, };
	
	char *base = (char *)0x0059E3B0;		// NES virtual CPU address
	char *addr = (char *)0x0059E3B0;
	int VirtuaNES_pid = 0;
	HANDLE Mario_Process;

	VirtuaNES_pid = PrintProcessList();
	printf("VirtuaNES's PID : [ %d ]\n\n", VirtuaNES_pid);
	
	Mario_Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, VirtuaNES_pid);  // get process handler

	while (1) {
		printf("===============================\n");
		printf("=== 1. Have Super Mushroom ====\n ");
		printf("=== 2. Have Fire Suit =========\n ");
		printf("=== 3. Have Raccoon Suit ======\n ");
		printf("=== 4. Have Frog Suit =========\n ");
		printf("=== 5. Have Tanookie Suit =====\n ");
		printf("=== 6. Have Hammer Suit =======\n ");
		printf("===============================\n");
		printf(">>  ");
		scanf("%d", &i);

		switch (i)
		{
			case 1: *array_byte = 1, addr = base + 0xed; break;
			case 2: *array_byte = 2, addr = base + 0xed; break;
			case 3: *array_byte = 3, addr = base + 0xed; break;
			case 4: *array_byte = 4, addr = base + 0xed; break;
			case 5: *array_byte = 5, addr = base + 0xed; break;
			case 6: *array_byte = 6, addr = base + 0xed; break;
		
		}
		WriteProcessMemory(Mario_Process, addr, array_byte, (SIZE_T)1, NULL);
	}
	return 0;
}

gif1

큰 내용은 아니였지만, 무거운 내용도 아니여서 팀원들이랑 웃고 즐기면서 준비해서 좋았다.