
Valgrind의 VEX IR을 위한 Python 바인딩.
PyVEX는 VEX IR을 위한 Python 바인딩입니다.
프로젝트 저장소: https://github.com/angr/pyvex
문서: https://api.angr.io/projects/pyvex/en/latest/
PyVEX는 pip으로 설치할 수 있습니다:
pip install pyvex
import pyvex
import archinfo
# translate an AMD64 basic block (of nops) at 0x400400 into VEX
irsb = pyvex.lift(b"\x90\x90\x90\x90\x90", 0x400400, archinfo.ArchAMD64())
# pretty-print the basic block
irsb.pp()
# this is the IR Expression of the jump target of the unconditional exit at the end of the basic block
print(irsb.next)
# this is the type of the unconditional exit (i.e., a call, ret, syscall, etc)
print(irsb.jumpkind)
# you can also pretty-print it
irsb.next.pp()
# iterate through each statement and print all the statements
for stmt in irsb.statements:
stmt.pp()
# pretty-print the IR expression representing the data, and the *type* of that IR expression written by every store statement
import pyvex
for stmt in irsb.statements:
if isinstance(stmt, pyvex.IRStmt.Store):
print("Data:", end="")
stmt.data.pp()
print("")
print("Type:", end="")
print(stmt.data.result_type)
print("")
# pretty-print the condition and jump target of every conditional exit from the basic block
for stmt in irsb.statements:
if isinstance(stmt, pyvex.IRStmt.Exit):
print("Condition:", end="")
stmt.guard.pp()
print("")
print("Target:", end="")
stmt.dst.pp()
print("")
# these are the types of every temp in the IRSB
print(irsb.tyenv.types)
# here is one way to get the type of temp 0
print(irsb.tyenv.types[0])
이것은 기본 블록의 구문적 표현이라는 점을 명심하세요. 즉, 블록이 무엇을 의미하는지는 알려주지만, 예를 들어 저장 명령어에 의해 실제로 어떤 데이터가 기록되는지 말할 수 있는 컨텍스트는 없습니다.
매우 다양한 아키텍처를 다루기 위해서는 중간 표현에 대한 분석을 수행하는 것이 유용합니다. IR은 서로 다른 아키텍처를 다룰 때 여러 아키텍처 차이를 추상화하여, 단일 분석을 모든 아키텍처에서 실행할 수 있게 해줍니다:
rax는 이 메모리 공간의 주소 16부터 저장됩니다).IR에는 많은 선택지가 있습니다. 우리는 VEX를 사용하는데, 바이너리 코드를 VEX로 리프팅하는 것이 상당히 잘 지원되기 때문입니다. VEX는 여러 대상 기계 언어에 대한 아키텍처 독립적이고 부작용이 없는 표현입니다. 이는 기계 코드를 프로그램 분석을 더 쉽게 만들기 위해 설계된 표현으로 추상화합니다. 이 표현은 다섯 가지 주요 객체 클래스를 가집니다:
t0부터 시작하여 번호가 매겨집니다. 이러한 임시 변수는 강한 타입을 가집니다 (즉, "64비트 정수" 또는 "32비트 부동 소수점").VEX IR은 실제로 VEX 저장소의 libvex_ir.h 파일 (https://github.com/angr/vex/blob/dev/pub/libvex_ir.h)에 상당히 잘 문서화되어 있습니다. 게으른 분들을 위해, 자주 상호작용할 가능성이 높은 VEX의 일부 부분을 자세히 설명하겠습니다. 먼저, 몇 가지 IR 표현식이 있습니다:
| IR 표현식 | 평가된 값 | VEX 출력 예시 |
|---|---|---|
| Constant | 상수 값. | 0x4:I32 |
| Read Temp | VEX 임시 변수에 저장된 값. | RdTmp(t10) |
| Get Register | 레지스터에 저장된 값. | GET:I32(16) |
| Load Memory | 메모리 주소에 저장된 값으로, 주소는 다른 IR 표현식에 의해 지정됨. | LDle:I32 / LDbe:I64 |
| Operation | 지정된 IR 표현식 인수에 적용된 지정된 IR 연산의 결과. | Add32 |
| If-Then-Else | 주어진 IR 표현식이 0으로 평가되면 하나의 IR 표현식을 반환합니다. 그렇지 않으면 다른 것을 반환합니다. | ITE |
| Helper Function | VEX는 특정 아키텍처의 조건 플래그 레지스터를 계산하는 것과 같은 특정 연산에 C 헬퍼 함수를 사용합니다. 이러한 함수는 IR 표현식을 반환합니다. | function_name() |
이러한 표현식은 다음으로 IR 명령문에서 사용됩니다. 다음은 몇 가지 일반적인 것입니다:
| IR 명령문 | 의미 | VEX 출력 예시 |
|---|---|---|
| Write Temp | 주어진 IR 표현식의 값으로 VEX 임시 변수를 설정합니다. | WrTmp(t1) = (IR Expression) |
| Put Register | 주어진 IR 표현식의 값으로 레지스터를 갱신합니다. | PUT(16) = (IR Expression) |
| Store Memory | IR 표현식으로 주어진 메모리 위치를, 역시 IR 표현식으로 주어진 값으로 갱신합니다. | STle(0x1000) = (IR Expression) |
| Exit | 기본 블록에서의 조건부 출구로, 점프 대상은 IR 표현식에 의해 지정됩니다. 조건은 IR 표현식에 의해 지정됩니다. | if (condition) goto (Boring) 0x4000A00:I32 |
ARM에서의 IR 변환 예시가 아래에 나와 있습니다. 예시에서 뺄셈 연산은 5개의 IR 명령문으로 구성된 단일 IR 블록으로 변환되며, 각 명령문은 적어도 하나의 IR 표현식을 포함합니다 (실제로는 IR 블록이 일반적으로 하나 이상의 명령어로 구성됩니다). 레지스터 이름은 GET 표현식과 PUT 명령문에 주어진 숫자 인덱스로 변환됩니다.
예리한 독자는 실제 뺄셈이 블록의 처음 4개 IR 명령문에 의해 모델링되고, 다음 명령어 (이 경우 0x59FC8에 위치)를 가리키도록 프로그램 카운터를 증가시키는 것이 마지막 명령문에 의해 모델링된다는 것을 관찰할 것입니다.
다음 ARM 명령어:
subs R2, R2, #8
는 다음과 같은 VEX IR이 됩니다:
t0 = GET:I32(16)
t1 = 0x8:I32
t3 = Sub32(t0,t1)
PUT(16) = t3
PUT(68) = 0x59FC8:I32
멋진 것들입니다!
학술 작업에서 PyVEX를 사용하는 경우, 이것이 개발된 논문을 인용해 주세요:
@article{shoshitaishvili2015firmalice,
title={Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware},
author={Shoshitaishvili, Yan and Wang, Ruoyu and Hauser, Christophe and Kruegel, Christopher and Vigna, Giovanni},
booktitle={NDSS},
year={2015}
}