Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
px-vm — PerimeterX의 바이트코드 VM을 위한 리버스 엔지니어링 툴킷으로, CFG 기반 디스어셈블러, 5계층 복호화 파이프라인, opcode 테이블 재구성, 봇 탐지 핑거프린팅에 대한 보안 연구를 위한 스택 에뮬레이션 클리너를 특징으로 합니다. | Kitploit
도구/GitHubGitHub/b9ph0met/px-vm
Dynamic Analysis (Sandboxing)IDS/IPS EvasionReverse EngineeringWeb SecurityMalware AnalysisCryptographyBinary AnalysisPapers & ResearchLearning & EducationAnti-BotFingerprint Spoofing
55144개월 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
GitHub
b9ph0met/px-vm

px-vm

PerimeterX의 바이트코드 VM을 위한 리버스 엔지니어링 툴킷으로, CFG 기반 디스어셈블러, 5계층 복호화 파이프라인, opcode 테이블 재구성, 봇 탐지 핑거프린팅에 대한 보안 연구를 위한 스택 에뮬레이션 클리너를 특징으로 합니다.

저장소 보기

PerimeterX Auditor VM 분석

요약

이 저장소는 PerimeterX의 auditor.js의 리버스 엔지니어링을 문서화합니다. 이 파일은 PX의 봇 탐지 파이프라인에서 2차 핑거프린팅 레이어로 사용되는 바이트코드 가상 머신입니다. 이 분석은 다음을 포함합니다:

  • 바이트코드 추출 및 5계층 복호화 파이프라인
  • Opcode 테이블 재구성 (107 기본 + 40 허니 + 24 패딩 + 16 슈퍼명령어 그룹)
  • 상수 풀 복호화 (1230개 항목, 1095개 암호화된 문자열)
  • 슈퍼명령어 서브 디스패치 해석을 포함한 CFG 기반 디스어셈블러
  • 읽기 쉬운 의사 코드를 생성하는 스택 에뮬레이션 클리너
  • 안티 분석 기법: 허니 opcode, 중첩 명령어, 코드 무결성 해싱

참고: 이 저장소는 (정적) VM 버전 하나만 다루며 보안 연구 및 분석 목적으로 작성되었습니다. 동적 솔버나 프로덕션 솔버 구현을 포함하지 않습니다.

배경

2026년 4월 2일 목요일, PerimeterX는 봇 탐지 파이프라인의 일부로 새로운 바이트코드 VM을 배포했습니다.

내부 구성

auditor.js는 일반적인 PX 센서 스크립트처럼 보이지 않습니다. 일반적인 난독화된 속성 조회 및 수집기 함수 대신:

  • 8개의 거대한 base64 문자열 (_fg0 ~ _fg7): 변수에 분할된 암호화된 VM 프로그램
  • XOR 복호화 함수 (_dp): 사이트별 키 (_pk)를 사용하여 프로그램 JSON을 푸는 함수
  • Fisher-Yates 셔플: 빌드마다 바이트코드 값이 달라지도록 opcode 테이블을 순열 변환
  • 107개 이상의 case 핸들러를 포함한 디스패치 루프: VM 인터프리터
  • BigInt 산술: 핑거프린트 출력의 RSA 암호화에 사용
  • 코드 무결성 해시 (_0x8df7): VM 자체 소스를 해싱하여 복호화 키를 도출하므로, 어떤 수정이든 바이트코드 복호화를 조용히 깨뜨림

1단계: 바이트코드 추출

VM 프로그램은 8개 변수에 분할된 후, _dp()를 통해 사이트별 XOR 암호를 사용하여 복호화되며, 키는 _pk입니다:

root@kitploit:~
var _pk = 893686289;
function _dp(_b) {
    var _r = atob(_b), _o = new Array(_r.length);
    for (var _i = 0; _i < _r.length; _i++) {
        _o[_i] = String.fromCharCode(
            _r.charCodeAt(_i) ^ (((_pk >>> (8 * (_i % 4))) ^ Math.imul(_i + 1, 0x6B8B4567)) & 0xFF)
        );
    }
    return _o.join("");
}

결과는 난독화된 두 문자 키 이름(예: 시드의 경우 "uo", 논스의 경우 "dk")을 가진 JSON 객체입니다. 매핑 테이블이 이를 표준 이름으로 변환합니다.

root@kitploit:~
node extractor.js
# -> program.json

프로그램 구조

2단계: 상수 복호화

모든 1095개 문자열 상수는 두 계층으로 암호화됩니다:

계층 1: 4008000571로 키잉된 정적 murmur XOR, 위치에 따라 다름.

계층 2: PRNG 스트림 XOR (glibc LCG 사용), 프로그램 시드와 각 상수 인덱스를 Knuth의 곱셈 해시로 결합하여 시딩.

복호화 전, 시드는 환경 핑거프린트 (_0xaf48)와 XOR됩니다. 이는 8비트 비트마스크로, 브라우저 API를 프로빙하여 계산됩니다:

Chrome의 경우: _0xaf48 = 0b01111111 = 127, 유효 시드 = 12755 ^ 127 = 12716.

즉, 같은 프로그램이 다른 환경에서 다른 복호화 결과를 생성합니다. Node.js, Chrome, Firefox에서 실행하면 다른 시드가 생성됩니다.

root@kitploit:~
node decrypt_constants.js
# -> program_decrypted.json, constants_table.txt

상수가 드러내는 정보

복호화된 문자열은 VM이 정확히 어떤 것을 핑거프린팅하는지 알려줍니다:

브라우저 핑거프린팅: screenWidth, screenHeight, innerWidth, innerHeight, devicePixelRatio, colorDepth, platform, userAgent, language, timezone, timezoneOffset, forcedColors, highContrast

성능 타이밍: navigationStart, domComplete, domLoading, fetchStart, requestStart, responseEnd, secureConnectionStart, serverTiming

RSA 암호화: BigInt, modPow, AQAB (base64로 65537), modulusLength, shiftLeft, shiftRight, getRandomValues

DOM/SVG 프로빙: http://www.w3.org/2000/svg, createElementNS, getBoundingClientRect, getTotalLength, getBBox

PX 필드 이름: mtr, tst, mst, enc, sbx, fstec, pdc, prb, wvi, wva, pti, dis, los, cv, sc, jd, , ,

엔드포인트 참조: https://fst-ec.perimeterx.net/?id=

안티 디버거: _CMP_RCX_07;_JNZ_0x0A_EB_CC, CC|CD-04|BREAKPOINT-005

3단계: Opcode 테이블

전체 JavaScript 언어를 포괄하는 107개의 기본 opcode와 동적으로 생성된 노이즈:

40개의 허니 opcode는 산술/비교 연산의 대체 구현으로, 수학적으로 동일하지만 구문적으로 다른 표현을 사용합니다. ADD는 (a^b) + 2*(a&b) 또는 -((-a)-b) 또는 a-(-b)로 나타날 수 있습니다. 각 기본 opcode는 최대 3개의 변형을 가질 수 있으며, 시드에서 결정론적으로 생성됩니다. 단순한 ADD 명령어가 같은 프로그램 내에서 4개의 다른 바이트코드 값으로 나타나 패턴 매칭 접근을 무력화합니다.

24개의 패딩 opcode는 순열에 할당되지만 핸들러가 없으며 절대 방출되지 않습니다. 이는 opcode 공간을 확장하고 셔플을 역추적하기 어렵게 만드는 역할을 합니다.

16개의 슈퍼명령어 그룹은 가장 중요한 안티 분석 기능입니다. 디스패치 루프가 opcode를 슈퍼명령어 리더로 해석하면, 핸들러는 바이트코드 스트림에서 추가 바이트 하나를 읽고 서브 핸들러로 디스패치합니다. 서브 핸들러는 완전히 다른 연산일 수 있습니다:

Opcode 테이블은 유효 시드로 시딩된 Fisher-Yates를 통해 셔플되므로 바이트코드 값이 빌드마다 다릅니다.

root@kitploit:~
node build_opcodes.js
# -> opcode_table.json, opcode_table.txt

4단계: CFG 빌더

이 툴킷의 핵심입니다. cfg.js는 PC=0부터 모든 실행 경로를 따라가며 각 명령어를 올바른 암호화 컨텍스트로 디코딩하여 제어 흐름 그래프를 구축합니다.

왜 선형 디스어셈블러가 아닌가

PX는 블록 경계에서 중첩 명령어를 사용합니다. 동일한 바이트가 한 실행 경로에서는 피연산자로 디코딩되고 다른 경로에서는 opcode로 디코딩되며, 이는 블록 암호화 컨텍스트에 따라 다릅니다. 선형 스캔은 각 바이트 위치를 한 번만 디코딩하여 대체 경로를 놓칩니다. CFG는 폴스루 및 점프 가장자리를 모두 따라 각 경로를 독립적으로 디코딩합니다.

5계층 바이트코드 암호화

  1. 계층 1 (_0x3ca8): 4008000571로 키잉된 원시 base64 바이트에 대한 정적 murmur XOR
  2. 계층 2 (_0xece1): 함수별 XOR, 두 서브 계층 포함: 위치 의존 정적 키 + 코드 무결성 해시 키
  3. 계층 3 (_0x427d): 블록별 롤링 XOR. 각 암호화 블록(fn.bl 경계로 정의)은 함수 키와 블록 인덱스에서 파생된 추가 XOR을 얻습니다. 블록 0은 첫 접근 시 암호화되지 않음; 블록 1+는 암호화됨. 이것이 첫 번째 블록에서는 선형 디스어셈블러가 작동하지만 이후 블록에서는 쓰레기를 생성하는 이유입니다.
  4. Opcode 순열: Fisher-Yates 셔플 + 위치 의존 오프셋 + 블록 의존 오프셋
  5. 명령어당 피연산자 XOR: 피연산자 바이트가 명령어 시작 위치에서 파생된 키와 XOR됨

CFG는 다섯 계층을 모두 비파괴적으로 적용하므로 (피연산자 XOR은 제자리가 아닌 즉시 계산), 중첩 명령어 영역이 서로 손상되지 않습니다.

슈퍼명령어 해석

각 슈퍼명령어 리더에 대해 CFG는 서브 바이트를 읽고 super_groups.json에서 실제 핸들러를 조회한 후 실제 opcode의 피연산자를 디코딩합니다. 융합된 점프 opcode(예: ASSIGN_OP_VAR처럼 보이지만 실제로는 JMP인 경우)의 점프 대상도 올바르게 추적됩니다.

root@kitploit:~
node cfg.js        # 모든 함수 -> cfg_output/
node cfg.js 79     # 단일 함수 -> stdout

브라우저 실행 추적으로 검증됨: 14개 함수에서 600개 명령어 추적, 스택 델타에서 0 불일치. 34개 고유 opcode 모두 검증됨. 초기화 중 실행된 모든 10개 리더 그룹에 대해 슈퍼명령어 디스패치가 올바르게 확인됨.

5단계: 클리너

CFG 출력을 가져와 스택 에뮬레이션을 실행하여 표현식 주석을 생성합니다. 원시 바이트코드를 읽기 쉬운 의사 코드로 변환합니다.

root@kitploit:~
node cleaner.js 79     # fn79 -> stdout

명령어를 순서대로 탐색하며 가상 스택을 추적합니다. 각 push/pop/call은 표현식 문자열을 구축합니다:

root@kitploit:~
  0018  GET_VAR                 ; 0.0001
  001e  GET_VAR                 ; or
  0024  PUSH_CONST              ; "_0x166"
  002b  CALL_METHOD_C           ; 0.0001._0x88(or, "_0x166")
  ...
  0114  PUSH_CONST              ; "fontSize"
  011b  PUSH_CONST              ; "pdc"
  0122  CALL_METHOD_C           ; _0x18c.getHours("fontSize", "pdc")

빈 스택에서 순수 산술/비교 노이즈만 억제됩니다. CFG가 이미 허니와 패딩을 필터링했으므로 나머지는 모두 유지됩니다.

112개 함수, 5435개 명령어 유지, 207개 노이즈 억제. fn79 (핑거프린트 수집기, 1109개 명령어)는 85% 표현식 주석 커버리지를 가짐.

VM 아키텍처

256슬롯 스택, 스코프 체인, try/catch 핸들러 체인, for-in 반복자 스택을 갖춘 스택 기반 VM. 디스패치 루프는 2바이트 LE opcode를 읽고, 순열 + 위치 XOR + 블록 오프셋을 통해 해석하고, 피연산자를 제자리에서 복호화하고, 핸들러를 실행한 후 피연산자를 다시 암호화하여 바이트코드가 메모리에 완전히 복호화된 상태로 남지 않도록 합니다.

주요 발견 사항

  • BigInt, modPow, 지수 65537을 사용한 핑거프린트 출력의 RSA 암호화
  • 구성된 경로에서 getTotalLength() 및 getBBox()를 통한 SVG 렌더링 핑거프린팅
  • 전체 performance.timing 워터폴 수집
  • 안티 디버거 탐지 마커 (CC|CD-04|BREAKPOINT-005)
  • 함수 79가 주요 핑거프린트 수집기 (8361 바이트, ~1200 명령어)

참고

AI가 코드 문서화, 도구 작성, 그리고 이 readme 초안 작성에 사용되었습니다.

면책 조항

순전히 교육적/보안 연구 목적입니다. 솔버나 우회 기법은 포함되어 있지 않으며, VM이 어떻게 작동하는지 문서화하는 것이 전부입니다. 이 저장소는 진정으로 흥미롭기 때문입니다.

PerimeterX/HUMAN Security 관계자 중 이 저장소에 대해 우려 사항이 있으면 언제든지 연락 주십시오: [email protected]

도구 다운로드
필드설명
s시드 (12755), 모든 암호화 연산을 구동
n논스 (1603730985), 프로그램별 무작위화
g생성기 플래그, 무결성 해시 복호화 계층 활성화
x암호화 플래그, 상수는 XOR 암호화됨
c상수 풀, 1230개 항목
f함수, 112개 항목 (암호화된 바이트코드 포함)
e진입점, 함수 인덱스 0
비트테스트Chrome
0typeof window.matchMedia === "function"1
1document.elementFromPoint 존재1
2typeof window.requestAnimationFrame === "function"1
3typeof window.getComputedStyle === "function"1
4CSS.supports 존재1
5navigator.sendBeacon 존재1
6document.execCommand 존재1
7process.versions.node 존재 (Node.js)0
ads
enve
init
리더가 해석하는 방식서브 바이트실제 실행
FOR_IN_NEXT74FOR_IN_NEXT
FOR_IN_NEXT100MAKE_CLOSURE
ASSIGN_OP_VAR165ASSIGN_OP_VAR
ASSIGN_OP_VAR37JMP
GET_VAR_PROP_C143SET_VAR_POP
GET_VAR_PROP_C23JMP_NULLISH