Files
MCPTrace/docs/windows-positive-memory-validation.md
T

10 KiB
Raw Blame History

Windows 실제 MCP 통신의 RAM 복원 검증

검증일: 2026-10-27. GitHub Actions의 Windows Server 2022 x64 빌드 20348 VM에서 공식 Python MCP SDK로 실제 stdio 요청·응답을 실행하고 WinPmem으로 RAM을 수집했습니다. windows.mcptrace.MCPTrace를 실행한 뒤 정답 로그와 비교하고, 모든 추출 원문을 native Windows 프로세스 레이어에서 다시 읽어 주소·길이·SHA-256·VAD 범위를 대조했습니다. 덤프에 테스트 JSON을 주입하거나 합성 페이지 테이블을 사용하지 않았습니다.

실행과 결과

최종 실행 40459138175은 수집·분석·대조·원본 임시 덤프 삭제를 모두 마치고 성공했습니다. 첫 실행 40457925570의 분석 관측값도 함께 기록해 실행 사이의 차이를 숨기지 않습니다.

실행 조건 복원 논리 메시지 완전한 도구 요청·응답 쌍 추출 레코드 / 통신 정답과 일치 비전송 대조군
첫 실행, 정리 단계 실패 natural 17/21 (81.0%) 6/8 92 / 92 0
첫 실행, 정리 단계 실패 retained 21/21 (100%) 8/8 106 / 105 1
수정 후 전체 성공 natural 16/21 (76.2%) 5/8 55 / 55 0
수정 후 전체 성공 retained 20/21 (95.2%) 7/8 65 / 64 1

최종 실행에서 내보낸 120개 레코드, 33,318 bytes 전부의 원문·해시·가상 주소·VAD 범위가 재읽기와 일치했습니다. 정답과 대조 가능한 방향성 후보 링크 316개에서 잘못된 연결 0개, 대조 불가능한 링크 0개를 관측했습니다. 이 수에는 복사본 간 연결이 포함되며 316개의 고유 요청·응답 쌍을 의미하지 않습니다. 첫 실행은 198개 레코드·80,401 bytes와 링크 550개를 대조했습니다. 두 실행 모두 각 조건에서 동일 PID 내 완전한 도구 요청·응답 쌍이 존재했습니다.

최종 실행의 동일 PID 내 메시지/쌍은 natural 클라이언트 14개/5쌍, 서버 7개/1쌍, retained 클라이언트 18개/6쌍, 서버 9개/2쌍입니다. retained의 합집합 7쌍을 한 프로세스에서 7쌍이 모두 복원됐다는 뜻으로 읽으면 안 됩니다.

두 실행의 차이는 관측 결과이며 원인을 분리하는 실험은 수행하지 않았습니다. 특히 프레임을 보관한 조건도 두 번째 실행에서는 1개 메시지를 찾지 못했습니다. 이 결과만으로 누락 원인을 메모리 해제·paging·수집 누락·스캐너 중 하나로 확정하지 않습니다. 전체 실행 성공 조건은 100% 복원이 아니라 원문 검증과 동일 PID의 실제 요청·응답 복원입니다.

최종 코드의 테스트 126개와 Ruff 검사·포맷 검사가 통과했습니다. Ubuntu/Windows × Python 3.10/3.12의 네 CI 조합도 통과했습니다.

첫 실행도 메시지 복원과 원문 대조는 통과했지만, 검증기가 파일 핸들을 닫지 않아 마지막 RAM 파일 삭제가 Windows WinError 32로 실패했습니다. 첫 실행의 manifest.json에는 정리 전에 기록한 passed가 남아 있으므로 첫 실행 전체를 성공으로 해석하면 안 됩니다. Volatility 레이어를 의존성 역순으로 닫고, 정리까지 끝난 뒤 성공을 기록하도록 수정했습니다. 검증 예외가 발생할 때도 실제 FileLayer 핸들이 닫히는 회귀 테스트를 추가했습니다.

실험 조건과 해석

독립된 클라이언트·서버 쌍 두 개, 총 Python 프로세스 네 개를 동시에 유지했습니다. 각 쌍은 initialize, initialized 알림, tools/list와 echo_marker 도구 호출 8회를 수행합니다. 정답은 조건별 21개 논리 메시지(요청 10개, 응답 10개, 알림 1개)입니다. 도구의 실제 응답에서 입력 문자열·실험 UUID·순번·서버 PID를 확인했습니다. 입력은 ASCII, 한글·이모지, 이스케이프·줄바꿈·중괄호, 2 KiB 문자열, 중복 텍스트 등을 포함합니다.

  • natural: 전송/수신 경계에서 정답 로그를 기록하지만 직렬화 프레임을 별도 목록으로 보관하지 않습니다. 로깅으로 생성된 복사본이 남을 수 있으므로 계측되지 않은 일반 클라이언트와 같은 조건은 아닙니다.
  • retained: 실제 통신 프레임 21개를 메모리에 보관하는 유리한 양성 대조군입니다. 전송하지 않은 tools/call JSON 하나도 보관합니다. 이것을 실제 통신 정답에는 포함하지 않습니다.

메시지 복원 수는 두 엔드포인트에서 찾은 결과의 합집합에서 중복을 제거한 값입니다. 메모리 복사본은 서로 다른 증거 레코드로 보존하며 복원 메시지 수를 늘리지 않습니다. 요청·응답 쌍도 두 엔드포인트 합집합과 동일 PID 내 발견을 따로 집계합니다. 각 조건의 클라이언트·서버 PID별 수치는 보고서의 comparison.json에 있습니다.

정답 대조는 JSON 키 순서와 공백을 정규화한 메시지 객체 기준입니다. 수신 정답은 SDK가 파싱한 객체를 재직렬화한 것으로, 파이프 원본 바이트 캡처가 아닙니다. 원문 바이트 검증은 별도로 덤프의 해당 가상 주소를 pad=False로 다시 읽어 수행했습니다. 같은 Volatility의 native 레이어를 사용한 재읽기이며, 다른 포렌식 엔진과의 교차 검증은 아닙니다.

비전송 대조군도 복원됐습니다. 따라서 MCP 형식의 JSON이 메모리에 존재한다는 사실만으로 실제 전송·공격·도구 실행을 입증할 수 없습니다. 통신 로그와 일치하는 레코드 비율을 일반적인 탐지 정밀도로 표현하지 않습니다. 후보 연결 오류 0도 이 단일 세션·고유 ID의 작은 통제 집합에서 대조 가능한 연결에 한정됩니다. ambiguous-id 표시와 복사본은 유지됩니다.

환경·수집·검사 범위

  • Windows Server 2022 x64 10.0.20348, Python 3.12.10, volatility3==2.28.2.
  • 공식 Python SDK mcp==1.26.0, stdio, 실제 협상 프로토콜 2025-11-25.
  • MCPTrace 0.2.0; 최종 실행 커밋 14a8947c388f10c559b406f5d3bf783265d29705.
  • 공식 WinPmem 배포 바이너리. SHA-256: 86691bb4af2c17dd9ec4834c04a99ad51e04f780a07d1b05bc382a5d1892e0c4. 서명 정책·보안 설정을 변경하지 않고 드라이버 설치·수집·제거를 수행했습니다.

최종 실행의 RAM은 8,584,425,472 bytes이고, 이미지 논리 크기는 11,811,160,064 bytes입니다. 이미지 SHA-256: 22027df47f06eec202e2d25c9fd40b996a1bc6374f158b035e9245764af3edb6. 수집 시작/종료는 2026-10-27 06:07:11.326 / 06:08:08.407 UTC로 약 57.08초입니다. 각 조건의 마지막 정답 메시지 기록부터 수집 시작까지 약 0.089초입니다. 첫 실행은 수집 약 40.56초, 마지막 메시지부터 시작까지 약 0.085초였습니다.

조건·역할 PID VAD 수 읽은 bytes 읽지 못한 bytes 내보낸 레코드
natural client 5752 141 25,849,856 4,441,501,696 39
natural server 3980 145 20,398,080 4,449,112,064 16
retained client 4552 141 25,763,840 4,441,587,712 46
retained server 6784 146 22,634,496 4,463,456,256 19

총 573개 VAD에서 94,646,272 bytes를 읽었습니다. 모든 대상에서 error/skip이 없고, 바이트·레코드·앵커·후보 제한에 도달하지 않았으며 잘못된 영역은 0입니다. 내부 candidate_records는 126개, MCP 관련성 필터 후 relevant/displayed_records는 120개입니다. 원문 검증과 정답 비교의 분모는 내보낸 120개입니다.

물리 주소 간 빈 구간을 포함한 sparse raw 이미지이므로 파일 논리 크기가 설치 RAM보다 큽니다. 물리 메모리 수집은 시간이 걸리는 live acquisition이며 단일 시점의 원자적 스냅샷이 아닙니다. 호출 직후 수집을 시작하고 수집 동안 추가 도구 호출 없이 프로세스를 유지했습니다. 지연 시간이나 메모리 부하를 달리한 실험이 아니므로 흔적의 보존 기간을 추정하지 않습니다.

windows.info.Info에서 심볼을 준비한 뒤 MCPTrace와 원문 재읽기는 offline으로 실행했습니다. 선택한 네 PID의 VAD를 기본 설정으로 검사했고 크기/레코드 제한을 임의로 늘리지 않았습니다. unreadable_bytes에는 예약되어 사용하지 않는 주소 공간도 포함됩니다. RAM 해시는 분석 전후 같았으며, 전체 덤프는 VM 내부에서 분석 후 삭제했습니다.

재현 자료와 남은 범위

실행 하네스와 명령, 수동 실행 워크플로를 제공합니다. 저장소에는 재현 코드와 집계 결과를 기록합니다. Actions 결과 아티팩트에는 측정 JSON, 정답 메시지, 정답 또는 명시적 비전송 대조군과 일치한 추출 레코드, 수집/분석 로그가 있으며 최종 아티팩트의 보관 만료는 2026-11-03입니다. ZIP SHA-256은 abfdbbea869bc47c8dfac01bfaa515882d8204d5914a62abf70ce68851689a5e입니다. RAM 이미지나 미분류 후보 원문은 업로드하지 않습니다. RAM에서 추출한 원문을 포함하는 결과 ZIP은 저장소에 추가하지 않았습니다. 덤프 원본을 보존하지 않았으므로 동일 이미지의 제3자 재분석은 불가능하며, 하네스로 새로운 캡처를 만들어 반복 검증할 수 있습니다. 각 실행의 이미지 해시는 실행 결과의 식별자입니다.

이 결과는 Windows Server 2022의 특정 Python SDK·stdio 조건에서 실제 MCP 메시지를 추출할 수 있다는 양성 검증입니다. Windows 11에서의 MCP 양성 검증, Claude Desktop 등 실제 클라이언트, HTTP/SSE 전송, 장시간 지연·추가 부하, 다른 SDK, 다중 세션의 ID 재사용, 부분 메시지·인자 복원율은 아직 평가하지 않았습니다. 공격 탐지율을 측정한 실험도 아닙니다.

기존 Windows 11 공개 덤프 검사는 MCP 후보가 없었던 별도 호환성·읽기 검증입니다. 그 결과와 이번 Server 2022 양성 검증을 구분합니다.