원래 Michal Zalewski [email protected]이 개발했습니다.
이 파일을 읽을 시간이 없으면 QuickStartGuide.txt를 참조하십시오.
퍼징은 실제 소프트웨어의 보안 문제를 식별하기 위한 가장 강력하고 입증된 전략 중 하나입니다. 현재까지 보안에 중요한 소프트웨어에서 발견된 원격 코드 실행 및 권한 상승 버그의 대부분을 담당합니다.
불행하게도 퍼징 역시 상대적으로 얕습니다. 맹목적이고 무작위적인 돌연변이로 인해 테스트된 코드의 특정 코드 경로에 도달할 가능성이 거의 없으며 일부 취약점은 확실히 이 기술의 범위를 벗어나게 됩니다.
이 문제를 해결하기 위한 수많은 시도가 있었습니다. Tavis Ormandy가 개척한 초기 접근 방식 중 하나는 코퍼스 증류입니다. 이 방법은 커버리지 신호를 사용하여 대량의 고품질 후보 파일 모음에서 흥미로운 시드의 하위 집합을 선택한 다음 전통적인 방법으로 퍼징합니다. 이 접근 방식은 매우 잘 작동하지만 이러한 코퍼스를 쉽게 사용할 수 있어야 합니다. 또한 블록 범위 측정은 프로그램 상태에 대한 매우 단순한 이해만을 제공하며 장기적으로 퍼징 작업을 안내하는 데는 유용하지 않습니다.
다른 보다 정교한 연구에서는 프로그램 흐름 분석("concolic 실행"), 기호 실행 또는 정적 분석과 같은 기술에 중점을 두었습니다. 이러한 모든 방법은 실험 환경에서는 매우 유망하지만 실제 사용에서는 신뢰성 및 성능 문제를 겪는 경향이 있으며 현재 "멍청한" 퍼징 기술에 대한 실행 가능한 대안을 제공하지 않습니다.
American Fuzzy Lop은 매우 단순하지만 견고한 계측 기반 유전자 알고리즘과 결합된 무차별 퍼저입니다. 이는 수정된 형태의 가장자리 적용 범위를 사용하여 프로그램 제어 흐름에 대한 미묘한 로컬 규모 변경 사항을 쉽게 포착합니다.
조금 단순화하면 전체 알고리즘은 다음과 같이 요약될 수 있습니다.
사용자가 제공한 초기 테스트 사례를 대기열에 로드합니다.
대기열에서 다음 입력 파일을 가져옵니다.
프로그램의 측정된 동작을 변경하지 않는 가장 작은 크기로 테스트 케이스를 자르려고 시도합니다.
균형있고 잘 연구된 다양한 기존 퍼징 전략을 사용하여 파일을 반복적으로 변형합니다.
생성된 변형으로 인해 계측에 의해 기록된 새로운 상태 전환이 발생한 경우 변형된 출력을 대기열의 새 항목으로 추가합니다.
2로 가세요.
발견된 테스트 사례는 정기적으로 선별되어 더 새롭고 높은 적용 범위의 발견으로 쓸모없어진 테스트 사례를 제거합니다. 여러 가지 다른 계측 중심 노력 최소화 단계를 거칩니다.
퍼징 프로세스의 부차적인 결과로 이 도구는 흥미로운 테스트 사례로 구성된 작고 독립적인 코퍼스를 생성합니다. 이는 스트레스 테스트 브라우저, 사무용 애플리케이션, 그래픽 제품군 또는 비공개 소스 도구와 같이 노동 집약적이거나 리소스 집약적인 기타 테스트 체제를 시드하는 데 매우 유용합니다.
퍼저는 철저한 테스트를 거쳐 블라인드 퍼징 또는 적용 전용 도구보다 훨씬 뛰어난 기본 성능을 제공합니다.
소스 코드를 사용할 수 있는 경우 타사 코드에 대한 표준 빌드 프로세스에서 gcc 또는 clang을 즉시 대체하는 도구로 작동하는 도구를 삽입할 수 있습니다.
계측은 성능에 상당히 적은 영향을 미칩니다. afl-fuzz에 의해 구현된 다른 최적화와 함께 대부분의 프로그램은 기존 도구를 사용하여 가능한 한 빨리 또는 훨씬 더 빠르게 퍼지될 수 있습니다.
대상 프로그램을 다시 컴파일하는 올바른 방법은 빌드 프로세스의 세부 사항에 따라 다를 수 있지만 거의 보편적인 접근 방식은 다음과 같습니다.
$ CC=/path/to/afl/afl-gcc ./configure
$ make clean all
C++ 프로그램의 경우 CXX=/path/to/afl/afl-g++
설정하는 것이 좋습니다.
clang 래퍼(afl-clang 및 afl-clang++)도 동일한 방식으로 사용할 수 있습니다. clang 사용자는 llvm_mode/README.llvm에 설명된 대로 고성능 계측 모드를 활용하도록 선택할 수도 있습니다.
라이브러리를 테스트할 때 stdin 또는 파일에서 데이터를 읽고 이를 테스트된 라이브러리에 전달하는 간단한 프로그램을 찾거나 작성해야 합니다. 이러한 경우 이 실행 파일을 계측된 라이브러리의 정적 버전에 연결하거나 런타임에 올바른 .so 파일이 로드되는지 확인하는 것이 중요합니다(일반적으로 LD_LIBRARY_PATH
설정을 통해). 가장 간단한 옵션은 일반적으로 다음을 통해 가능한 정적 빌드입니다.
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
'make'를 호출할 때 AFL_HARDEN=1
로 설정하면 CC 래퍼가 자동으로 코드 강화 옵션을 활성화하여 간단한 메모리 버그를 더 쉽게 감지할 수 있습니다. AFL에 포함된 도우미 라이브러리인 Libdislocator(libdislocator/README.dislocator 참조)는 힙 손상 문제를 찾는 데도 도움이 될 수 있습니다.
추신. ASAN 사용자는 Notes_for_asan.txt 파일에서 중요한 주의 사항을 검토하는 것이 좋습니다.
소스 코드를 사용할 수 없는 경우 fuzzer는 블랙박스 바이너리의 빠르고 즉각적인 계측을 위한 실험적 지원을 제공합니다. 이는 덜 알려진 "사용자 공간 에뮬레이션" 모드에서 실행되는 QEMU 버전을 통해 수행됩니다.
QEMU는 AFL과 별개의 프로젝트이지만 다음을 수행하여 편리하게 기능을 구축할 수 있습니다.
$ cd qemu_mode
$ ./build_qemu_support.sh
추가 지침 및 주의 사항은 qemu_mode/README.qemu를 참조하세요.
이 모드는 컴파일 시간 계측보다 약 2~5배 느리고 병렬화에 덜 도움이 되며 몇 가지 다른 단점이 있을 수 있습니다.
올바르게 작동하려면 fuzzer에는 대상 애플리케이션에서 일반적으로 예상되는 입력 데이터의 좋은 예가 포함된 하나 이상의 시작 파일이 필요합니다. 두 가지 기본 규칙이 있습니다.
파일을 작게 유지하십시오. 꼭 필요한 것은 아니지만 1kB 미만이 이상적입니다. 크기가 중요한 이유에 대한 설명은 perf_tips.txt를 참조하세요.
기능적으로 서로 다른 경우에만 여러 테스트 케이스를 사용하십시오. 이미지 라이브러리를 혼란스럽게 만들기 위해 50개의 서로 다른 휴가 사진을 사용하는 것은 의미가 없습니다.
이 도구와 함께 제공되는 testcases/ 하위 디렉터리에서 파일 시작에 대한 많은 좋은 예를 찾을 수 있습니다.
추신. 스크리닝에 대규모 데이터 모음을 사용할 수 있는 경우 afl-cmin 유틸리티를 사용하여 대상 바이너리에서 서로 다른 코드 경로를 실행하는 기능적으로 구별되는 파일의 하위 집합을 식별할 수 있습니다.
퍼징 프로세스 자체는 afl-fuzz 유틸리티에 의해 수행됩니다. 이 프로그램에는 초기 테스트 사례가 있는 읽기 전용 디렉터리, 결과를 저장할 별도의 장소, 테스트할 바이너리 경로가 필요합니다.
stdin에서 직접 입력을 허용하는 대상 바이너리의 경우 일반적인 구문은 다음과 같습니다.
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
파일에서 입력을 받는 프로그램의 경우 '@@'을 사용하여 입력 파일 이름이 배치되어야 하는 대상의 명령줄 위치를 표시합니다. Fuzzer는 이를 다음과 같이 대체합니다.
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
-f 옵션을 사용하여 변형된 데이터를 특정 파일에 기록할 수도 있습니다. 이는 프로그램이 특정 파일 확장자를 기대하는 경우 유용합니다.
계측되지 않은 바이너리는 QEMU 모드(명령줄에 -Q 추가) 또는 기존의 블라인드 퍼저 모드(-n 지정)에서 퍼징될 수 있습니다.
-t 및 -m을 사용하여 실행된 프로세스에 대한 기본 시간 초과 및 메모리 제한을 재정의할 수 있습니다. 이러한 설정을 수정해야 할 수 있는 대상의 드문 예로는 컴파일러와 비디오 디코더가 있습니다.
퍼징 성능 최적화를 위한 팁은 perf_tips.txt에서 설명합니다.
afl-fuzz는 일련의 결정론적 퍼징 단계를 수행하는 것으로 시작합니다. 이 단계는 며칠이 걸릴 수 있지만 깔끔한 테스트 케이스를 생성하는 경향이 있습니다. zzuf 및 기타 기존 fuzzer와 유사하게 빠르고 더러운 결과를 즉시 원한다면 -d 옵션을 명령줄에 추가하세요.
표시된 통계를 해석하고 프로세스 상태를 모니터링하는 방법에 대한 자세한 내용은 status_screen.txt 파일을 참조하세요. 특히 UI 요소가 빨간색으로 강조표시된 경우 이 파일을 참조하세요.
퍼징 프로세스는 Ctrl-C를 누를 때까지 계속됩니다. 최소한 fuzzer가 하나의 대기열 주기를 완료할 수 있도록 허용해야 하며, 이는 몇 시간에서 일주일 정도 걸릴 수 있습니다.
출력 디렉터리 내에 생성되고 실시간으로 업데이트되는 세 개의 하위 디렉터리가 있습니다.
queue/ - 모든 고유 실행 경로에 대한 테스트 케이스와 사용자가 제공한 모든 시작 파일입니다. 이는 섹션 2에서 언급한 합성 코퍼스입니다. 이 코퍼스를 다른 목적으로 사용하기 전에 afl-cmin 도구를 사용하여 더 작은 크기로 축소할 수 있습니다. 이 도구는 동등한 가장자리 범위를 제공하는 더 작은 파일 하위 집합을 찾습니다.
crashes/ - 테스트된 프로그램이 치명적인 신호(예: SIGSEGV, SIGILL, SIGABRT)를 수신하게 만드는 고유한 테스트 사례입니다. 항목은 수신된 신호별로 그룹화됩니다.
hangs/ - 테스트된 프로그램의 시간 초과를 유발하는 고유한 테스트 사례입니다. 정지로 분류되기 전의 기본 시간 제한은 1초와 -t 매개변수 값 중 더 큰 값입니다. AFL_HANG_TMOUT을 설정하여 값을 미세 조정할 수 있지만 이는 거의 필요하지 않습니다.
관련 실행 경로에 이전에 기록된 오류에서 볼 수 없는 상태 전환이 포함된 경우 충돌 및 중단이 "고유한" 것으로 간주됩니다. 단일 버그가 여러 방법으로 접근할 수 있는 경우 프로세스 초기에 카운트 인플레이션이 발생하지만 이는 빠르게 줄어들 것입니다.
충돌 및 중단에 대한 파일 이름은 오류가 없는 상위 대기열 항목과 상호 연관됩니다. 이는 디버깅에 도움이 될 것입니다.
afl-fuzz에서 발견한 충돌을 재현할 수 없는 경우 가장 가능성이 높은 원인은 도구에서 사용하는 것과 동일한 메모리 제한을 설정하지 않았기 때문입니다. 노력하다:
$ LIMIT_MB=50
$ ( ulimit -Sv $[LIMIT_MB << 10] ; /path/to/tested_binary ... )
afl-fuzz에 전달된 -m 매개변수와 일치하도록 LIMIT_MB를 변경합니다. OpenBSD에서는 -Sv도 -Sd로 변경합니다.
기존 출력 디렉터리를 사용하여 중단된 작업을 재개할 수도 있습니다. 노력하다:
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
gnuplot이 설치되어 있으면 afl-plot을 사용하여 활성 퍼징 작업에 대한 멋진 그래프를 생성할 수도 있습니다. 이것이 어떻게 보이는지에 대한 예는 http://lcamtuf.coredump.cx/afl/plot/을 참조하십시오.
afl-fuzz의 모든 인스턴스는 대략 하나의 코어를 차지합니다. 이는 멀티 코어 시스템에서 하드웨어를 완전히 활용하려면 병렬화가 필요하다는 것을 의미합니다. 여러 코어 또는 여러 네트워크로 연결된 시스템에서 공통 대상을 퍼징하는 방법에 대한 팁은 parallel_fuzzing.txt를 참조하세요.
병렬 퍼징 모드는 또한 AFL을 다른 퍼저, 기호 또는 concolic 실행 엔진 등에 인터페이스하는 간단한 방법을 제공합니다. 다시 한번 말씀드리지만, 팁은 parallel_fuzzing.txt의 마지막 섹션을 참조하세요.
기본적으로 afl-fuzz 돌연변이 엔진은 이미지, 멀티미디어, 압축 데이터, 정규식 구문 또는 쉘 스크립트와 같은 압축 데이터 형식에 최적화되어 있습니다. 특히 HTML, SQL 또는 JavaScript를 포함하여 장황하고 중복된 표현이 있는 언어에는 다소 덜 적합합니다.
구문 인식 도구를 구축하는 번거로움을 피하기 위해 afl-fuzz는 선택적 언어 키워드 사전, 매직 헤더 또는 대상 데이터 유형과 관련된 기타 특수 토큰을 사용하여 퍼징 프로세스를 시드하고 이를 사용하여 재구성하는 방법을 제공합니다. 이동 중에도 기본 문법:
http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html
이 기능을 사용하려면 먼저 dictionaries/README.dictionaries에 설명된 두 가지 형식 중 하나로 사전을 만들어야 합니다. 그런 다음 명령줄에서 -x 옵션을 통해 fuzzer를 지정합니다.
(해당 하위 디렉토리에는 이미 여러 개의 공통 사전이 제공되어 있습니다.)
기본 구문에 대해 더 구조화된 설명을 제공할 수 있는 방법은 없지만 퍼저는 계측 피드백만을 기반으로 이 중 일부를 알아낼 가능성이 높습니다. 이것은 실제로 실제로 작동합니다.
http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html
추신. 명시적 사전이 제공되지 않은 경우에도 afl-fuzz는 결정적 바이트 플립 중에 계측을 매우 면밀히 관찰하여 입력 코퍼스에서 기존 구문 토큰을 추출하려고 시도합니다. 이는 일부 유형의 파서 및 문법에 작동하지만 -x 모드만큼 좋지는 않습니다.
사전을 구하기가 정말 어려운 경우 또 다른 옵션은 AFL을 잠시 동안 실행한 다음 AFL과 함께 제공되는 유틸리티로 제공되는 토큰 캡처 라이브러리를 사용하는 것입니다. 이에 대해서는 libtokencap/README.tokencap을 참조하세요.
적용 범위 기반 충돌 그룹화는 일반적으로 수동으로 또는 매우 간단한 GDB 또는 Valgrind 스크립트를 사용하여 신속하게 분류할 수 있는 작은 데이터 세트를 생성합니다. 또한 모든 크래시는 대기열에 있는 크래시가 아닌 상위 테스트 사례까지 추적 가능하므로 결함을 더 쉽게 진단할 수 있습니다.
하지만 일부 퍼징 충돌은 많은 디버깅 및 코드 분석 작업 없이 악용 가능성을 신속하게 평가하기 어려울 수 있다는 점을 인정하는 것이 중요합니다. 이 작업을 지원하기 위해 afl-fuzz는 -C 플래그로 활성화된 매우 독특한 "충돌 탐색" 모드를 지원합니다.
이 모드에서 퍼저는 하나 이상의 충돌 테스트 사례를 입력으로 사용하고 피드백 기반 퍼징 전략을 사용하여 충돌 상태를 유지하면서 프로그램에서 도달할 수 있는 모든 코드 경로를 매우 빠르게 열거합니다.
충돌을 일으키지 않는 돌연변이는 거부됩니다. 실행 경로에 영향을 주지 않는 변경 사항도 마찬가지입니다.
출력은 공격자가 오류가 발생한 주소에 대해 어느 정도의 제어권을 가지고 있는지 또는 초기 범위를 벗어난 읽기를 통과할 수 있는지 여부를 확인하기 위해 매우 빠르게 검사할 수 있는 작은 파일 모음입니다. 그리고 그 아래에 무엇이 있는지 확인합니다. .
아, 한 가지 더: 테스트 케이스 최소화를 위해 afl-tmin을 사용해 보세요. 이 도구는 매우 간단한 방법으로 작동할 수 있습니다.
$ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
이 도구는 충돌이 발생하는 테스트 사례와 충돌이 발생하지 않는 테스트 사례 모두에서 작동합니다. 충돌 모드에서는 계측된 바이너리와 계측되지 않은 바이너리를 모두 허용합니다. 비충돌 모드에서 최소화 프로그램은 표준 AFL 계측을 사용하여 실행 경로를 변경하지 않고 파일을 더 단순하게 만듭니다.
최소화 프로그램은 afl-fuzz와 호환되는 방식으로 -m, -t, -f 및 @@ 구문을 허용합니다.
AFL에 최근 추가된 또 다른 도구는 afl-analyze 도구입니다. 입력 파일을 가져와 순차적으로 바이트 뒤집기를 시도하고 테스트된 프로그램의 동작을 관찰합니다. 그런 다음 중요한 섹션과 그렇지 않은 섹션에 따라 입력을 색상으로 구분합니다. 완벽하지는 않지만 종종 복잡한 파일 형식에 대한 빠른 통찰력을 제공할 수 있습니다. 해당 작업에 대한 자세한 내용은 Technical_details.txt 끝부분에서 확인할 수 있습니다.
퍼징은 충돌하지 않는 설계 및 구현 오류를 발견하는 데에도 훌륭하지만 활용도가 낮은 기술입니다. 다음과 같은 경우 abort()를 호출하도록 대상 프로그램을 수정하여 몇 가지 흥미로운 버그가 발견되었습니다.
두 개의 bignum 라이브러리는 동일한 fuzzer 생성 입력이 주어지면 서로 다른 출력을 생성합니다.
이미지 라이브러리는 동일한 입력 이미지를 연속해서 여러 번 디코딩하라는 요청을 받으면 서로 다른 출력을 생성합니다.
직렬화/역직렬화 라이브러리는 fuzzer 제공 데이터를 반복적으로 직렬화 및 역직렬화할 때 안정적인 출력을 생성하지 못합니다.
압축 라이브러리는 특정 Blob을 압축한 다음 압축을 풀도록 요청하면 입력 파일과 일치하지 않는 출력을 생성합니다.
이러한 또는 유사한 온전성 검사를 구현하는 데는 일반적으로 시간이 거의 걸리지 않습니다. 특정 패키지의 관리자인 경우 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
(libfuzzer와도 공유되는 플래그) 또는 #ifdef __AFL_COMPILER
(이것은 AFL에만 해당)를 사용하여 이 코드를 조건부로 만들 수 있습니다.
계산 집약적인 다른 많은 작업과 마찬가지로 퍼징은 하드웨어와 OS에 부담을 줄 수 있다는 점을 명심하세요. 특히:
CPU가 뜨거워지고 적절한 냉각이 필요합니다. 대부분의 경우 냉각이 충분하지 않거나 제대로 작동하지 않으면 CPU 속도가 자동으로 조절됩니다. 즉, 특히 적합하지 않은 하드웨어(노트북, 스마트폰 등)에서 퍼징할 때 무언가가 터지는 것이 완전히 불가능한 것은 아닙니다.
대상 프로그램은 비정상적으로 기가바이트의 메모리를 확보하거나 디스크 공간을 정크 파일로 가득 채울 수 있습니다. AFL은 기본 메모리 제한을 적용하려고 시도하지만 발생할 수 있는 모든 사고를 예방할 수는 없습니다. 요점은 데이터 손실 가능성이 허용 가능한 위험이 아닌 시스템에 대해 퍼징을 해서는 안 된다는 것입니다.
퍼징에는 파일 시스템에 대한 수십억 개의 읽기 및 쓰기가 포함됩니다. 최신 시스템에서는 일반적으로 캐시가 많이 캐시되어 "물리적" I/O가 상당히 적지만 이 방정식을 변경할 수 있는 요소가 많이 있습니다. 잠재적인 문제를 모니터링하는 것은 귀하의 책임입니다. I/O가 매우 많으면 많은 HDD 및 SSD의 수명이 단축될 수 있습니다.
Linux에서 디스크 I/O를 모니터링하는 좋은 방법은 'iostat' 명령입니다.
$ iostat -d 3 -x -k [...optional disk ID...]
AFL에 대한 가장 중요한 주의 사항은 다음과 같습니다.
AFL은 신호(SIGSEGV, SIGABRT 등)로 인해 처음 생성된 프로세스가 죽는지 확인하여 오류를 감지합니다. 이러한 신호에 대한 사용자 정의 처리기를 설치하는 프로그램에서는 관련 코드를 주석 처리해야 할 수도 있습니다. 같은 맥락에서, 퍼징된 대상에 의해 생성된 하위 프로세스의 결함은 이를 포착하기 위해 일부 코드를 수동으로 추가하지 않는 한 감지를 피할 수 있습니다.
다른 무차별 대입 도구와 마찬가지로 fuzzer는 암호화, 체크섬, 암호화 서명 또는 압축을 사용하여 테스트할 실제 데이터 형식을 완전히 래핑하는 경우 제한된 적용 범위를 제공합니다.
이 문제를 해결하려면 관련 검사를 주석 처리하면 됩니다(영감을 얻으려면 실험적/libpng_no_checksum/ 참조). 이것이 가능하지 않다면 실험적/post_library/에 설명된 대로 후처리기를 작성할 수도 있습니다.
ASAN과 64비트 바이너리에는 몇 가지 불행한 절충점이 있습니다. 이는 afl-fuzz의 특정 결함으로 인한 것이 아닙니다. 팁은 Notes_for_asan.txt를 참조하세요.
퍼징 네트워크 서비스, 백그라운드 데몬 또는 UI 상호 작용이 필요한 대화형 앱에 대한 직접적인 지원은 없습니다. 보다 전통적인 방식으로 작동하도록 하려면 간단한 코드를 변경해야 할 수도 있습니다. Preeny는 비교적 간단한 옵션도 제공할 수 있습니다. https://github.com/zardus/preeny를 참조하세요.
네트워크 기반 서비스 수정에 대한 몇 가지 유용한 팁은 https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop에서도 확인할 수 있습니다.
AFL은 사람이 읽을 수 있는 커버리지 데이터를 출력하지 않습니다. 적용 범위를 모니터링하려면 Michael Rash의 afl-cov를 사용하세요: https://github.com/mrash/afl-cov
때때로 지각을 가진 기계가 창조자에 맞서 일어납니다. 이런 일이 발생하면 http://lcamtuf.coredump.cx/prep/를 참조하세요.
이 외에도 플랫폼별 팁은 INSTALL을 참조하세요.
afl-fuzz의 많은 개선 사항은 다음의 피드백, 버그 보고서 또는 패치 없이는 불가능합니다.
Jann Horn Hanno Boeck
Felix Groebert Jakub Wilk
Richard W. M. Jones Alexander Cherepanov
Tom Ritter Hovik Manucharyan
Sebastian Roschke Eberhard Mattes
Padraig Brady Ben Laurie
@dronesec Luca Barbato
Tobias Ospelt Thomas Jarosch
Martin Carpenter Mudge Zatko
Joe Zbiciak Ryan Govostes
Michael Rash William Robinet
Jonathan Gray Filipe Cabecinhas
Nico Weber Jodie Cunningham
Andrew Griffiths Parker Thompson
Jonathan Neuschfer Tyler Nighswander
Ben Nagy Samir Aguiar
Aidan Thornton Aleksandar Nikolich
Sam Hakim Laszlo Szekeres
David A. Wheeler Turo Lamminen
Andreas Stieger Richard Godbee
Louis Dassy teor2345
Alex Moneger Dmitry Vyukov
Keegan McAllister Kostya Serebryany
Richo Healey Martijn Bogaard
rc0r Jonathan Foote
Christian Holler Dominique Pelle
Jacek Wielemborek Leo Barnes
Jeremy Barnes Jeff Trull
Guillaume Endignoux ilovezfs
Daniel Godas-Lopez Franjo Ivancic
Austin Seipp Daniel Komaromy
Daniel Binderman Jonathan Metzman
Vegard Nossum Jan Kneschke
Kurt Roeckx Marcel Bohme
Van-Thuan Pham Abhik Roychoudhury
Joshua J. Drake Toby Hutton
Rene Freingruber Sergey Davidoff
Sami Liedes Craig Young
Andrzej Jackowski Daniel Hodson
감사합니다!
질문? 우려사항이 있나요? 버그 신고? GitHub를 이용해주세요.
프로젝트에 대한 메일링 리스트도 있습니다. 가입하려면 [email protected]으로 메일을 보내세요. 또는 먼저 아카이브를 탐색하고 싶다면 https://groups.google.com/group/afl-users를 시도해 보세요.