분석 결과, 제로 브릿지(zero bridges)가 카운트되었습니다. 해당 앱은 51개의 메서드를 노출합니다.
요약
본 글은 데스크톱 앱의 프로세스 간 통신(IPC) 메커니즘을 분석하는 방법을 다룹니다. 특히, 소스 코드가 없는 애플리케이션을 검사할 수 있는 REA(Reverse Engineer Anything)라는 도구를 소개하며, 이를 통해 Electron 같은 복잡한 앱 구조를 리버스 엔지니어링하고 API 호출 및 브릿지를 식별하는 과정을 설명합니다.
핵심 포인트
- REA는 소스 코드가 없는 앱도 분석 가능한 MCP 서버/CLI입니다.
- Electron Fiddle을 예시로 사용하여 실제 코드 실행 없이 IPC 메커니즘을 파악했습니다.
- 분석 결과, 컨텍스트 브릿지 API와 IPC 채널 등 핵심 통신 구조를 상세히 식별할 수 있었습니다.
저는 데스크톱 애플리케이션의 창이 메인 프로세스와 어떻게 통신하는지 알고 싶었습니다. Electron에서 이는 거의 모든 '저희도 저런 기능 만들어 주세요' 요청 뒤에 오는 첫 번째 질문입니다: 어떤 호출들이 브릿지를 가로지르는지, 그리고 다른 쪽에서는 그것들에 대해 어떤 답변을 하는가.
그래서 저는 출시된 앱 하나를 리버스 엔지니어링 도구에 넣고 요약본을 읽었습니다. 세 개의 컨텍스트 브릿지가 있었습니다. 그중 어느 것도 해당 앱에 속하지 않았습니다.
이번 주 모두가 별표 친 도구
REA는 Reverse Engineer Anything의 약자로, 소스 코드가 없는 애플리케이션을 검사할 수 있게 해주는 MCP 서버이자 CLI입니다. 네이티브 바이너리는 Hopper, Ghidra 또는 IDA를 거칩니다. JavaScript 및 Electron 앱, .NET 어셈블리, Android 패키지 및 웹사이트는 자체적인 경로를 가집니다. 이 도구는 일요일에 GitHub 트렌딩 1위를 차지하며 하루 만에 25,599개의 새로운 별표(총 약 118,000개, MIT 라이선스)를 기록했고, Hacker News 스레드는 774점과 327개의 댓글에 도달했습니다.
핵심은 간단합니다: 다른 사람의 앱에서 마음에 드는 기능을 보고, 자신의 에이전트가 그것이 어떻게 구축되었는지 알아내도록 하는 것입니다. npm 릴리스 버전 6.4.0에는 127개의 CLI 명령어가 포함되어 있습니다(저는 --help를 통해 카운트했습니다).
이 중 대부분은 네이티브 디어셈블러가 필요합니다. 정적 JavaScript 경로는 그렇지 않으므로, 저는 이 경로를 사용했습니다. 저는 REA를 에이전트의 MCP 설정에 등록하는 rea setup을 건너뛰고, 임시 폴더에서 npx를 통해 CLI를 호출했습니다.
실행 결과
제 목표는 Electron 팀 자체 MIT 라이선스 플레이그라운드 앱인 Electron Fiddle 0.39.0이었습니다. 의도적으로 오픈 소스로 공개되었기 때문에, 저는 모든 답변을 실제 코드와 비교할 수 있었습니다. GitHub 릴리스에서 macOS arm64 zip 파일을 다운로드하고, 그 SHA-256 값을 GitHub가 게시하는 다이제스트와 일치시킨 다음, app.asar(21.9 MB)만 추출했습니다. 아무것도 설치하거나 실행하지 않았습니다.
npx -y rea-agents@latest analyze-javascript-application /abs/path/app.asar --json > fiddle.json
약 3분 후 저는 43MB의 JSON을 얻었습니다. REA는 89개의 파일을 인벤토리화하고, 71개의 JavaScript 파일을 구문 분석했으며, 앱의 코드를 실행하지 않고도 2,386,900개의 구문 노드를 방문했습니다. 간소화된 Electron 요약은 다음과 같습니다:
context_bridge_apis: 3 exposed_api_members: 5
ipc.operations: 26 ipc.literal_channels: 11
ipc.dynamic_channel_operations: 6
이러한 발견들은 모두 실제이며, 그중 어느 것도 Fiddle의 것이 아닙니다. 11개의 리터럴 채널(clipboard:readText, open-github, get-displays 및 나머지)은 앱 번들에 시작 템플릿으로 포함된 예제 피들(fiddles)에서 가져온 것입니다. 이 여섯 개의 동적 채널과 __SENTRY_IPC__ 브리지는 Sentry SDK에 속합니다. Fiddle 자체의 브리지인 window.ElectronFiddle은 카운트에 포함되지 않았습니다.
답이 사라진 곳
그것은 조용히 누락된 것이 아니었으며, 이 부분은 충분한 공로를 인정받아야 합니다.
브리지를 정의하는 프리로드(preload) 파일은 9,768바이트 크기입니다. REA는 이를 완전히 구문 분석한 다음, 여기에 두 개의 unknown 노드를 첨부했습니다. 하나는 부모가 남은 결과 용량을 예약했기 때문에 파일의 의미론적 분석이
REA의 문서는 --source-path 플래그를 사용하여 사용자가 지정한 파일만 분석한다고 설명합니다. 이 기능은 main 브랜치에 있습니다. 제가 실행한 npm 릴리스 버전에서는 Unknown flag: --source-path라는 메시지가 나왔고, 공정하게 말하자면 설치 문서에서도 main이 npm보다 앞서 나갈 수 있다고 경고하고 있습니다.
한 줄 실험
그래서 저는 직접 선택했습니다. 해당 프리로드(preload) 파일을 별도의 폴더에 복사한 다음 그것만 단독으로 분석했습니다. 10초 후: 제로 브릿지, 제로 IPC 작업, 그리고 이번에는 그래프 커버리지가 complete라고 표시되었습니다.
이유는 최소화된 파일 자체에 있습니다. Webpack은 코드가 사용하는 곳에 require("electron")를 남기지 않습니다. 대신 require 구문을 번호가 매겨진 모듈로 감싸서 자체 로더를 통해 그 결과를 전달합니다:
288(E){E.exports=require("electron")}
// ...같은 파일의 나중 부분
const E=_(288)
REA의 시맨틱 그래프는 ipcRenderer.send와 ipcRenderer.invoke 호출을 감지했습니다. 하지만 바인딩 E를 알 수 없는 값으로 표시했고, REA는 별칭(alias)임을 증명할 수 있을 때만 무언가를 Electron API로 취급합니다. 따라서 E.contextBridge.exposeInMainWorld(...)는 브릿지로 간주되지 않았습니다. Sentry 프리로드에서 포착한 것은 require("electron")를 직접 호출하는 아주 작은 별도의 파일입니다.
저는 복사본의 한 표현식, 즉 _(288)을 require("electron")으로 변경하고 다시 실행했습니다:
context_bridge_apis: 1 (ElectronFiddle, 51 members, 0 unknown)
ipc.operations: 48 ipc.dynamic_channel_operations: 48
51개의 멤버는 Fiddle의 src/preload/preload.ts가 정의하는 것과 정확히 일치합니다. 저는 소스에서 그것들을 세었습니다. 모든 48개 채널이 동적(dynamic)으로 유지되는 이유는 열거형(enum) 값들이 다른 webpack 모듈에 존재하기 때문이며, REA는 각각을 그렇게 레이블링합니다.
대조적으로, 저는 Electron의 아카이브된 API 데모 앱의 번들링되지 않은 소스에 연결했습니다. 17초 동안 18개의 리터럴 채널을 통해 30개의 IPC(Inter-Process Communication) 작업이 발생했으며, 12개의 모든 렌더러 전송(renderer sends)은 해당 메인 프로세스 핸들러와 쌍을 이루었습니다. 순수한 코드만으로는 정적인 그림이 매우 좋습니다. 하지만 프로덕션 번들에서는, 번들러가 런타임을 삽입한 바로 그 지점에서 간극이 생깁니다.
저는 webpack의 로더를 통해 Electron을 추적하는 것에 대한 열린 이슈를 찾을 수 없었습니다. 제가 파일을 올린 것은 아니며, 이는 읽고 실행만 한 세션이었습니다.
증거는 하한선이다 (Evidence is a lower bound)
제가 이와 같은 도구를 기반으로 구축되는 모든 에이전트 워크플로우에 가져갈 규칙은 다음과 같습니다. 정적 카운트는 바닥(floor)일 뿐, 결코 전체 조사(census)가 아닙니다. '브릿지 3개 발견'이라는 것은 적어도 세 개라는 뜻이며, 나머지는 알 수 없는 영역에 있다는 의미입니다.
REA는 바로 그 아이디어를 중심으로 설계되었습니다. REA의 그래프 문서(graph docs)는 이용할 수 없는 값들을 명시적으로 저장하여 '증거의 부재가 침묵 속에 부재의 증거가 될 수 없도록' 만듭니다. 제가 실행한 모든 동적 채널과 추론된 전송-핸들러 쌍은 자체적인 한계 문자열을 가지고 있었고, 최상위 결과는 발견되지 않은 사실들이 부재를 확립하지 않는다고 반복합니다.
가장 약한 고리는 결과를 읽는 사람입니다. context_bridge_apis: 3이라는 것을 '앱이 3개의 API를 노출한다'로 해석하는 에이전트는 가장 유용한 부분을 버린 것입니다. 만약 제가 REA에 에이전트를 배치하여 기능을 연구하게 한다면, 결론을 내리기 전에 그 기능과 관련된 모든 알 수 없는 목록을 작성하도록 시킬 것입니다. 제 실행에서 그 목록은 grep exposeInMainWorld로 몇 초 만에 사람이 찾을 수 있는 9.8 KB의 파일로 곧장 이어졌습니다.
이것이 진정한 변화입니다. 기능을 복사하는 것은 예전에는 그것이 어떻게 작동하는지에 대한 추측으로 시작했습니다. 이제는 증거로 시작하며, 그 증거는 무엇을 볼 수 없었는지에 대한 목록과 함께 옵니다.
시도하기 전에 (Before you try it)
- 매우 빠르게 움직입니다. 버전 6.2.0, 6.3.0 및 6.4.0이 약 36시간 만에 출시되었으며, 10월 11일과 12일(UTC)에만 130개의 이슈가 열렸습니다. Issue #1387은 현재까지도 열려 있으며, 6.2.0에서는 완료된 대용량 번들에서 6.3.0이 중단되는 것을 기록하고 있습니다. 보고자는 6.4.0이 이를 해결했다고 말합니다. 스크립팅에 사용하려면 버전을 고정하세요.
- 설정이 에이전트 구성을 수정합니다. 계획을 보여주고, 승인을 요청하며, 백업을 유지합니다. 한 HN 댓글 작성자는 여전히 이 붙여넣기 프롬프트 설치를 '
curl | bash방식의 다음 진화'라고 불렀습니다. CLI는 그것 없이도 작동합니다. - 법적 경계는 당신에게 달려 있습니다. REA의 면책 조항은 DRM 우회 및 독점 코드 복사를 배제하며, 라이선스 및 현지 법률이 리버스 엔지니어링을 제한할 수 있음을 명시합니다. 저는 그 이유로 MIT 앱을 선택했습니다. 기능의 작동 방식을 배우는 것과 다른 사람의 코드를 배포하는 것은 다른 행위입니다.
에이전트가 앱에 특정 것이 없다고 보고할 때, 알려지지 않은 것을 보여주게 합니까, 아니면 제로(zero)를 취하게 합니까?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기