형님이 요청하신 대로, 이번에는 REA를 직접 설치해서 써 보는 실전편입니다. 지난번 글이 REA의 구조와 설계 철학을 뜯어본 글이었다면, 이번 글은 “그래서 내 컴퓨터에서 어떻게 돌리고, 결과를 어떻게 읽고, 어디에 쓰면 되는가”에 집중합니다. 실제로 블로그 작업용 리눅스 박스에 REA 6.4.0과 Ghidra 12.1.4를 설치하고, 직접 만든 작은 라이선스 검사 프로그램(crackme)을 분석해 봤습니다. 아래 명령과 출력은 모두 그 실행에서 나온 것입니다.

1. 리버스 엔지니어링, 3분 요약
개발자가 쓴 C 코드는 컴파일러를 거치며 CPU가 읽는 기계어, 즉 바이너리가 됩니다. 이 과정에서 변수 이름, 주석, 함수 구조 같은 “사람을 위한 정보”가 대부분 사라지죠. 리버스 엔지니어링(역공학)은 이 바이너리를 거꾸로 읽어서 “이 프로그램이 실제로 무슨 일을 하는지”를 다시 알아내는 작업입니다.
전통적으로는 Ghidra, IDA, Hopper 같은 도구를 사람이 직접 띄워서 함수 목록을 훑고, 문자열을 검색하고, 참조를 따라가며 추론했습니다. 시간과 숙련이 많이 드는 일이죠. REA는 이 작업을 AI 에이전트가 대신 손을 움직이게 만드는 도구입니다.
| 단계 | 무엇이 일어나나 | 무엇이 남고 무엇이 사라지나 |
|---|---|---|
| 컴파일 | C 소스 → 기계어 바이너리 | 동작은 남고, 변수명·주석·구조는 대부분 사라짐 |
| 최적화(-O1 등) | 작은 함수 인라인, 루프 변형 | 원래 함수 경계가 아예 없어지기도 함 (이번 실습에서 실제로 확인) |
| strip | 심볼 테이블 제거 | 함수 이름까지 사라져 FUN_00101160 같은 이름만 남음 |
| 디스어셈블 | 기계어 → 어셈블리 | 명령 단위로 정확하지만 읽기 어려움 |
| 디컴파일 | 어셈블리 → C 비슷한 의사코드 | 읽기 쉽지만 원래 소스와 같지는 않음 |
2. REA는 무엇이 다른가: “LLM”이 아니라 “에이전트의 손”
이름 때문에 오해하기 쉬운데, REA 자체가 언어 모델을 내장한 챗봇은 아닙니다. 저장소의 설명을 그대로 옮기면 REA는 “바이너리, 애플리케이션, 런타임 동작 전반을 위한 하나의 MCP”입니다. 즉 Claude Code, Codex, Cursor, Gemini CLI 같은 에이전트에 MCP 서버로 붙어서, 에이전트가 Ghidra·Hopper·IDA 같은 분석 엔진을 안전한 규격으로 호출할 수 있게 해 줍니다. 같은 기능을 터미널의 rea 명령으로도 쓸 수 있고요.
그래서 실전에서는 두 가지 쓰는 법이 있습니다. ① 에이전트에게 “이 앱의 검색 기능이 어떻게 동작하는지 증거와 함께 보여 줘”처럼 자연어로 맡기는 방식, ② 사람이 CLI로 직접 analyze, trace, function을 두드리는 방식. 두 방식 모두 결과는 같은 Evidence(증거) JSON 형식으로 나옵니다. 어떤 엔진이 무엇을 근거로 이런 결론을 냈는지, 무엇은 아직 모르는지(한계·미확인 항목)가 함께 붙어 나온다는 점이 핵심 차별점입니다.

| 구분 | 전통적인 수작업 RE | REA + 에이전트 |
|---|---|---|
| 도구 조작 | 사람이 GUI에서 직접 클릭 | 에이전트가 MCP 도구로 호출 (CLI도 동일 기능) |
| 결과 형태 | 화면에 보이는 것, 메모 | Evidence JSON: 대상 해시·엔진·신뢰도·한계 포함 |
| 재현성 | 사람마다 다름 | 스냅샷으로 같은 질문의 결과를 재사용 |
| 엔진 | 도구 하나에 종속 | Hopper·Ghidra·IDA 중 설치된 것을 선택 |
| 판단 | 사람 | 에이전트가 가설을 세우고, 사람이 증거로 검증 |
구조와 증거 봉투(Evidence) 설계를 깊게 보고 싶으시면 지난 글 REA 아키텍처·운영 심층 해부에서 확인하시면 됩니다. 이 글에서는 바로 손을 움직여 보겠습니다.
3. 준비물: 무엇이 있어야 돌아가나
10월 12일 기준 최신 npm 버전은 rea-agents 6.4.0(10월 11일 공개), 라이선스는 MIT입니다. GitHub 별은 11만 개를 넘었습니다. 요구사항은 “무엇을 분석하느냐”에 따라 달라집니다.
| 항목 | 저장소가 밝힌 요구사항 | 비고 |
|---|---|---|
| Node.js | 22.x(22.19 이상), 24.x(24.11 이상), 26 이상 + npm | 23·25와 프리릴리스는 미지원 |
| 네이티브 분석 엔진 | Hopper, Ghidra, IDA 중 하나 | 정적 JavaScript·.NET 분석은 엔진 없이 가능 |
| Ghidra | 12.1.x + 64비트 full JDK 21 이상 | Linux x64/arm64, macOS, Windows x64는 실험적(P0, 읽기 전용) |
| Hopper | 상용 소프트웨어(데모 모드 있음) | setup이 승인 후 설치를 제안할 수 있음(macOS) |
| IDA | 기존 설치본 사용 | 저장소의 IDA 가이드에 설정법 |
| 호스트(Hopper 기준) | macOS 12+, Ubuntu 24.04+, Fedora 41+, Nobara 44+, 64비트 Arch, CachyOS, Omarchy | 엔진마다 호스트 조건이 따로 있음 |
분석 대상별로 필요한 것도 정리해 두면 편합니다. README의 표에서 실전에서 자주 쓸 만한 것만 추렸습니다.
| 대상 | REA가 돌려주는 것 | 추가로 필요한 것 |
|---|---|---|
| 네이티브 바이너리 | 의사코드, 어셈블리, 문자열, 심볼, 호출·참조 | Hopper / Ghidra / IDA |
| JavaScript / Electron | 모듈, import, 소스맵, 라우트, IPC | Node.js와 npm |
| .NET 어셈블리 | 메타데이터, CIL 명령, 빌드 비교 | 없음(정적 분석만) |
| Android APK | 매니페스트, 클래스, 디컴파일된 메서드 | 헤드리스 JADX + full JDK |
| 웹사이트 | 페이지 구조, 스크립트, 네트워크 관찰 | Chrome 계열 브라우저 |
| 펌웨어 | 영역, 추출 결과, 네이티브 분석 연결 | Linux의 Binwalk / Unblob |
| 프로세스 동작 | 터미널 출력, 종료 코드, 파일 변화, 실행 비교 | Linux/macOS의 네이티브 PTY |
4. 설치: 실제로 친 명령 그대로
이번 실습 환경은 Debian 13 x64 박스입니다. 박스의 기본 Node가 20이라 요구사항(22.19 이상)에 맞지 않아서, Node 22.20.0을 따로 받아 PATH 앞에 두었습니다. 여러분 PC에 Node 22/24가 이미 있다면 이 부분은 건너뛰시면 됩니다.
4-1. REA CLI 설치
# Node 버전 확인 (22.19+ / 24.11+ / 26+ 필요)
node -v # v22.20.0
# 전역 설치 후 버전 확인
npm install --global rea-agents
rea --version # 6.4.0
rea --help # 전체 명령 목록
4-2. 분석 엔진(Ghidra) 준비
REA는 Ghidra나 Java를 대신 설치하지 않습니다. 공식 배포본을 직접 풀고 경로만 알려 주면 됩니다. 저는 Ghidra 12.1.4 공식 zip과 Temurin JDK 21을 받아 풀었습니다.
export GHIDRA_INSTALL_DIR=/path/to/ghidra_12.1.4_PUBLIC
export JAVA_HOME=/path/to/jdk-21 # java가 PATH에 있으면 생략 가능
# 내가 쓰려는 엔진만 콕 집어 점검
rea doctor --provider ghidra --json | jq .healthy
# → true
rea doctor를 돌리면 설치된 모든 에이전트·엔진을 감사하기 때문에, 하나만 빠져도 "healthy": false가 나옵니다. 실제로 이 박스에서도 전체 감사는 “Debian 13은 Hopper 기준 지원 호스트가 아님”, “Hopper 없음” 등으로 false였지만, --provider ghidra로 범위를 좁히자 Node·Ghidra 버전·analyzeHeadless·네이티브 디컴파일러·JDK 21까지 9개 항목이 모두 healthy였습니다. 내가 할 작업 기준으로 점검하세요.4-3. 에이전트에 연결하기 (선택)
에이전트와 함께 쓰려면 npx rea-agents setup을 실행합니다. 쓸 에이전트를 고르고, 바뀔 설정을 검토해 승인하면 MCP 서버 등록과 작업 지침(skill)이 설치되고 기존 설정은 백업됩니다. 끝나면 에이전트를 재시작합니다.
npx rea-agents setup
# 특정 에이전트 연결 상태만 점검
rea doctor --client claude_code --json
| setup이 지원하는 에이전트(일부) | –client 값 |
|---|---|
| Claude Code / Claude Desktop | claude_code / claude_desktop |
| Codex | codex |
| Cursor | cursor |
| Gemini CLI | gemini_cli |
| VS Code / Windsurf | vscode / windsurf |
| GitHub Copilot CLI | copilot_cli |
| Qwen Code / OpenCode | qwen_code / opencode |
| Grok Build | grok_build |
5. 실습: 직접 만든 crackme를 REA로 해부하기
분석 대상은 남의 프로그램이 아니라 제가 직접 짠 20줄짜리 C 프로그램입니다. 8글자 키를 받아 각 글자를 0x5A와 XOR한 합계, 첫 글자, 마지막 글자를 검사하는 전형적인 연습용 “시리얼 키 검사기”죠. 일부러 -O1 최적화로 빌드했습니다. 이제 소스를 모른다고 가정하고 바이너리만 보겠습니다.

gcc -O1 -o licensecheck licensecheck.c
./licensecheck ABC # ACCESS DENIED
5-1. 전체 개요: analyze
rea analyze $PWD/licensecheck --provider ghidra \
--snapshot $PWD/out/snap.json --json > out/analyze.json
첫 실행은 약 10.9초 걸렸습니다. Ghidra가 임시 프로젝트에 바이너리를 가져와 자동 분석을 마친 뒤 결과를 돌려주기 때문입니다. 결과 JSON에서 먼저 볼 곳은 아래와 같습니다.
| 필드 | 이번 결과 | 읽는 법 |
|---|---|---|
| subject.format / architecture | elf / x86_64 | 엔진이 인식한 파일 형식 |
| subject.digest.sha256 | 2aeddde4… | 이 증거가 ‘어떤 바이트’에 대한 것인지 고정 |
| normalized_result | 세그먼트 33개, 함수 26개, 문자열 76개 | 전체 지도. 어디부터 볼지 정하는 용도 |
| provider / analysis_profile | rea-workflow ← Ghidra 12.1.4 | 어떤 엔진·설정으로 얻은 결과인지 |
| confidence / limitations | derived / 한계 문구 | 확정 사실인지, 엔진이 도출한 추론인지 구분 |
5-2. 단서 찾기: 문자열에서 거꾸로 추적(trace)
실전 RE의 정석은 “눈에 보이는 문자열에서 시작해 코드로 거슬러 올라가기”입니다. 화면에 뜨는 “ACCESS”를 검색해 봅니다.
rea trace $PWD/licensecheck ACCESS --provider ghidra --snapshot $PWD/out/snap.json --json
결과에는 “ACCESS GRANTED”(0x10201e)와 “ACCESS DENIED”(0x10202d) 두 문자열, 그리고 각 문자열을 참조하는 코드 위치(0x1011ed, 0x1011bc)가 모두 main 함수(0x101160) 안에 있다는 사실이 나옵니다. residual_unknowns는 빈 배열이었고, truncated는 false였습니다. 즉 “빠뜨린 것 없이 다 찾았다”는 뜻입니다.
5-3. 함수 하나를 통째로: function
rea function $PWD/licensecheck main --provider ghidra --snapshot $PWD/out/snap.json --json \
| jq -r .normalized_result.pseudocode
function은 함수 하나의 “서류철(dossier)”을 돌려줍니다. 이번 결과 JSON은 약 93KB였고 의사코드, 어셈블리, 기본 블록, 호출하는 함수(puts, strlen), 참조 문자열, 한계 문구가 모두 들어 있었습니다. 핵심인 의사코드는 이렇습니다(Ghidra 출력 원문 일부).
sVar1 = strlen((char *)__s);
if (sVar1 == 8) {
iVar3 = 0;
pbVar4 = __s;
do {
iVar3 = iVar3 + (uint)(*pbVar4 ^ 0x5a);
pbVar4 = pbVar4 + 1;
} while (pbVar4 != __s + 8);
if (((iVar3 == 0x141) && (*__s == 0x4d)) && (__s[7] == 0x57)) {
puts("ACCESS GRANTED");
return 0;
}
}
읽어 보면 규칙이 그대로 드러납니다. 길이 8, 각 바이트를 0x5A와 XOR한 합이 0x141, 첫 글자 0x4D(“M”), 마지막 글자 0x57(“W”). 이 조건을 만족하는 키 MORIC26W를 넣자 실제로 ACCESS GRANTED가 출력됐습니다.

5-4. 어셈블리와 의사코드를 나란히 읽기
의사코드는 읽기 쉽지만 “엔진의 해석”입니다. 중요한 판단은 어셈블리로 한 번 더 확인하는 습관이 좋습니다. 같은 function 결과의 assembly 필드에서 해당 부분을 찾으면 XOR EAX, 0x5a → ADD ECX, EAX 루프, CMP ECX, 0x141, CMP byte ptr [RBX], 0x4d, CMP byte ptr [RBX + 0x7], 0x57가 정확히 대응합니다. REA도 한계 항목에 “의사코드와 어셈블리는 Ghidra 고유의 표현이며 원본 소스가 아니다”라고 적어 둡니다.

5-5. 실습 중 만난 “함정” 두 가지
| 상황 | 실제로 일어난 일 | 교훈 |
|---|---|---|
rea decompile ./licensecheck check_key |
실패: “Unknown Ghidra procedure name or address: check_key” (code invalid_request) | -O1 최적화로 check_key가 main 안에 인라인되어 함수 자체가 없어짐. 소스 기준 함수명을 가정하지 말고 trace로 찾은 실제 함수를 볼 것 |
| strip한 바이너리에 trace | 같은 두 참조를 찾았지만 함수 이름이 FUN_00101160 | 심볼이 없으면 이름 대신 주소로 추적. 실제 레거시 바이너리는 대부분 이런 상태 |
5-6. 스냅샷으로 두 번째부터 빠르게
--snapshot을 붙이면 성공한 결과가 로컬 파일(소유자만 읽기 권한)에 저장되고, 대상 바이트·작업·파라미터·엔진·설정이 정확히 같으면 재사용됩니다. 같은 analyze를 다시 돌리자 10.9초 → 1.2초로 줄었습니다. 문서에 따르면 CLI는 정확히 일치하는 캐시가 있으면 엔진 프로세스를 띄우기 전에 읽습니다.
5-7. 재구현이 원본과 같은지 검증: capture-process
리버스 엔지니어링의 결과물이 “다시 만든 코드”라면, 원본과 똑같이 동작하는지 증명해야 합니다. 의사코드를 보고 파이썬으로 같은 검사기를 다시 짠 뒤, 두 프로그램을 같은 입력으로 실행해 비교했습니다.
# scen-orig.json
{"executable":"./licensecheck","arguments":["MORIC26W"],"working_directory":"."}
# scen-recon.json
{"executable":"python3","arguments":["reconstructed.py","MORIC26W"],"working_directory":"."}
rea capture-process ./scen-orig.json --json > cap-orig.json
rea capture-process ./scen-recon.json --json > cap-recon.json
rea compare-process-captures cap-orig.json cap-recon.json
비교 결과 터미널 출력(terminal)과 상호작용(interaction)은 unchanged, 두 쪽 모두 종료 코드 0이었습니다. 다만 최상위 status는 changed였고, 첫 차이(first_divergence)가 exit 차원에서 잡혔습니다. 양쪽 종료 정보를 보면 코드와 시그널은 같고 정착 시간만 69ms 대 70ms로 달랐는데, 비교가 10ms 단위로 시간을 묶기 때문에 이 차이가 잡힌 것으로 보입니다. 그래서 status 한 줄만 보지 말고 차원별 결과를 같이 읽어야 합니다. 그리고 문서는 Process Capture가 사용자 권한으로 실제 실행하며 샌드박스가 아니라고 분명히 적고 있습니다. 정체를 모르는 프로그램에는 쓰지 마세요.
6. 결과를 읽는 법: 체크리스트
| 먼저 볼 것 | 왜 중요한가 |
|---|---|
| subject.digest.sha256 | 내가 분석한 파일이 맞는지, 버전이 바뀌지 않았는지 |
| provider · analysis_profile | 같은 질문이라도 엔진·설정이 다르면 결과가 다를 수 있음 |
| confidence · authority | 관찰(observed)인지 도출(derived)인지 구분 |
| limitations | 엔진이 못 보는 영역(간접 호출 등)이 명시됨 |
| residual_unknowns · truncated | 빠졌거나 잘린 결과가 있는지 |
| error.code · remediation | 실패 시 원인과 다음 행동 제안이 함께 옴 |
7. 에이전트에게 맡길 때는 이렇게
setup 후 에이전트에게는 README의 예시처럼 “기능을 이해하고, 증거를 보여 주고, 내 프로젝트용으로 비슷하게 만들어 줘” 형태로 요청하면 됩니다. REA는 MCP 프롬프트로 여섯 가지 조사 흐름도 제공합니다.
| MCP 프롬프트 | 언제 쓰나 |
|---|---|
investigate_feature |
기능 하나를 발견부터 함수 증거까지 추적 |
compare_application_versions |
두 배포본(구버전/신버전) 비교 |
verify_reconstruction |
재구현이 원본과 맞는지 평가 |
trace_crash |
크래시 증상을 정적 경로·실행 캡처와 연결 |
audit_residual_unknowns |
아직 풀리지 않은 미확인 항목 점검 |
prepare_bounded_process_capture |
실행 실험을 하기 전에 범위부터 설계 |
예시 요청)
이 폴더의 licensecheck 바이너리가 어떤 키를 받아들이는지 REA로 조사해 줘.
관찰한 사실, 추론, 아직 모르는 것을 구분하고, 근거가 된 주소를 함께 적어 줘.
inspect-analysis-view로 요약·페이지 단위만 보라고 권합니다(예: 334MB짜리 Obsidian 증거 파일이 약 10KB 요약으로). 또 README FAQ대로 REA는 대상을 업로드하지 않지만, 에이전트가 받은 결과는 모델 제공사의 데이터 정책을 따릅니다. 회사 코드라면 이 부분을 꼭 확인하세요.8. 실전 활용 시나리오

| 시나리오 | 이렇게 쓴다 | 주의 |
|---|---|---|
| 소스가 사라진 레거시 바이너리 | 사내 옛 도구의 계산 로직을 trace → function으로 찾고, 재구현 후 capture-process로 출력 비교 | 회사가 권리를 가진 프로그램인지 먼저 확인 |
| 호환·연동(interoperability) | 파일 포맷·통신 규약을 문자열·참조로 추적해 내 프로그램과 연동 | 필요한 범위만, 연동 목적으로만 |
| 자사 소프트웨어 보안 점검 | 배포 빌드에 하드코딩된 키·디버그 문자열·숨은 분기가 남았는지 확인 | 소스와 배포물이 다를 수 있다는 점이 오히려 점검 포인트 |
| Electron/웹 앱 구조 파악 | analyze-javascript-application으로 모듈·IPC 관계를 정적 분석 (엔진 불필요) | 결과가 매우 크므로 view로 요약 |
| CTF·학습 | 이 글처럼 직접 만든 crackme나 CTF 공식 문제로 의사코드와 어셈블리 읽는 연습 | 대회 규칙이 허용하는 범위에서 |
저장소의 쇼케이스도 실전 감을 잡는 데 좋습니다. 예컨대 DX-Ball 사례에서는 사운드 팬 계산 함수를 의사코드에서 C로 재구성해, 원본 x86 대상 3,205개 케이스를 통과하고 컴파일된 함수 바이트 63개를 그대로 재현했다고 소개합니다.
9. 한계와 팁
| 한계·특징 | 실전 팁 |
|---|---|
| 의사코드는 원본 소스가 아님 | 중요한 조건은 어셈블리로 재확인 |
| 최적화·strip이 구조를 지움 | 소스 기준 함수명 대신 문자열·참조에서 출발 |
| 간접 호출은 참조로 표현되지 않을 수 있음 | limitations 항목을 꼭 읽기 |
| 큰 바이너리는 시작이 오래 걸림 | REA_GHIDRA_STARTUP_TIMEOUT_MS로 시작 데드라인 조정 |
| Process Capture는 샌드박스가 아님 | 내가 신뢰하는 실행 파일에만 사용 |
| 빠르게 변하는 프로젝트 | rea update 또는 npx rea-agents@latest setup으로 자주 갱신 |
10. 법과 윤리: 분석해도 되는 것만

REA 저장소도 면책 문구에서 “합법적인 리버스 엔지니어링 연구·분석·재구성”을 위한 도구이며, 필요한 권한 확보와 법률 준수는 사용자 책임이라고 밝힙니다. 원칙은 단순합니다. 내가 권리를 가진 소프트웨어, 또는 분석이 명시적으로 허용된 대상만 다루세요.
한국 저작권법은 프로그램의 호환에 필요한 정보를 얻기 위한 “프로그램코드역분석”을 일정 조건에서 허용하는 규정(제101조의4)을 두고 있습니다. 대체로 정당한 권한으로 프로그램을 이용하는 사람이, 호환에 필요한 정보를 쉽게 얻을 수 없을 때, 필요한 부분에 한해 하는 경우로 이해되며, 그렇게 얻은 정보를 다른 목적으로 쓰거나 실질적으로 비슷한 프로그램을 만드는 데 쓰는 것은 허용 범위를 벗어날 수 있습니다. 기술적 보호조치를 무력화하는 행위도 별도로 금지 대상이 될 수 있습니다. 이 글은 법률 자문이 아니므로, 업무로 다룬다면 라이선스·이용약관을 확인하고 필요하면 전문가와 상의하시길 권합니다.
| 대체로 괜찮은 경우 | 조심하거나 피해야 할 경우 |
|---|---|
| 내가 만든 프로그램, 회사가 권리를 가진 레거시 | 라이선스가 역분석을 금지하는 상용 프로그램 |
| CTF 공식 문제, 학습용으로 공개된 crackme | 크랙·키 생성기 제작과 배포 |
| 계약으로 허가받은 보안 점검 | 분석으로 얻은 코드를 그대로 복제해 경쟁 제품 개발 |
| 호환을 위한 최소한의 포맷·규약 파악 | 보호조치 우회 방법의 공개·배포 |
11. 이번 실습에서 실제로 돌린 것과 안 돌린 것
| 항목 | 결과 |
|---|---|
| rea 6.4.0 설치, –help, doctor(–provider ghidra) | 실행함 · Ghidra 범위 healthy |
| analyze / search / trace / function (Ghidra 12.1.4) | 실행함 · 위 결과 그대로 |
| decompile check_key | 실행함 · 인라인으로 실패 (함정 사례) |
| strip한 바이너리 trace | 실행함 · FUN_00101160으로 추적 |
| 스냅샷 재사용 | 실행함 · 10.9초 → 1.2초 |
| capture-process + compare-process-captures | 실행함 · 출력·종료 코드 동일 |
| 에이전트(LLM) 연결과 자연어 조사 | 이번엔 실행 안 함 (setup은 에이전트 설정을 바꾸므로 생략) |
| Hopper / IDA, Android·펌웨어 분석 | 실행 안 함 |
마무리
REA를 직접 써 보니, 가장 큰 장점은 “AI가 알아서 해킹해 준다”가 아니라 엔진의 결과를 증거 단위로 정리해서, 사람이든 에이전트든 같은 근거로 판단하게 해 준다는 점이었습니다. 20줄짜리 crackme도 trace → function → 어셈블리 확인 → 재구현 → 실행 비교라는 흐름을 그대로 밟을 수 있었고, 이 흐름은 소스 없는 사내 레거시 도구에도 똑같이 적용됩니다. 처음 시작하신다면 직접 만든 작은 프로그램을 -O0, -O2, strip 버전으로 각각 빌드해서 결과가 어떻게 달라지는지 비교해 보세요. 리버스 엔지니어링 감각이 가장 빨리 붙는 방법입니다.