
Windows, macOS, Linux 및 Android용 바이너리 커버리지 기반 퍼저
Copyright 2020 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Jackalope는 블랙박스 바이너리와 함께 작동할 수 있는 사용자 정의 가능한 분산형 커버리지 기반 퍼저입니다.
소스 코드가 제공되는 대상에서 작동하는 좋은 커버리지 기반 퍼저는 많지만, 특히 Windows 및 macOS 운영 체제에서 블랙박스 바이너리에서 작동하는 퍼저는 상대적으로 적으며, 존재하는 것들도 주로 사용자 정의가 쉽지 않은 코드베이스를 기반으로 합니다. Jackalope의 초기 목표는 다음과 같습니다:
Jackalope는 독립 실행형으로 사용할 수 있지만, 기본 동작을 대체할 사용자 정의 컴포넌트를 플러그인할 수 있는 라이브러리로 사용할 때 더 강력합니다. 기본적으로 Jackalope는 다음을 제공합니다:
Jackalope는 병렬로 실행할 수 있습니다
-nthreads 명령줄 매개변수를 통해 퍼징 스레드 수를 전달하여-start_server 명령줄 플래그) 워커 머신의 퍼저가 이 서버에 연결하도록 함(-server 명령줄 플래그). 그런 다음 서버는 워커 간에 샘플, 크래시 및 커버리지를 수집하고 배포합니다.Jackalope는 현재 고급 변이 전략을 포함하지 않습니다. 대신 많은 대상에서 작동할 범용 뮤테이터 세트를 제공하지만, 사용자는 퍼징하려는 대상에 따라 사용자 정의 뮤테이터와 변이 전략을 작성하는 것이 권장됩니다.
현재 블랙박스 바이너리 퍼징은 Windows, macOS, Linux 및 Android 운영 체제에서 지원됩니다.
Linux에서는 Jackalope가 Sanitizer Coverage(대상의 소스 코드 필요)와 함께 실행될 수도 있습니다. 이 모드는 별도 문서에 설명되어 있습니다.
전제 조건: Python 3
터미널을 열고 필요한 경우 빌드 환경을 설정합니다. Windows에서는 일반 명령 프롬프트를 여는 대신 빌드하려는 플랫폼에 맞는 Visual Studio 명령 프롬프트를 열거나 vcvars64.bat / vcvars32.bat을 실행해야 합니다.
소스가 포함된 디렉터리로 이동합니다.
다음 명령을 실행합니다:
cd Jackalope
git clone --recurse-submodules [email protected]:googleprojectzero/TinyInst.git
(alternately: git clone --recurse-submodules https://github.com/googleprojectzero/TinyInst.git)
mkdir build
cd build
cmake <generator arguments> ..
cmake --build . --config Release
생성기 인수는 환경에 따라 다릅니다. macOS에서는 -G Xcode를 사용하고, 예를 들어 Visual Studio 2019가 있는 Windows에서 64비트 빌드의 경우 -G "Visual Studio 16 2019" -A x64를 사용합니다. Linux에서는 생성기 인수가 필요하지 않습니다. Android용으로 크로스 컴파일할 때는 추가 cmake 인수가 필요하며, 이는 Android용 TinyInst 빌드 지침에 설명된 것과 동일합니다. /dev/shm을 통한 공유 메모리를 지원하는 VM에서 Android를 퍼징하는 경우 -DANDROID_TARGET=VM이 공유 메모리 샘플 전달을 활성화합니다.
macOS에서 arm64e 바이너리에 대해 실행할 빌드에 대해서는 여기를 참조하세요.
macOS에서 No CMAKE_C_COMPILER could be found 오류가 발생하나요? cmake를 업데이트해 보세요. 또한 Xcode가 설치되어 있고 최소 한 번 실행했는지 확인하세요(첫 실행 시 일부 컴포넌트를 설치합니다).
사용법:
./fuzzer <fuzzer arguments> <instrumentation and other components arguments> -- <target command line>
다음 명령줄 인수가 지원됩니다:
-in - 입력 디렉터리(초기 샘플 세트가 포함된 디렉터리). 입력 디렉터리가 "-"인 경우 퍼저는 이전 세션을 복원하려고 시도합니다(-restore 플래그를 사용하는 것과 동일).
-out - 출력 디렉터리
-t - 샘플 타임아웃(ms)
-t1 - 대상 초기화를 위한 타임아웃(예: 정의된 경우 대상 메서드에 도달하기 전). 기본값은 샘플 타임아웃입니다.
-nthreads - 퍼저 스레드 수. 기본값은 1입니다.
-delivery <file|shmem> - 사용할 샘플 전달 메커니즘. file인 경우 각 샘플이 파일로 출력되고 대상 인수의 "@@"가 파일 경로로 대체됩니다. shmem인 경우 퍼저가 대신 공유 메모리를 생성하고 대상 인수의 "@@"를 공유 메모리 이름으로 대체합니다. 이 경우 대상이 공유 메모리를 열고 샘플을 추출할 책임이 있습니다. 기본값은 file입니다.
-file_extension - file 샘플 전달을 사용할 때 파일 이름에 지정된 확장자를 추가합니다. 대상이 입력 파일에 특정 확장자를 기대하는 경우 유용합니다.
-restore 또는 -resume - 이전 퍼징 세션을 복원하고 재개합니다. 퍼저와 서버 프로세스 모두 복원을 지원합니다.
-server - 사용할 커버리지 서버를 지정합니다.
-start_server - 퍼징 프로세스 대신 서버 프로세스를 실행합니다.
-crash_retry - 크래시를 재현하려고 시도하는 횟수. 기본값은 10입니다. 이 재시도 횟수 내에 재현되지 않거나 계측 없이 실행할 때 재현되지 않는 크래시는 불안정한 것으로 표시됩니다.
-coverage_retry - 새로운 커버리지를 재현하려고 재시도하는 횟수. 이 재시도 횟수 내에 안정적으로 재현할 수 없는 커버리지는 불안정한 것으로 간주됩니다. 불안정한 커버리지만 포함하는 샘플은 저장되지 않습니다.
-clean_target_on_coverage - 커버리지를 재현할 때 대상을 재시작합니다. 기본값은 true입니다.
-minimize_samples - 새로운 샘플을 코퍼스에 저장하기 전에 최소화하려고 시도합니다. 기본값은 true입니다.
-iterations_per_round - 다음 샘플로 넘어가기 전에 코퍼스의 샘플을 변이하고 실행하는 횟수. 기본값은 1000입니다. 매우 느린 대상의 경우 이 값을 줄이는 것을 고려할 수 있습니다.
-deterministic_mutations - 비결정적 변이에 추가로 결정적 변이를 사용합니다. -server 플래그를 사용하지 않는 한 기본값은 true입니다.
-deterministic_only - 결정적 변이를 우선시합니다. 참고: 이 플래그를 사용하더라도 퍼저는 여전히 비결정적 변이를 사용하지만, 모든 결정적 변이가 소진된 후에만 사용합니다. -server와 함께 실행할 때 단일 클라이언트 인스턴스가 결정적 변이를 수행하도록 하는 것이 유용할 수 있습니다.
-max_sample_size - 사용할 최대 샘플 크기. max_sample_size보다 큰 모든 입력 샘플은 잘리고 뮤테이터는 이 크기를 초과하는 새 샘플을 생성할 수 없습니다. 기본값은 1000000입니다. 경고: 공유 메모리 샘플 전달을 사용할 때 max_sample_size는 대상이 기대하는 최대 샘플 크기와 일치해야 합니다. 예를 들어 여기의 테스트 대상과 같습니다.
-keep_samples_in_memory - 모든 샘플을 항상 메모리에 유지할지 여부. 기본값은 true입니다. 코퍼스가 메모리에 맞지 않을 정도로 크지 않은 한 권장됩니다.
-track_ranges - 읽기 범위 추적 기능을 활성화합니다. 자세한 내용은 여기를 참조하세요.
-dry_run - 모든 입력 샘플이 처리된 후 실제 퍼징을 시작하기 전에 Jackalope를 종료시킵니다. 코퍼스 최소화(참고: Jackalope는 이전에 보지 못한 커버리지를 포함하는 샘플만 출력 코퍼스에 추가합니다) 또는 많은 수의 크래시를 재현하는 데 유용합니다.
-add_all_inputs - 새로운 커버리지를 트리거하지 않는 샘플을 포함하여 입력 디렉터리의 모든 샘플을 퍼징 코퍼스에 추가합니다. 기본값은 꺼짐입니다.
-dict <path> - 변이 중에 사용할 사전을 제공합니다. 사전은 각 항목이 별도의 줄에 있는 텍스트 파일이어야 합니다. \xXX 이스케이프 시퀀스를 사용할 수 있습니다.
-dump_coverage - Lighthouse로 가져오기에 적합한 형식으로 커버리지를 주기적으로 내보냅니다(출력 디렉터리의 coverage.txt로).
TinyInst 계측 명령줄 인수는 TinyInst readme를 참조하세요.
예제(macOS):
./fuzzer -in in -out out -t 1000 -delivery shmem -instrument_module test -target_module test -target_method __Z4fuzzPc -nargs 1 -iterations 10000 -persist -loop -cmp_coverage -- ./test -m @@
예제(Windows):
fuzzer.exe -in in -out out -t 1000 -delivery shmem -instrument_module test.exe -target_module test.exe -target_method fuzz -nargs 1 -iterations 10000 -persist -loop -cmp_coverage -- test.exe -m @@
설명: 이 명령은 "in"을 입력 디렉터리로, "out"을 출력 디렉터리로 사용하여 퍼저를 실행합니다. 샘플은 디스크에 쓰지 않고 공유 메모리를 통해 전달됩니다(-delivery shmem). 커버리지는 test / test.exe 모듈에서 수집됩니다(-instrument_module 플래그). 대상은 영구 모드로 실행되며 test / test.exe 모듈의 fuzz() 함수가 루프에서 실행됩니다. 이 함수는 1개의 인수를 받으며 대상 프로세스를 재시작하기 전에 최대 10000회 반복 동안 루프에서 실행됩니다. 다중 바이트 비교를 쉽게 무차별 대입하기 위해 비교 커버리지가 사용됩니다(-cmp_coverage 플래그). test.exe -m @@는 대상 명령줄이며 @@는 공유 메모리 이름으로 대체됩니다(-delivery shmem을 사용하지 않은 경우 파일 이름으로 대체됩니다).
Jackalope는 다음과 같은 주요 클래스로 구성됩니다:
Fuzzer - 코퍼스와 커버리지 추적, 스레드에 작업 스케줄링, 서버와의 통신(있는 경우) 등 대부분의 고수준 작업을 처리하는 '메인' 클래스입니다. Fuzzer 클래스는 동작을 수정하는 데 사용할 수 있는 여러 가상 메서드를 노출합니다. 사용자는 Fuzzer 클래스를 서브클래싱하고 이러한 메서드를 오버로드하여 사용자 정의 퍼저를 만들 수 있습니다.
Sample - 샘플 데이터(바이트)를 저장하기 위한 간단한 클래스입니다.
Mutator - 변이를 처리합니다. Mutator 클래스의 주요 작업은 샘플을 수정하는 Mutate() 메서드를 구현하는 것입니다. 그러나 뮤테이터는 더 복잡할 수 있으며, 예를 들어 Mutate() 호출 중에 전달될 각 샘플에 대한 추가 컨텍스트를 정의할 수 있습니다. 뮤테이터는 다른 뮤테이터를 다양한 방식으로 결합하는 "메타 뮤테이터"일 수도 있습니다. 내장 뮤테이터는 mutator.h를 참조하세요. 퍼저가 입력 샘플을 선택하면 다음 샘플로 넘어가기 전에 특정 횟수의 "라운드" 동안 퍼징하며, 뮤테이터가 라운드 수를 제어할 수 있습니다. 구체적으로, 퍼저는 최상위 뮤테이터가 Mutate() 메서드에서 false를 반환할 때까지 동일한 입력 샘플로 계속합니다.
Instrumentation - 대상 실행 및 커버리지 수집을 처리합니다. 퍼저는 TinyInst를 사용하는 Instrumentation 구현과 함께 제공됩니다.
SampleDelivery - 샘플을 대상에 전달하는 것을 처리합니다. 퍼저는 파일 및 공유 메모리를 통한 샘플 전달과 함께 번들로 제공됩니다.
PRNG - 의사 난수 생성기. 기본적으로 Jackalope는 Mersenne twister 기반 PRNG를 사용합니다.
Server - 퍼저의 서버 컴포넌트를 구현합니다. 서버는 클라이언트로부터 커버리지와 새로운 커버리지를 트리거하는 샘플을 수집할 책임이 있습니다. 그런 다음 서버는 이러한 샘플을 다른 퍼저 프로세스에 배포합니다.
Client - 서버와 통신하기 위한 메서드를 구현합니다.