맥북을 메인으로 쓰면서 보안 공부를 하다 보면, 리버싱 실습에서 벽에 부딪힌다. 분석 대상이 x86 윈도우 프로그램인데 내 맥은 ARM이다. Parallels로 윈도우를 돌려도 그게 ARM 윈도우라, x86 동적 분석이 매끄럽지 않다. 처음엔 "에뮬레이션이 다 되는 거 아닌가" 싶었는데, 실제로 후킹 도구를 붙여보면서 왜 안 되는지, 그리고 결국 어떻게 환경을 정리했는지를 남긴다.
Parallels로 애플 실리콘 맥에 윈도우를 깔면 ARM64 버전의 윈도우가 깔린다. 일상적인 윈도우 앱은 x86 에뮬레이션으로 그럭저럭 돌아가지만, 보안 공부에서 하는 동적 분석은 얘기가 다르다. Frida 같은 후킹 도구로 x86 프로세스에 붙어서 함수 호출을 가로채고 메모리를 들여다보려고 하면, 아키텍처가 어긋나서 잘 안 되는 경우가 많았다.
이유를 찾아보니 단순히 "에뮬레이션이라 느려서"가 아니라 구조 자체의 문제였다. Windows 11 on Arm은 x86/x64 코드를 실행할 때 Prism이라는 에뮬레이터가 x86 명령어 블록을 그때그때 Arm64 명령어로 JIT 컴파일한다. 이렇게 번역된 코드 블록은 캐시에 저장돼서, 같은 코드를 다시 실행할 때는 번역을 또 하지 않고 캐시된 걸 재사용한다. x86 쪽 시스템 콜과 파일/레지스트리 접근은 WOW64 레이어가 맡아서, x64용 WOW64가 x86 코드를 감싸는 것과 비슷한 방식으로 처리된다.
문제는 Frida 같은 동적 계측 도구가 하는 일이 이 캐시 구조와 정면으로 부딪힌다는 거다. Frida의 Interceptor.attach는 원래 대상 함수의 진입부에 트램폴린(작은 점프 코드)을 심어서 실행 흐름을 가로챈다. 그런데 이게 네이티브 x86 위에서는 "그 함수가 메모리 어디에 있고, 그 자리의 바이트를 어떻게 바꿀지"가 명확한 반면, 에뮬레이션 위에서는 Frida가 보는 코드와 실제로 CPU가 실행하는 코드가 다른 레이어에 있다. Frida는 x86 프로세스의 가상 메모리에 패치를 심지만, 실제 실행은 이미 Arm64로 번역되어 캐시된 블록 위에서 일어난다. 패치 시점과 캐시 갱신 시점이 어긋나면 후킹이 아예 안 걸리거나, 걸렸다가도 캐시가 다시 채워지면서 무효화되는 일이 생겼다.
정적 분석은 그나마 낫다. IDA나 Ghidra로 바이너리를 열어서 어셈블리를 읽는 건 파일을 디스크에서 읽어서 해석하는 작업이라 아키텍처를 가리지 않는다. 맥에서도 똑같이 된다. 하지만 실제로 프로그램을 실행시켜서 런타임 동작을 관찰하는 x86 동적 분석은, 캐시·번역 레이어가 끼어들지 않는 진짜 x86 환경이 필요했다.


이걸 눈으로 확인하는 방법이 있다. 윈도우 Task Manager의 세부 정보 탭에는 Architecture 열이 있는데, 네이티브 Arm64 앱은 그냥 ARM64로 뜨고 x86/x64 앱이 에뮬레이션으로 돌아가고 있으면 ARM64 (x64 compatible) 같은 식으로 표시된다. 이 열을 한 번 띄워보면 지금 돌리고 있는 프로세스가 진짜 네이티브인지, 에뮬레이션 위에서 돌고 있는지가 바로 보인다. 리버싱 대상 프로그램을 실행해놓고 이 열을 확인하는 걸 습관으로 들이면, "지금 내가 보는 게 진짜 x86 실행인가"를 헷갈리지 않을 수 있다.
더 근본적인 문제도 있다. 마이크로소프트 문서에 따르면 에뮬레이션은 유저 모드 코드만 지원하고 드라이버는 지원하지 않는다. 커널 모드 컴포넌트는 반드시 Arm64로 네이티브 컴파일되어야 한다. 그런데 커널 레벨 후킹이나 드라이버 기반 안티치트 분석, 커널 디버거를 쓰는 종류의 분석은 이 조건 때문에 ARM 윈도우 위에서는 원천적으로 막힌다. 유저 모드 후킹이 캐시 문제로 불안정한 것과는 또 다른, 더 단단한 벽이다.
결론은 x86이 꼭 필요한 작업은 집에 있는 x86 윈도우 PC에서 하는 거였다. 지뢰찾기 동적 분석이나 x86 메신저 프로그램 분석 같은 건 그 PC에서 돌렸다. 문제는 그러면 분석 파일이 집 컴퓨터에만 남는다는 거다. 다른 작업은 다 맥에서 하는데 이것만 물리적으로 다른 기계에 묶여 있으니, 매번 옮겨서 관리하기가 번거로웠다.
이 부분은 파일 동기화로 풀었다. 집 PC와 맥 사이에 분석 파일을 오가게 해서, 어느 쪽에서 작업하든 최신 파일을 볼 수 있게 했다. 큰 파일이 오갈 때는 홈 네트워크가 빠르니 그쪽에서 처리하는 게 유리했다. 핵심은 "x86 실행은 x86 기계에서, 문서·정리·정적 분석은 맥에서"라는 역할 분리를 명확히 하고, 그 사이를 동기화로 잇는 것이었다.


이 환경 문제를 가장 확실하게 느낀 건 수업 과제로 받은 고전 지뢰찾기(winmine.exe)를 패치할 때였다. 32비트 PE로 된 오래된 프로그램이라 요즘 프로그램과 달리 난독화나 패킹이 없어서, 리버싱 입문 대상으로는 오히려 좋았다. 목표는 "지뢰를 눌러도 지지 않게" 승패 분기를 무력화하는 것이었다.
접근은 정적 분석과 동적 분석을 같이 썼다. 디스어셈블러로 코드를 열어서 칸을 클릭했을 때 호출되는 흐름을 따라가며, 지뢰 여부를 검사하고 게임 오버로 이어지는 조건 분기를 어셈블리에서 찾았다. 그다음 실제로 프로그램을 실행하면서 디버거로 그 지점에 브레이크포인트를 걸고, 칸을 눌렀을 때 정말 그 경로로 가는지 대조했다. 정적으로 짐작한 분기가 실제 실행에서도 그 자리인지 확인하는 과정이다.
바로 이 동적 확인 단계에서 ARM 윈도우의 한계를 체감했다. 브레이크포인트를 걸고 실행 흐름을 관찰하는 작업이 에뮬레이션 위에서는 캐시된 번역 블록 때문에 예상과 다르게 동작하는 경우가 있었고, 그래서 이 검증은 결국 x86 PC로 옮겨서 다시 했다. 분기를 특정한 뒤에는 그 지점의 바이트 몇 개를 바꿔서 패배 경로를 타지 않게 만들고, 패치 전후로 실제 실행 결과를 대조해서 내가 건드린 지점이 정말 그 로직이었다는 걸 증명했다. 분석 대상은 수업에서 허가된 과제용 바이너리였고, 여기서는 접근 방법과 환경 문제를 남기는 게 목적이라 패치 바이트나 파일 자체는 다루지 않는다.
리버싱 도구는 종류가 많고 서로 요구하는 게 다르다. 정리하면서 쓴 기준은 이렇다.
이걸 한 환경에 다 섞으면 충돌하거나 어느 게 어디서 되는지 헷갈린다. "이 도구는 어느 환경에서 쓰는가"를 먼저 정하고, 그 기준대로 도구를 배치하는 걸로 정리했다.
돌아보면 맥 사용자가 보안 공부를 하려면 이 x86 문제를 한 번은 정면으로 겪게 된다. ARM 윈도우로 다 될 것 같지만 동적 분석에서 막히고, 그 원인이 단순 성능 문제가 아니라 JIT 번역 캐시와 유저 모드 한정 에뮬레이션이라는 구조적인 이유라는 걸 알고 나면, 오히려 왜 진짜 x86 환경이 필요한지가 명확해진다. 처음부터 "정적은 맥, 동적 x86은 x86 기계, 그 사이는 동기화"라는 그림을 잡아두면 시행착오를 줄일 수 있다.
참고로 실습에 쓴 분석 대상은 수업에서 과제용으로 제공되고 허가된 것들이었다. 환경을 어떻게 꾸렸는지가 이 글의 요지고, 특정 프로그램의 분석 결과나 제공된 바이너리 자체를 배포하려는 건 아니다.
맥에서 윈도우 PC를 원격 분석 환경으로 쓸 때 겪은 키 입력 문제는 Parsec으로 맥→윈도우 원격접속할 때 Cmd+W 누르면 파섹이 꺼지는 문제에 따로 정리했다. 퍼징으로 찾은 크래시를 실제로 제보까지 이어간 과정은 오픈소스 취약점을 GitHub에 책임 있게 제보하기에 있다.

| Parsec 원격 PC에서 npm 없을 때 Node.js 설치하고 재부팅 없이 파섹 유지하기 (0) | 2026.07.26 |
|---|---|
| Parsec으로 맥→윈도우 원격접속할 때 Cmd+W 누르면 파섹이 꺼지는 문제 (0) | 2026.07.26 |
| iTerm2에서 SSH로 Linux 접속할 때 한글 깨짐 + zsh/Powerline 테마 잡기 (0) | 2026.07.22 |
| 새 PC에 Windows + Ubuntu 듀얼부팅 처음 깔아보기 (Rufus, BIOS, 미할당 256GB) (0) | 2026.07.22 |
| Kali Linux에 fcitx5로 한글 입력기 잡기 + 한영 전환 키 매핑 (0) | 2026.07.22 |