
스크립트 가능한 바이너리 에뮬레이션 프레임워크로, IDA Pro/Radare2와 Unicorn 엔진을 통합하여 x86, ARM, ARM64 아키텍처에서 자동화된 악성코드 분석, 문자열 복호화, 코드 경로 탐색을 수행합니다.
flare-emu는 IDA Pro 또는 Radare2와 같은 지원되는 바이너리 분석 프레임워크를 Unicorn의 에뮬레이션 프레임워크와 결합하여 사용자가 에뮬레이션 작업을 스크립팅할 수 있는 사용하기 쉽고 유연한 인터페이스를 제공합니다. 지원되는 아키텍처에 대해 유연하고 강력한 에뮬레이터를 설정하는 모든 관리 작업을 처리하도록 설계되어 코드 분석 문제 해결에 집중할 수 있습니다. 현재 flare-emu는 x86, x86_64, ARM 및 ARM64 아키텍처를 지원합니다.
현재 다수의 관련 헬퍼 및 유틸리티 함수와 함께 에뮬레이션 요구를 충족시키기 위한 다섯 가지 인터페이스를 제공합니다.
emulateRange – 이 API는 사용자가 지정한 컨텍스트 내에서 명령어 범위 또는 함수를 에뮬레이션하는 데 사용됩니다. 개별 명령어 및 "call" 명령어가 발견될 때 사용자 정의 훅을 위한 옵션을 제공합니다. 사용자는 에뮬레이터가 함수 호출을 건너뛸지 또는 호출할지를 결정할 수 있습니다. 이 인터페이스는 사용자가 특정 레지스터 및 스택 인수에 값을 쉽게 지정할 수 있는 방법을 제공합니다. 바이트 문자열이 지정되면 에뮬레이터의 메모리에 기록되고 포인터는 레지스터 또는 스택 변수에 기록됩니다. 에뮬레이션 후 사용자는 flare-emu의 유틸리티 함수를 사용하여 에뮬레이션된 메모리나 레지스터에서 데이터를 읽거나 반환된 Unicorn 에뮬레이션 객체를 직접 조사할 수 있습니다. emulateRange의 작은 래퍼 함수인 emulateSelection을 사용하여 IDA Pro에서 현재 강조 표시된 명령어 범위를 에뮬레이션할 수 있습니다.
iterate - 이 API는 주어진 대상에 도달하기 위해 함수 내 특정 분기를 따라 에뮬레이션을 강제하는 데 사용됩니다. 사용자는 대상 주소 목록 또는 함수의 주소를 지정할 수 있으며, 해당 함수에 대한 상호 참조 목록이 대상으로 사용됩니다. 대상에 도달하면 호출되는 콜백도 제공됩니다. 에뮬레이션 중 다른 분기를 유발할 수 있는 조건에 관계없이 대상에 도달합니다. emulateRange API와 마찬가지로 개별 명령어 및 "call" 명령어가 발견될 때 사용자 정의 훅을 위한 옵션이 제공됩니다. iterate API의 사용 예는 argtracker 도구가 수행하는 것과 유사한 작업을 달성하는 것입니다.
iterateAllPaths - 이 API는 iterate와 매우 유사하지만, 대상 주소를 제공하는 대신 모든 경로를 찾아 에뮬레이션할 대상 함수를 제공합니다. 이는 함수의 모든 기본 블록에 도달하려는 코드 분석을 수행할 때 유용합니다.
emulateBytes – 이 API는 간단히 외부 셸코드 블롭을 에뮬레이션하는 방법을 제공합니다. 제공된 바이트는 IDB에 추가되지 않고 그대로 에뮬레이션됩니다. 이는 에뮬레이션 환경을 준비하는 데 유용할 수 있습니다. 예를 들어, flare-emu 자체는 Unicorn에서 노출되지 않는 ARM64 CPU의 Model Specific Register (MSR)를 조작하여 Vector Floating Point (VFP) 명령어 및 레지스터 액세스를 활성화하기 위해 이 API를 사용합니다. Unicorn 에뮬레이션 객체가 반환되어 사용자가 추가로 조사할 수 있습니다.
emulateFrom - 이 API는 난독화된 바이너리나 셸코드에서 종종 발생하는 것처럼 함수 경계가 명확하게 정의되지 않은 경우에 유용합니다. 시작 주소를 제공하면 에뮬레이션할 것이 남지 않거나 훅 중 하나에서 에뮬레이션을 중지할 때까지 에뮬레이션합니다. IDA Pro를 사용하는 경우 strict 매개변수를 False로 설정하여 호출하면 동적 코드 발견이 활성화됩니다. flare-emu는 에뮬레이션 중에 명령어가 발견될 때마다 IDA Pro가 명령어를 생성하도록 합니다.
flare-emu를 IDA Pro에 설치하려면 flare_emu.py, flare_emu_ida.py, flare_emu_hooks.py 파일을 IDA Pro의 python 디렉토리에 넣고 IDAPython 스크립트에서 모듈로 임포트하기만 하면 됩니다.
flare-emu를 Rizin에 설치하려면 flare_emu.py, flare_emu_rizin.py, flare_emu_hooks.py 파일이 Python의 모듈 검색 경로에 있는지 확인하기만 하면 됩니다. flare-emu의 바이너리 분석 구성 요소로 Rizin을 사용할 때는 rzpipe가 필요합니다.
flare-emu를 Radare2에 설치하려면 flare_emu.py, flare_emu_radare.py, flare_emu_hooks.py 파일이 Python의 모듈 검색 경로에 있는지 확인하기만 하면 됩니다. flare-emu의 바이너리 분석 구성 요소로 Radare2를 사용할 때는 r2pipe가 필요합니다.
어쨌든 flare-emu는 Unicorn과 그 Python 바인딩에 의존합니다.
중요 참고 사항
flare-emu는 새로운 IDA Pro 7x API를 사용하여 작성되었으며, 이전 버전의 IDA Pro와 역호환되지 않습니다.
flare-emu는 다양한 코드 분석 문제를 해결하는 데 사용될 수 있지만, 가장 일반적인 용도 중 하나는 악성코드 바이너리에서 문자열을 복호화하는 데 도움을 주는 것입니다. FLOSS는 문자열 복호화 함수를 식별하고 각 상호 참조에서 전달된 문자열을 에뮬레이션을 통해 복호화하여 자동으로 이를 수행할 수 있는 훌륭한 도구입니다. 그러나 FLOSS가 항상 이러한 함수를 식별하고 일반적인 접근 방식으로 제대로 에뮬레이션할 수 있는 것은 아닙니다. 때로는 조금 더 작업을 해야 하며, 이때 flare-emu에 익숙해지면 많은 시간을 절약할 수 있습니다. 암호화된 문자열을 다룰 때 악성코드 분석가가 직면하는 일반적인 시나리오를 살펴보겠습니다.
x86_64 바이너리에서 모든 문자열을 복호화하는 함수를 식별했습니다. 이 함수는 곳곳에서 호출되며 다양한 문자열을 복호화합니다. IDA Pro에서 이 함수의 이름을 decryptString으로 지정했습니다. 다음은 모든 문자열을 복호화하고 각 함수 호출 지점에 복호화된 문자열을 주석으로 추가하고 각 복호화된 문자열과 복호화된 주소를 로깅하는 flare-emu 스크립트입니다.```
from future import print_function
import flare_emu
def decrypt(argv): myEH = flare_emu.EmuHelper() myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), registers = {"arg1":argv[0], "arg2":argv[1], "arg3":argv[2], "arg4":argv[3]}) return myEH.getEmuString(argv[0])
def iterateCallback(eh, address, argv, userData): s = decrypt(argv) print("%s: %s" % (eh.hexString(address), s)) eh.analysisHelper.setComment(address, s, False)
if name == 'main':
eh = flare_emu.EmuHelper()
eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)
`__main__`에서 `flare-emu`의 `EmuHelper` 클래스 인스턴스를 생성하는 것으로 시작합니다. 이 클래스를 사용하여 `flare-emu`로 모든 작업을 수행합니다. 그런 다음 `iterate` API를 사용하여 `decryptString` 함수의 주소와 `EmuHelper`가 에뮬레이션된 각 교차 참조에 대해 호출할 콜백 함수의 이름을 전달합니다.
`iterateCallback` 함수는 `eh`라는 이름의 EmuHelper 인스턴스, 교차 참조의 주소, 이 특정 호출에 전달된 인수, 그리고 여기서 `userData`라는 특수 사전을 받습니다. `userData`는 이 간단한 예제에서는 사용되지 않지만, 에뮬레이터에 대한 지속적인 컨텍스트로 생각하여 사용자 정의 데이터를 저장할 수 있습니다. 하지만 주의하세요. `flare-emu` 자체도 이 사전을 사용하여 작업 수행에 필요한 중요한 정보를 저장합니다. 그러한 데이터 중 하나는 "EmuHelper" 키에 저장된 `EmuHelper` 인스턴스 자체입니다. 관심이 있다면 소스 코드를 검색하여 이 사전에 대해 더 알아보세요. 이 콜백 함수는 단순히 `decrypt` 함수를 호출하고, 복호화된 문자열을 출력하며, `decryptString` 호출 주소에 주석을 추가합니다.
`decrypt`는 두 번째 `EmuHelper` 인스턴스를 생성하여 `decryptString` 함수 자체를 에뮬레이션하며, 이를 통해 문자열을 복호화합니다. 이 `decryptString` 함수의 프로토타입은 다음과 같습니다: `char * decryptString(char *text, int textLength, char *key, int keyLength)`. 이 함수는 문자열을 제자리에서 복호화합니다. 우리의 `decrypt` 함수는 `iterateCallback` 함수에서 받은 인수를 `EmuHelper`의 `emulateRange` API 호출에 전달합니다. 이 바이너리는 `x86_64`이므로 호출 규약이 스택이 아닌 레지스터를 사용하여 인수를 전달합니다. `flare-emu`는 IDA Pro가 결정한 바이너리의 아키텍처와 파일 형식을 기반으로 어떤 레지스터가 어떤 인수를 나타내는지 자동으로 결정하므로, 어느 정도 아키텍처에 독립적인 코드를 작성할 수 있습니다. 만약 32비트 `x86`이라면 `stack` 인수를 사용하여 인수를 전달해야 합니다. 예를 들어: `myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), stack = [0, argv[0], argv[1], argv[2], argv[3]])`. 첫 번째 스택 값은 `x86`에서 반환 주소이므로 여기서는 자리 표시자로 `0`을 사용합니다. 에뮬레이션이 완료되면 `getEmuString` API를 호출하여 함수에 전달된 첫 번째 인수가 가리키는 메모리 위치에 저장된 null 종료 문자열을 검색합니다.
### flare-emu and idalib
* IDA Pro 설치
* Hex-Rays 사용자 가이드에 따라 idalib 설치
* (가상 환경 활성화)
* pip install /path/to/IDA/installation/idalib/python
* python /path/to/IDA/installation/idalib/python/py-activate-idalib.py [-d /path/to/active/IDA/installation]
* idapro를 임포트하고 스크립트 작성
* 예제는 tests/test_flare_emu_idalib.py 참조
### Rizin을 사용한 쉬운 문자열 복호화 시나리오
위의 동일한 예제를 사용할 때, IDA Pro 대신 Rizin으로 작업해도 크게 달라지지 않습니다. 한 가지 차이점은 `flare-emu`가 현재 Rizin으로 작업할 때 명령줄 스크립트 또는 Python 셸 내에서 실행되도록 설계되었다는 것입니다. Python 셸은 임시 문제 해결에 적합하며, 명령줄 스크립트는 일괄 처리에 적합합니다. 위 스크립트의 Rizin 버전은 다음과 같습니다 (rizin 내에서 실행하려면 샘플 경로를 생략할 수 있습니다):```
from __future__ import print_function
import sys
import flare_emu
def decrypt(argv, eh):
myEH = flare_emu.EmuHelper(samplePath=sys.argv[1], emuHelper=eh, isRizin=True)
myEH.emulateRange(
myEH.analysisHelper.getNameAddr("decryptString"),
registers={
"arg1": argv[0],
"arg2": argv[1],
"arg3": argv[2],
"arg4": argv[3],
},
)
return myEH.getEmuString(argv[0])
def iterateCallback(eh, address, argv, userData):
s = decrypt(argv, eh)
print("%s: %s" % (eh.hexString(address), s))
eh.analysisHelper.setComment(address, s, False)
if __name__ == "__main__":
eh = flare_emu.EmuHelper(samplePath=sys.argv[1], isRizin=True)
rz = eh.analysisHelper.r
eh.analysisHelper.setName(0x100000D60, "decryptString")
eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)
위와 동일한 예제를 사용할 때, IDA Pro 대신 Radare2로 작업해도 큰 변화는 없습니다. 한 가지 차이점은 flare-emu가 현재 Radare2와 함께 사용될 때 명령줄 스크립트 또는 Python 셸 내에서 실행되도록 설계되었다는 것입니다. Python 셸은 임시 문제 해결에 적합한 반면, 명령줄 스크립트는 배치 처리에 적합합니다. 위 스크립트의 Radare2 버전은 다음과 같습니다:```
from future import print_function
import flare_emu
def decrypt(argv, eh): myEH = flare_emu.EmuHelper(samplePath=sys.argv[1], emuHelper=eh) myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), registers = {"arg1":argv[0], "arg2":argv[1], "arg3":argv[2], "arg4":argv[3]}) return myEH.getEmuString(argv[0])
def iterateCallback(eh, address, argv, userData): s = decrypt(argv, eh) print("%s: %s" % (eh.hexString(address), s)) eh.analysisHelper.setComment(address, s, False)
if name == 'main':
eh = flare_emu.EmuHelper(samplePath=sys.argv[1])
eh.analysisHelper.setName(, "decryptString")
eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)
이 스크립트에는 두 가지 차이점이 있습니다. 첫째, `EmuHelper` 생성자는 여기서 `samplePath=sys.argv[1]` 매개변수를 받습니다. `samplePath` 매개변수가 제공되면 `flare-emu`는 바이너리 분석 엔진으로 `r2pipe`와 함께 Radare2를 사용합니다. 또한 `decrypt` 함수에서 생성된 두 번째 `EmuHelper` 인스턴스에 두 번째 매개변수가 전달되는 것을 볼 수 있습니다. `emuHelper` 매개변수는 기존 `EmuHelper` 객체를 받아 새 객체를 생성할 때 해당 메모리를 복제합니다. 또한 Radare2를 사용하는 경우 새 인스턴스는 새 세션을 생성하는 대신 기존 Radare2 세션을 재사용하므로 오버헤드가 줄어듭니다. 둘째, `flare-emu`는 `r2pipe.open`을 사용하여 새 Radare2 인스턴스를 생성하므로, 우리가 관심 있는 함수의 이름이 `decryptString`이 아닐 가능성이 높습니다. `EmuHelper`의 `analysisHelper` 객체를 사용하여 직접 이름을 설정하거나(`eh.analysisHelper.setName(<some address>, "decryptString")`), `iterate` 및 `emulateRange` 호출에 주소를 직접 입력할 수 있습니다.
## [Emulation Functions](#emulationfuncs)
`emulateRange(startAddr, endAddr=None, registers=None, stack=None, instructionHook=None, callHook=None, memAccessHook=None, hookData=None, skipCalls=True, hookApis=True, strict=True, count=0)` - `startAddress`에서 시작하여 `endAddress`(포함하지 않음)까지 명령어 범위를 에뮬레이트합니다. `endAddress`가 `None`이면 에뮬레이션이 시작된 함수 내에서 "return" 유형 명령어를 만나면 에뮬레이션이 중지됩니다.
* `registers` – 키가 레지스터 이름이고 값이 레지스터 값인 딕셔너리입니다. `flare-emu`에서 생성된 특수 레지스터 이름(`arg1`, `arg2` 등, `ret`, `pc`)이 사용될 수 있습니다.
* `stack` – 값을 역순으로 스택에 푸시할 배열로, `x86`에서 함수 인수와 유사합니다. `x86`에서는 이 배열의 첫 번째 값이 함수 호출의 리턴 주소로 사용되고 함수의 첫 번째 인수가 아니라는 점을 기억하세요. `flare-emu`는 `registers` 및 `stack` 인수에 지정된 값에 따라 에뮬레이트된 스레드의 컨텍스트와 메모리를 초기화합니다. 이러한 값 중 하나라도 문자열로 지정되면 메모리 위치에 기록되고 해당 메모리에 대한 포인터가 지정된 레지스터 또는 스택 위치에 대신 기록됩니다.
* `instructionHook` – 각 명령어가 에뮬레이트되기 전에 호출되도록 정의할 수 있는 함수입니다. 프로토타입: `instructionHook(unicornObject, address, instructionSize, userData)`.
* `callHook` – 에뮬레이션 중에 "call" 유형 명령어가 발견될 때마다 호출되도록 정의할 수 있는 함수입니다. 프로토타입: `callHook(address, arguments, functionName, userData)`.
* `hookData` – 사용자 정의 데이터를 포함하는 딕셔너리로, 후크 함수에서 사용할 수 있습니다. 에뮬레이션 전반에 걸쳐 데이터를 지속하는 수단입니다. `flare-emu`도 이 딕셔너리를 자체 목적으로 사용하므로, 이미 정의된 키를 정의하지 않도록 주의해야 합니다. 이 변수는 Unicorn에서의 명명 때문에 사용자 정의 후크 함수에서 종종 `userData`라고 불립니다.
* `skipCalls` – 에뮬레이터가 "call" 유형 명령어를 건너뛰고 스택을 적절히 조정하도록 하며, 기본값은 `True`입니다.
* `hookApis` – `flare-emu`가 에뮬레이션 중에 만나는 일반적인 런타임 및 OS 라이브러리 함수 중 일부에 대해 기본적인 구현을 수행하도록 합니다. 이렇게 하면 `memcpy`, `strcat`, `malloc` 등과 같은 함수 호출에 신경 쓸 필요가 없으며, 기본값은 `True`입니다.
* `memAccessHook` – 메모리 읽기 또는 쓰기 시 호출되도록 정의할 수 있는 함수입니다. 프로토타입: `memAccessHook(unicornObject, accessType, memAccessAddress, memAccessSize, memValue, userData)`.
* `strict` – `True`(기본값)로 설정하면 분기 대상을 확인하여 디스어셈블러가 명령어를 예상하는지 확인합니다. 그렇지 않으면 분기 명령어를 건너뜁니다. IDA Pro를 사용할 때 `False`로 설정하면 `flare-emu`가 에뮬레이트할 때 IDA Pro에서 명령어를 생성합니다 **(주의해서 비활성화)**.
* `count` – 에뮬레이트할 최대 명령어 수이며, 기본값은 `0`(제한 없음)입니다.
`iterate(target, targetCallback, preEmuCallback=None, callHook=None, instructionHook=None, hookData=None, resetEmuMem=False, hookApis=True, memAccessHook=None)` - `target`에 지정된 각 대상에 대해 포함 함수의 시작부터 대상 주소까지 별도의 에뮬레이션이 수행됩니다. 각 대상에 도달하는 데 필요한 분기를 따라 에뮬레이션이 강제됩니다. `target`은 함수의 주소일 수 있으며, 이 경우 대상 목록은 지정된 함수에 대한 모든 교차 참조로 채워집니다. 또는 `target`은 명시적 대상 목록일 수 있습니다.
* `targetCallback` – 에뮬레이션 중에 각 대상에 도달할 때 `flare-emu`가 호출하는 사용자 정의 함수입니다. 프로토타입: `targetHook(emuHelper, address, arguments, userData)`.
* `preEmuCallback` – 각 대상에 대한 에뮬레이션이 시작되기 전에 호출되는 사용자 정의 함수입니다. 필요한 경우 일부 설정 코드를 구현할 수 있습니다.
* `resetEmuMem` – 각 대상의 에뮬레이션이 시작되기 전에 `flare-emu`가 에뮬레이션 메모리를 재설정하도록 하며, 기본값은 `False`입니다.
`iterateAllPaths(target, targetCallback, preEmuCallback=None, callHook=None, instructionHook=None, hookData=None, resetEmuMem=False, hookApis=True, memAccessHook=None, maxPaths=MAXCODEPATHS, maxNodes=MAXNODESEARCH)` - `target` 주소를 포함하는 함수에 대해, 발견된 각 경로마다 별도의 에뮬레이션이 `maxPaths`까지 수행됩니다.
* `maxPaths` – 함수를 통해 검색하고 에뮬레이트할 최대 경로 수입니다. 일부 복잡한 함수는 그래프 검색 함수가 매우 오래 걸리거나 완료되지 않을 수 있습니다. 합리적인 시간 내에 필요에 맞게 이 매개변수를 조정하십시오.
* `maxNodes` – 대상 함수를 통해 경로를 찾을 때 검색할 최대 기본 블록 수입니다. 이는 비합리적인 검색 시간과 중단을 방지하기 위한 안전 조치이며 변경할 필요가 없습니다.
`emulateBytes(bytes, registers=None, stack=None, baseAddress=0x400000, instructionHook=None, hookData=None)` - `bytes`에 포함된 코드를 가능하면 `baseAddress`의 에뮬레이션 메모리에 쓰고 `bytes`의 시작부터 끝까지 명령어를 에뮬레이트합니다.
`emulateFrom(startAddr, registers=None, stack=None, instructionHook=None, callHook=None, memAccessHook=None, hookData=None, skipCalls=True, hookApis=True, strict=True, count=0)` - 이 API는 난독화된 바이너리나 쉘코드에서 종종 발생하는 함수 경계가 명확하지 않은 경우에 유용합니다. `startAddr`로 시작 주소를 제공하면 에뮬레이트할 것이 없거나 후크 중 하나에서 에뮬레이션을 중지할 때까지 에뮬레이션합니다. `strict` 매개변수를 `False`로 설정하여 동적 코드 검색을 활성화할 수 있습니다. `flare-emu`는 에뮬레이션 중에 명령어가 발견되면 IDA Pro에서 명령어를 생성하게 합니다.
## [Utility Functions](#utility)
다음은 `EmuHelper` 클래스에서 제공하는 유용한 유틸리티 함수 중 일부의 불완전한 목록입니다.
* `hexString(value)` – 값에 대한 16진수 형식 문자열을 반환합니다. 로깅 및 print 문에 유용합니다.
* `skipInstruction(userData, useAnalysisHelper=False)` – 에뮬레이션 후크에서 이 함수를 호출하여 현재 명령어를 건너뛰고 프로그램 카운터를 다음 명령어로 이동합니다. `useAnalysisHelper` 옵션은 바이너리 분석 프레임워크가 여러 명령어를 하나의 의사 명령어로 접을 때 이 모든 명령어를 건너뛰고자 하는 경우를 처리하기 위해 추가되었습니다. 이 함수는 단일 명령어 후크에서 여러 번 호출하여 여러 명령어를 건너뛸 수 없습니다. 여러 명령어를 건너뛰려면 ARM 코드를 에뮬레이트하는 경우 프로그램 카운터에 직접 쓰지 않는 것이 좋습니다. 대신 `EmuHelper`의 `changeProgramCounter` API를 사용하십시오.
* `changeProgramCounter(userData, newAddress)` – 에뮬레이션 후크에서 이 함수를 호출하여 프로그램 카운터 레지스터 값을 변경합니다. 이 API는 ARM 아키텍처의 썸 모드 추적을 처리합니다.
* `getRegVal(registerName)` – 지정된 레지스터의 값을 검색하며, 서브 레지스터 주소 지정에 민감합니다. 예를 들어, "ax"는 `x86`에서 EAX/RAX 레지스터의 하위 16비트를 반환합니다.
* `stopEmulation(userData)` – 에뮬레이션 후크에서 이 함수를 호출하여 에뮬레이션을 중지합니다. Unicorn API의 `emu_stop`을 직접 호출하는 대신 이 함수를 사용하여 `EmuHelper` 객체가 `iterate` 기능과 관련된 부기를 처리할 수 있도록 합니다.
* `getEmuString(address)` – 에뮬레이션된 메모리의 주소에 위치한 문자열을 null 종결자까지 반환합니다. 문자가 반드시 인쇄 가능한 것은 아닙니다.
* `getEmuWideString(address)` – 에뮬레이션된 메모리의 주소에 위치한 "와이드 문자" 문자열을 null 종결자까지 반환합니다. "와이드 문자"는 여기서 UTF-16 LE로 인코딩된 ASCII 문자열의 경우처럼 매 다른 바이트마다 null 바이트를 포함하는 모든 바이트 시리즈를 느슨하게 의미합니다. 문자가 반드시 인쇄 가능한 것은 아닙니다.
* `getEmuBytes(address, length)` – 에뮬레이션된 메모리의 주소에 위치한 바이트 문자열을 반환합니다.
* `getEmuPtr(address)` – 주어진 주소에 위치한 포인터 값을 반환합니다.
* `writeEmuPtr(address, value)` – 에뮬레이션된 메모리의 주어진 주소에 포인터 값을 씁니다.
* `loadBytes(bytes, address=None)` – 에뮬레이터에 메모리를 할당하고 바이트를 씁니다.
* `isValidEmuPtr(address)` – 제공된 주소가 유효한 에뮬레이션된 메모리를 가리키면 `True`를 반환합니다.
* `getEmuMemRegion(address)` – 제공된 주소를 포함하는 메모리 영역의 시작 및 끝 주소를 포함하는 튜플을 반환하거나, 주소가 유효하지 않으면 `None`을 반환합니다.
* `getArgv()` – "call" 유형 명령어의 에뮬레이션 후크에서 이 함수를 호출하여 함수 인수 배열을 받습니다.
* `addApiHook(apiName, hook)` – 이 `EmuHelper` 인스턴스에 새 API 후크를 추가합니다. 에뮬레이션 중 `apiName`에 대한 호출 명령어가 발견되면 `EmuHelper`는 `hook`에 지정된 함수를 호출합니다. `hook`이 문자열인 경우, `EmuHelper`에 이미 후크된 API의 이름일 것으로 예상되며, 이 경우 기존 후크 함수를 호출합니다. `hook`이 함수인 경우 해당 함수를 호출합니다.
* `allocEmuMem(size, addr=None)` – `size` 바이트를 포함할 수 있는 충분한 에뮬레이터 메모리를 할당합니다. 요청된 `address`를 존중하려고 하지만, 기존 메모리 영역과 겹치면 사용되지 않은 메모리 영역에 할당하고 새 주소를 반환합니다. `address`가 페이지 정렬되지 않은 경우, 새 영역 내에서 동일한 페이지 정렬 오프셋을 유지하는 주소를 반환합니다. 예를 들어, `0x1000`이 이미 할당된 상태에서 주소 `0x1234`를 요청하면 `0x2000`에 할당되고 대신 `0x2234`를 반환할 수 있습니다.
# [Learn More](#learn)
**flare-emu**에 대해 더 알아보려면 https://www.fireeye.com/blog/threat-research/2018/12/automating-objective-c-code-analysis-with-emulation.html에서 소개 블로그를 읽어보세요.