Giting.
낮의 Giting

홈 · macOS · iOS · 가상화 · 테스트

vphone-cli 대표 이미지 11,674 / 1,478
분석 리포트실측 정보적용점원문 README

구조와 기능

vphone-cli는 Apple Silicon 맥에서 가상 iPhone을 만들어 부팅하는 CLI다. Apple의 Virtualization.framework 위에서 iOS 펌웨어를 내려받아 패치하고, DFU 복원 모드로 VM에 설치한 뒤, SSH와 VNC로 접속한다. 가상 인스턴스 여러 대를 만들고 스냅샷으로 내보내고 들여올 수 있으며, 펌웨어 변형은 보안 장치를 최소로 늦춘 것부터 완전 탈옥(jb)까지 다섯 단계다. 게스트 안에서 도는 데몬(vphoned)을 크로스 컴파일해 넣고 호스트 소켓으로 프로그램 제어가 가능해, README는 "AI 주도 E2E 테스트"를 용도로 명시한다.

2026-02-27부터 2026-09-10까지 373개 커밋이 쌓였고 42명이 기여했다. 상위 기여자로 zqxwce, Lakr 등이 실측에 잡혔으며 태그 26개로 릴리스를 끊어 왔다. 코드는 언어가 역할별로 나뉜다.

  • Swift 156개 파일, 29,156줄, VM 생성·관리의 중심
  • Objective-C(.m) 20개 파일, 8,465줄, 저수준 연동
  • 셸 스크립트 24개 파일, 6,397줄 · Python 31개 파일, 6,040줄, 펌웨어 다운로드·패치·빌드 보조

전체 333개 파일에 텍스트 67,010줄, 최대 파일은 3,249줄짜리 Objective-C 단일 파일이다. 의존성은 표준 매니페스트 없이 brew 설치 목록(Python 3.13, aria2, ldid-procursus 등)과 서브모듈로 관리된다. 최근 90일 커밋이 122건으로 전체의 3분의 1이라, 약 6개월 이력 내내 개발 속도가 붙어 있다.

하이라이트

실기기 없이 iPhone 환경을 손에 넣는다

시뮬레이터가 아니라 실제 iOS 펌웨어를 부팅하는 VM이라는 점이 이 리포의 존재 이유다. 기기 수급 없이 iOS 연구·테스트 환경을 여러 대 만들고, 스냅샷으로 상태를 되감고, 소켓으로 자동화한다. 스타 11,674와 포크 1,478, 그리고 주변 생태계(웹 제어판·원스크립트 설치·MCP 서버 파생 리포들)가 이 수요의 크기를 보여준다.

이슈 대응이 빠르다

표본 25건 중 21건에 응답이 달렸고 중앙값은 1.9시간이다. 이 규모의 프로젝트치고 이슈가 방치되지 않는다.

에이전트용 문서를 별도로 갖췄다

실측에서 루트 에이전트 문서와 SKILL.md 패키징이 확인됐다. VM 제어 소켓과 묶으면 "AI가 가상 iPhone을 조작하며 E2E 테스트"하는 구성이 문서 수준에서 준비돼 있다는 뜻이다.

주의점

입장료가 뚜렷하다. 맥의 보안 장치(SIP/AMFI)를 늦춰야 설치·구동이 가능하다. 이는 일상 개발 장비에 올릴 도구가 아니라 격리된 연구 머신에 둘 도구라는 뜻이다. iOS 펌웨어를 패치해 부팅하는 성격상 용도도 연구·테스트로 명시돼 있다. 이 경계 안에서 쓰는 것이 안전하다.

검증 장치도 규모 대비 얇다. 하드웨어·가상화 접점 코드치고 테스트 파일이 333개 중 22개(6.6%), CI 워크플로 1개다. 빠른 이슈 대응이 이를 어느 정도 상쇄하지만, 회귀를 자동으로 걸러내는 안전망은 넉넉하지 않다. 의존성 구조 역시 프로브에 표준 매니페스트가 잡히지 않아(서브모듈 1개 + brew 목록), 무엇을 끌어다 쓰는지는 직접 열어 확인해야 한다.

실측 정보

스타 / 포크
11,674 / 1,478
라이선스
MIT
총 규모
333개 파일, 텍스트 67,010줄
최근 90일 커밋 / 전체
122 / 373
기여자 수
42명
이슈 응답
표본 25건 중 21건 응답, 중앙값 1.9시간
CI 워크플로 / 테스트 파일
1개 / 22개
요구 사항
Apple Silicon · macOS 15+ · SIP/AMFI 완화

적용점

  • P1 · 비용 낮음루트 문서와 SKILL.md를 갖춘 에이전트 문서 패키징, 그리고 제어 소켓으로 VM을 프로그램으로 조작하게 한 설계는 사내 도구에도 그대로 참고할 수 있다.
  • P1 · 비용 중간SIP/AMFI 완화 요구는 이 도구를 일상 장비가 아니라 전용 머신에 두라는 뜻이다. 도입한다면 격리된 연구용 맥에서.
  • P2 · 비용 중간의존성이 표준 매니페스트가 아니라 brew 목록+서브모듈로 흩어져 있어, 도입 전 서브모듈과 설치 스크립트를 직접 열어 확인해야 한다.
◈ 참고용

MIT에 최근 90일 122커밋, 이슈 표본 25건 중 21건 응답(중앙값 1.9시간), 기여자 42명: 유지보수는 확실히 살아 있고, 실기기 없이 iOS 환경을 다뤄야 하는 연구·테스트 수요에는 대체재가 드문 물건이다. 참고용에 둔 이유는 입장료다. 맥의 보안 장치(SIP/AMFI)를 늦춰야 하고, iOS 펌웨어를 패치해 부팅하는 연구 지향 도구라 일상 개발 장비에 올릴 물건은 아니다. 하드웨어·가상화 접점 코드치고 테스트 파일 비율이 낮은 점(333개 중 22개)과 서브모듈 기반 의존성도 도입 전 확인 항목이다. 전용 연구 머신이 있다면 이야기가 달라진다.

원문 README

아래는 Lakr233/vphone-cli 저장소의 README 원문입니다(GitHub 렌더, 수집 시점 기준). 저작권은 원 프로젝트에 있으며 원 저장소의 라이선스를 따릅니다. GitHub에서 보기

README 전문 펼치기

vphone-cli

Boot a virtual iPhone via Apple's Virtualization.framework using PCC research VM infrastructure.

poc

Prerequisites

Host:

Dependencies:

brew install python@3.13 aria2 wget gnu-tar openssl@3 ldid-procursus sshpass keystone cmake libusb ipsw zstd

Install

brew install zqxwce/tap/vphone-cli

Build

git clone --recurse-submodules https://github.com/Lakr233/vphone-cli.git

./scripts/setup_tools.sh      # install deps, build toolchain submodules, create the Python venv
./scripts/build.sh            # build + sign vphone-cli, bundle the .app, cross-compile vphoned

cd .build/vphone-cli.app/Contents/MacOS/
vphone-cli --help

Quick Start

One command creates a VM end-to-end (download → patch → DFU restore → CFW install → first boot):

vphone-cli vm create myphone -V jb        # -V / --variant

vphone-cli vm launch myphone

Commands

vphone-cli vm create runs the whole pipeline; the individual steps below let you drive it manually or re-run one stage.

Manage

vphone-cli vm list                         # list VMs (--json for scripting)
vphone-cli vm info myphone                  # show one VM
vphone-cli vm new myphone                   # create an empty bundle (cpu/mem/disk options)
vphone-cli vm config myphone --cpu 8 --memory 8192
vphone-cli vm clone myphone myphone-2       # fast APFS clone, fresh device identity
vphone-cli vm export myphone --out myphone.tzst   # zstd fast by default (--max = xz -9); --out may be a dir (auto-names <vm>.tzst/.txz); skips restore dir + staging files
vphone-cli vm import myphone.tzst --name restored
vphone-cli vm rename myphone iphone16
vphone-cli vm delete iphone16

Build a VM manually (what vm create automates)

vphone-cli vm new myphone                              # 1. empty bundle
vphone-cli fw prepare myphone --iphone-version 26.1     # 2. download + merge IPSWs
vphone-cli fw patch myphone --variant jb                # 3. patch the boot chain

vphone-cli vm launch myphone --dfu &                    # 4. boot into DFU (background)
vphone-cli restore myphone --get-shsh                   #    fetch SHSH
vphone-cli restore myphone                              #    DFU restore
vphone-cli vm stop myphone                              #    stop the DFU boot

vphone-cli cfw install myphone --variant jb             # 5. install CFW (host-mount; asks for sudo)
vphone-cli vm launch myphone                            # 6. first boot

Update to a newer iOS by pointing fw prepare at an IPSW: --iphone-source /path/to.ipsw --cloudos-source /path/to.ipsw.

Firmware Variants

Five patch variants with increasing security bypass — pass one to --variant:

Variant Boot Chain CFW Notes
less 4 patches 2 phases Patchless — keeps iOS mitigations enabled
regular 42 patches 10 phases AMFI/SSV/Img4/TXM bypass
dev 53 patches 12 phases + TXM entitlement/debug bypass
jb 113 patches 14 phases + full jailbreak (Sileo, TrollStore auto-install on first boot)
exp 141 patches 18 phases JB superset + anti-VM-detection research patches

See research/0_binary_patch_comparison.md for the per-component breakdown.

Running & Connecting

  • SSH (jailbreak): ssh -p 22222 mobile@<vm-ip> (password alpine)
  • SSH (regular/dev): ssh -p 22222 root@<vm-ip>
  • VNC: vnc://<vm-ip>:5901

Locations

Everything vphone-cli creates lives under ~/.vphone/ — kept outside the repo and the .app so the signed bundle stays portable. Redirect the whole tree with $VPHONE_ROOT:

Path Contents
~/.vphone/ The per-user data root — override the entire location with $VPHONE_ROOT.
~/.vphone/VMs/ VM bundles — one directory per VM. This is the library; override with $VPHONE_LIBRARY_ROOT.
~/.vphone/ipsws/ Downloaded iPhone + cloudOS IPSWs, cached and reused across VMs.
~/.vphone/tools/ Cached APFS seal-volume artifacts (apfs_sealvolume_<version>) fetched during fw prepare.
~/.vphone/debs/ Cached .deb packages the jb/exp CFW install lays into the guest (Sileo, apt, …).
~/.vphone/venv/ Auto-provisioned Python environment (see Python runtime; override with $VPHONE_VENV_DIR).

Precedence: the per-item overrides ($VPHONE_LIBRARY_ROOT, $VPHONE_VENV_DIR) win over $VPHONE_ROOT, which wins over the ~/.vphone default. The ipsws/, tools/, and debs/ caches always sit directly under whichever root is active.

SIP/AMFI Relaxation

Option A — fully disable SIP, then disable AMFI via boot-arg (most permissive).

In Recovery (long-press power → Terminal):

csrutil disable
csrutil allow-research-guests enable

Then reboot into macOS and set the AMFI boot-arg (needs SIP fully off to take effect):

sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"   # reboot after

Option B — keep SIP on (debug-only relaxed), then allowlist the binary with amfidont (leaves AMFI enabled system-wide).

In Recovery:

csrutil enable --without debug
csrutil allow-research-guests enable

Then reboot into macOS and:

vphone-amfidont         # .build/vphone-cli.app/Contents/Resources/vphone-amfidont for local builds

Tested Environments

Host iPhone CloudOS
Mac16,11 27.0b2 17,3_18.6.2_22G100 26.1-23B85
Mac16,8 26.5.1 17,3_26.0_23A341 26.1-23B85
Mac16,8 26.5.1 17,3_26.0.1_23A355 26.1-23B85
Mac16,12 26.3 17,3_26.1_23B85 26.1-23B85
Mac16,12 26.3 17,3_26.3_23D127 26.1-23B85
Mac16,12 26.3 17,3_26.3_23D127 26.3-23D128
Mac16,12 26.3 17,3_26.3.1_23D8133 26.3-23D128
Mac16,11 26.2 17,3_26.4_23E246 26.4-23E5207q
Mac16,11 26.2 17,3_26.5_23F77 26.4-23E5207q
Mac16,11 27.0b2 17,3_26.5.2_23F84 26.4-23E5207q
Mac16,6 26.4.1 17,3_26.6_23G71 26.4-23E5207q
Mac16,11 27.0b2 17,3_26.6.1_23G83 26.4-23E5207q
Mac16,6 26.6.1 17,3_26.6.2_23G90 26.4-23E5207q
Mac16,11 27.0b2 17,3_27.0_24A5380h 26.4-23E5207q
Mac16,6 26.4.1 17,3_27.0_24A5390f 26.4-23E5207q
Mac16,6 26.6.1 17,3_27.0_24A5408d 26.4-23E5207q
Mac16,11 27.0b2 17,3_27.0_24A5418b 26.4-23E5207q
Mac16,11 27.0b2 17,3_27.0_24A5424a 26.4-23E5207q
Mac16,11 27.0b2 17,3_27.0_24A5430a 26.4-23E5207q
Mac16,6 26.6.1 17,3_27.0_24A435 26.4-23E5207q

FAQ

zsh: killed ./vphone-cli — AMFI/debug restrictions aren't bypassed; see Prerequisites (amfi_get_out_of_my_way=1 or amfidont).

Virtualization is not available on this hardware — your Mac is itself a VM; PV=3 guest boot can't nest. Use a non-nested macOS 15+ host.

Stuck on "Press home to continue" — connect via VNC and right-click (two-finger click) to simulate the home button.

System apps won't install — during iOS setup, don't pick Japan or the EU as your region (extra regulatory checks the VM can't satisfy); pick e.g. United States.

App crashes on launch with EXC_GUARD / GUARD_TYPE_MACH_PORT — re-patch with vphone-cli fw patch <name> --variant <v> --force-exc-guard, then re-restore/install (#291). Always on for iOS 18 bases.

Install a .ipa/.tipa — use the running VM's Install menu (drag-drop or file picker).

cfw install hangs re-signing a system binary (e.g. Campo), memory climbing unbounded — known bug in ldid-procursus up to 2.1.5-procursus7 (the current Homebrew stable): bytes(uint64_t) calls __builtin_clzll(0) with no zero-guard, which is undefined behavior, and on this build resolves to a 0-length that underflows an unsigned loop counter — ldid spins writing one byte at a time into a growing buffer instead of terminating. Triggered by any entitlements plist containing an integer value of exactly 0 (some real Apple system binaries have these). Fixed upstream but not yet in a tagged release; rebuild from source: brew install --HEAD ldid-procursus && brew link --overwrite ldid-procursus. Kill the hung ldid process first (sudo kill -9 <pid>) if you already hit it.

Automation

vphone-cli exposes a host control socket (<bundle>/vphone.sock) for programmatic control — screenshots, touch, swipes, hardware keys, clipboard — each action returning an inline screenshot for AI-driven E2E testing. See vphone-mcp for an MCP server wrapping it.

Acknowledgements

의견

판정에 대한 반론, 도입 경험, 정정 제보를 환영합니다. GitHub 계정으로 참여합니다.

이것도 재봤습니다

전체 리포트 →