mirror of
https://github.com/Kowntaewook/MCPTrace.git
synced 2026-11-02 04:45:09 +01:00
133 lines
10 KiB
Markdown
133 lines
10 KiB
Markdown
# 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](https://github.com/Kowntaewook/MCPTrace/actions/runs/40459138175)은
|
||
수집·분석·대조·원본 임시 덤프 삭제를 모두 마치고 성공했습니다.
|
||
[첫 실행 40457925570](https://github.com/Kowntaewook/MCPTrace/actions/runs/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 조합](https://github.com/Kowntaewook/MCPTrace/actions/runs/40459143849)도 통과했습니다.
|
||
|
||
첫 실행도 메시지 복원과 원문 대조는 통과했지만, 검증기가 파일 핸들을 닫지 않아 마지막
|
||
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 배포 바이너리](https://github.com/Velocidex/WinPmem/releases/download/v4.0.rc1/go-winpmem_amd64_1.0-rc2_signed.exe).
|
||
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 내부에서 분석 후 삭제했습니다.
|
||
|
||
## 재현 자료와 남은 범위
|
||
|
||
[실행 하네스와 명령](../validation/windows_positive/README.md),
|
||
[수동 실행 워크플로](../.github/workflows/windows-positive-memory.yml)를 제공합니다.
|
||
저장소에는 재현 코드와 집계 결과를 기록합니다. Actions 결과 아티팩트에는 측정 JSON,
|
||
정답 메시지, 정답 또는 명시적 비전송 대조군과 일치한 추출 레코드, 수집/분석 로그가 있으며
|
||
[최종 아티팩트](https://github.com/Kowntaewook/MCPTrace/actions/runs/40459138175/artifacts/12713857072)의
|
||
보관 만료는 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 공개 덤프 검사](windows-memory-validation.md)는 MCP 후보가 없었던 별도
|
||
호환성·읽기 검증입니다. 그 결과와 이번 Server 2022 양성 검증을 구분합니다.
|