前言

这是一个libprotobuf-mutator的练习项目

环境搭建参考

所有环境与代码已上传至GitHub

搭建

安装Clang/LLVM & libfuzzer

1
sudo apt-get install clang-11 libfuzzer-11-dev

安装libprotobuf-mutator

安装说明

安装相关依赖:

1
2
3
sudo apt-get update
sudo apt-get install protobuf-compiler libprotobuf-dev binutils cmake \
ninja-build liblzma-dev libz-dev pkg-config autoconf libtool

然后编译测试,先把libprotobuf-mutator这个项目给git下来W

1
2
3
4
5
6
7
8
9
10
11
12
cd libprotobuf-mutator
mkdir build
cd build
#编译动态库
cmake .. -GNinja -DCMAKE_C_COMPILER=clang-11 \
-DCMAKE_CXX_COMPILER=clang++-11 \
-DCMAKE_BUILD_TYPE=Debug \
-DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \
-DCMAKE_C_FLAGS="-fPIC" -DCMAKE_CXX_FLAGS="-fPIC"
ninja check # test, might took very long time
ninja # just build, use this if you don't want to wait too long
sudo ninja install # install

如果要把libprotobuf编译成动态库的话,可以用:

1
2
3
4
5
cmake .. -GNinja -DCMAKE_C_COMPILER=clang-11 \ 
-DCMAKE_CXX_COMPILER=clang++-11 \
-DCMAKE_BUILD_TYPE=Debug \
-DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \
-DBUILD_SHARED_LIBS=ON

或者:

1
make -j 12

关于更多环境搭建的步骤,看原项目就好了(个人感觉环境搭建还挺费时的),或者这里

练习1:simple_protobuf

这个练习就是熟悉一下protobuf

如下:

Buffers
1
2
3
4
5
6
syntax = "proto2";

message TEST {
required uint32 a = 1;
required string b = 2;
}

接下来编译这个protobuf

很简单,使用protoc并指定输出路径就行,如下所示:

1
2
mkdir genfiles  
protoc ./test.proto --cpp_out=./genfiles

编译完成之后,就会发现在genfiles/目录下生成了.pb.cc和.pb.h文件

然后我们写个简单的程序来测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "test.pb.h"

#include <bits/stdc++.h>

using std::cin;
using std::cout;
using std::endl;

int main(int argc, char *argv[])
{
TEST t;
t.set_a(101);
t.set_b("testtest");
cout << t.a() << endl;
cout << t.b() << endl;
return 0;
}

接下来就是编写Makefile,设置好lib路径、头文件、clang等等

当然我这里把protoc一起整进来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
PROTOC=../../external/bin/protoc
CC=clang
CXX=clang++
INC=-I../../ -I../../external/include
CXXFLAGS=-O3 -fPIC -std=c++11 $(INC)
LD=$(CXX)

PROTOBUF_DIR=../../build/external.protobuf
PROTOBUF_LIB=../../external/lib/libprotobuf-mutator-libfuzzer.a ../../external/lib/libprotobuf-mutator.a $(PROTOBUF_DIR)/lib/libprotobuf.a

test_proto: test.pb.o test_proto.o
$(LD) -pthread $(CXXFLAGS) -o $@ $^ $(PROTOBUF_LIB)

test.pb.cc test.pb.h: test.proto
$(PROTOC) --cpp_out=. $<

test.pb.o: test.pb.cc
$(CXX) $(CXXFLAGS) -c -o $@ $<

%.o: %.cc
$(CXX) $(CXXFLAGS) -c -o $@ $<

.PHONY: clean
clean:
rm *.pb.*
rm test_proto
rm *.o

如果一切顺利的话,会发现生成了test_proto文件,运行:

image.png

这样的话,就说明我们的第一个protobuf编译成功了

练习2:libprotobuf_libfuzzer

这个练习的目的就是结合libfuzzer,写出自己的变异fuzz

我们需要把前面生成的test.pb.h和test.pb.cc搬过来

这一练习多了两个文件:

  • lpm_libfuzz.cc: 将 protobuf 转换为原始数据并将其提供给目标函数
  • harness.cc: 我们需要fuzz的目标函数

我们需要各自编译,然后将其串起来

lpm_libfuzz.cc如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h"
#include "test.pb.h"

#include <bits/stdc++.h>

using std::cin;
using std::cout;
using std::endl;

std::string ProtoToData(const TEST &test_proto) {
std::stringstream all;
const auto &aa = test_proto.a();
const auto &bb = test_proto.b();
all.write((const char*)&aa, sizeof(aa));
if(bb.size() != 0) {
all.write(bb.c_str(), bb.size());
}

std::string res = all.str();
if (bb.size() != 0 && res.size() != 0) {
// set PROTO_FUZZER_DUMP_PATH env to dump the serialized protobuf
if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
std::ofstream of(dump_path);
of.write(res.data(), res.size());
}
}
return res;
}

extern "C" int FuzzTEST(const uint8_t* data, size_t size); // our customized fuzzing function

DEFINE_PROTO_FUZZER(const TEST &test_proto) {
auto s = ProtoToData(test_proto); // convert protobuf to raw data
FuzzTEST((const uint8_t*)s.data(), s.size()); // fuzz the function
}

大概就是把我们编写的TEST转为一个string了,这样才能喂进fuzz

Makefile编写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
TARGET=lpm_libfuzz
PROTOC=../../external/bin/protoc
CC=clang
CXX=clang++
INC=-I../../ -I../../external/include -I../../external/include/libprotobuf-mutator
CXXFLAGS=-O3 -fPIC -std=c++11 -fsanitize=fuzzer,address $(INC)
DFUZZ=-DLLVMFuzzerTestOneInput=FuzzTEST
LD=$(CXX)
#include头文件路径也是图省力

PROTOBUF_DIR=../../build/external.protobuf
PROTOBUF_LIB=../../external/lib/libprotobuf-mutator-libfuzzer.a ../../external/lib/libprotobuf-mutator.a $(PROTOBUF_DIR)/lib/libprotobuf.a
#相应的库路径,为了图省力一下子全写进来了

all: $(TARGET)

$(TARGET): test.pb.o harness.o $(TARGET).o
$(LD) -pthread $(CXXFLAGS) -o $@ $^ $(PROTOBUF_LIB)

test.pb.cc test.pb.h: test.proto
$(PROTOC) --cpp_out=. $<

$(TARGET).o: $(TARGET).cc
$(CXX) $(CXXFLAGS) -c -o $@ $<

harness.o: harness.cc
$(CXX) $(CXXFLAGS) -c $(DFUZZ) $<

test.pb.o: test.pb.cc
$(CXX) $(CXXFLAGS) -c -o $@ $<

%.o: %.cc
$(CXX) $(CXXFLAGS) -c -o $@ $<

.PHONY: clean
clean:
rm *.pb.*
rm $(TARGET)
rm *.o

编译完事了之后,就会发现生成了lpm_libfuzz

注意我们的harness此时的触发条件为第一个字节时”\x01”

一运行就crash了

会发现有类似这样的crash文件生成信息提示:

image.png

我这里跑了三遍,我们打开三个crash文件看看,如下:

crash1:

image.png

crash2:

image.png

crash3:

image.png

其中:

268435457=0x10000001,转化后结果为”\x01\x00\x00\x10”

285212673=0x11000001,转化后结果为”\x01x00\x00\x11”

可以看到,三个样本都与我们的预期相符合,即遇到首字节为”\x01”时触发crash

练习3:libprotobuf_libfuzzer_custom_mutator

这个练习其实代码角度来说跟练习2很像,只是在这里我们开始加入了自己的变异策略而不再使用原来的变异策略

改动如下,lpm_libufuzz_custom_mutator.cc(原词不太文明,我们这里换一下):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h"
#include "test.pb.h"

#include <bits/stdc++.h>

using std::cin;
using std::cout;
using std::endl;

std::string ProtoToData(const TEST &test_proto) {
std::stringstream all;
const auto &aa = test_proto.a();
const auto &bb = test_proto.b();
all.write((const char*)&aa, sizeof(aa));
if(bb.size() != 0) {
all.write(bb.c_str(), bb.size());
}

std::string res = all.str();
if (bb.size() != 0 && res.size() != 0) {
// set PROTO_FUZZER_DUMP_PATH env to dump the serialized protobuf
if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
std::ofstream of(dump_path);
of.write(res.data(), res.size());
}
}
return res;
}

extern "C" int FuzzTEST(const uint8_t* data, size_t size); // our customized fuzzing function
bool hasRegister = false;

DEFINE_PROTO_FUZZER(const TEST &test_proto) {
/* Register post processor with our custom mutator method */
if(!hasRegister) {
protobuf_mutator::libfuzzer::RegisterPostProcessor(
TEST::descriptor(),
[](google::protobuf::Message* message, unsigned int seed) {
TEST *t = static_cast<TEST *>(message);
/* test.b will only be "VICTORY" or "DEFEAT" */
if (seed % 2) {
t->set_b("VICTORY");
}
else {
t->set_b("DEFEAT");
}
}
);
hasRegister = true;
return;
}

auto s = ProtoToData(test_proto);
FuzzTEST((const uint8_t*)s.data(), s.size());
}

然后Makefile的话也跟原来的也是基本一样的,修改下TARGET就行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
TARGET=lpm_libfuzz_custom_mutator
PROTOC=../../external/bin/protoc
CC=clang
CXX=clang++
INC=-I../../ -I../../external/include -I../../external/include/libprotobuf-mutator
CXXFLAGS=-O3 -fPIC -std=c++11 -fsanitize=fuzzer,address $(INC)
DFUZZ=-DLLVMFuzzerTestOneInput=FuzzTEST
LD=$(CXX)
#include头文件路径也是图省力

PROTOBUF_DIR=../../build/external.protobuf
PROTOBUF_LIB=../../external/lib/libprotobuf-mutator-libfuzzer.a ../../external/lib/libprotobuf-mutator.a $(PROTOBUF_DIR)/lib/libprotobuf.a
#相应的库路径,为了图省力一下子全写进来了

all: $(TARGET)

$(TARGET): test.pb.o harness.o $(TARGET).o
$(LD) -pthread $(CXXFLAGS) -o $@ $^ $(PROTOBUF_LIB)

test.pb.cc test.pb.h: test.proto
$(PROTOC) --cpp_out=. $<

$(TARGET).o: $(TARGET).cc
$(CXX) $(CXXFLAGS) -c -o $@ $<

harness.o: harness.cc
$(CXX) $(CXXFLAGS) -c $(DFUZZ) $<

test.pb.o: test.pb.cc
$(CXX) $(CXXFLAGS) -c -o $@ $<

%.o: %.cc
$(CXX) $(CXXFLAGS) -c -o $@ $<

.PHONY: clean
clean:
rm *.pb.*
rm $(TARGET)
rm *.o

然后我们来运行这个fuzz:

1
./lpm_libfuzz_custom_mutator

运行几次过后会发现,crash样本中的b不是VICTORY就是DEFEAT

image.png

image.png

可见,这与我们的预期变异策略是相符合的

练习4:libprotobuf_aflpp_custom_mutator

这里就要将afl++和我们自写的protobuf结合起来使用

我们需要完成afl++给我们提供的一些接口函数

lpm_aflpp_custom_mutator.h中的定义

1
2
3
4
5
6
7
8
9
10
11
#include "libprotobuf-mutator/src/mutator.h"
#include "test.pb.h"

#include <bits/stdc++.h>

extern "C" size_t afl_custom_fuzz(void *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf, uint8_t *add_buf, size_t add_buf_size, size_t max_size);

class MyMutator : public protobuf_mutator::Mutator {
public:
friend size_t afl_custom_fuzz(void *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf, uint8_t *add_buf, size_t add_buf_size, size_t max_size);
};

lpm_aflpp_custom_mutator.cc如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include "lpm_aflpp_custom_mutator.h"

using std::cin;
using std::cout;
using std::endl;

std::string ProtoToData(const TEST &test_proto) {
std::stringstream all;
const auto &aa = test_proto.a();
const auto &bb = test_proto.b();
all.write((const char*)&aa, sizeof(aa));
if(bb.size() != 0) {
all.write(bb.c_str(), bb.size());
}

std::string res = all.str();
if (bb.size() != 0 && res.size() != 0) {
// set PROTO_FUZZER_DUMP_PATH env to dump the serialized protobuf
if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
std::ofstream of(dump_path);
of.write(res.data(), res.size());
}
}
return res;
}

/**
* Initialize this custom mutator
*
* @param[in] afl a pointer to the internal state object. Can be ignored for
* now.
* @param[in] seed A seed for this mutator - the same seed should always mutate
* in the same way.
* @return Pointer to the data object this custom mutator instance should use.
* There may be multiple instances of this mutator in one afl-fuzz run!
* Return NULL on error.
*/
extern "C" void *afl_custom_init(void *afl, unsigned int seed) {
srand(seed);
return afl;
}

/**
* Perform custom mutations on a given input
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[in] buf Pointer to input data to be mutated
* @param[in] buf_size Size of input data
* @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on
* error.
* @param[in] add_buf Buffer containing the additional test case
* @param[in] add_buf_size Size of the additional test case
* @param[in] max_size Maximum size of the mutated output. The mutation must not
* produce data larger than max_size.
* @return Size of the mutated output.
*/
extern "C" size_t afl_custom_fuzz(void *data, // afl state
uint8_t *buf, size_t buf_size, // input data to be mutated
uint8_t **out_buf, // output buffer
uint8_t *add_buf, size_t add_buf_size, // add_buf can be NULL
size_t max_size) {

// This function can be named either "afl_custom_fuzz" or "afl_custom_mutator"
// A simple test shows that "buf" will be the content of the current test case
// "add_buf" will be the next test case ( from AFL++'s input queue )

// Here we implement our own custom mutator
static MyMutator mutator;
TEST input;
// mutate input.a ( integer )
int id = rand() % 305;
input.set_a(id);
// mutate input.b ( string )
std::string tmp = "";
std::string new_string = mutator.MutateString(tmp, 1000); // use the default protobuf mutator
input.set_b(new_string);
// convert input from TEST to raw data, and copy to mutated_out
const TEST *p = &input;
std::string s = ProtoToData(*p); // convert TEST to raw data
// Copy the raw data to output buffer
size_t mutated_size = s.size() <= max_size ? s.size() : max_size; // check if raw data's size is larger than max_size
uint8_t *mutated_out = new uint8_t[mutated_size+1];
memcpy(mutated_out, s.c_str(), mutated_size);
*out_buf = mutated_out;

return mutated_size;
}

/**
* Deinitialize everything
*
* @param data The data ptr from afl_custom_init
*/
extern "C" void afl_custom_deinit(void *data) {
// Honestly I don't know what I'm gonna do with this...
return;
}

afl_custom_fuzz函数就是我们自写的变异

这里对于uint32 a的变异策略为产生0-304的随机数

然后通过ProtoToData进行序列化后进行返回

然后我们要fuzz的程序vuln.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
char str[100]={};
read(0, str, 100);
int *ptr = NULL;
if( str[0] == '\x02' || str[0] == '\xe8') {
*ptr = 123;
}
return 0;
}

当首字节为”\x02”或”\xe8”时会触发crash

Makefile如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
TARGET=lpm_aflpp_custom_mutator
PROTOC=../../external/bin/protoc
CC=clang
CXX=clang++
AFLCC=afl-clang-fast
INC=-I../../ -I../../external/include -I../../external/include/libprotobuf-mutator
CXXFLAGS=-O3 -fPIC $(INC)
LD=$(CXX)
#include头文件路径也是图省力

PROTOBUF_DIR=../../build/external.protobuf
PROTOBUF_LIB=../../external/lib/libprotobuf-mutator-libfuzzer.a ../../external/lib/libprotobuf-mutator.a $(PROTOBUF_DIR)/lib/libprotobuf.a
#相应的库路径,为了图省力一下子全写进来了

all: $(TARGET).so

$(TARGET).so: $(TARGET).cc test.pb.cc
$(CXX) -fPIC -c $^ $(INC)
$(CXX) -shared -Wall -O3 -o $@ *.o $(PROTOBUF_LIB)

test.pb.cc test.pb.h: test.proto
$(PROTOC) --cpp_out=. $<

vuln: vuln.c
$(AFLCC) -o $@ $<

.PHONY: clean
clean:
rm *.pb.*
rm *.o
rm $(TARGET).so
rm vuln

run_fuzz.sh:

1
2
3
4
5
#!/bin/sh
export AFL_CUSTOM_MUTATOR_ONLY=1
export AFL_CUSTOM_MUTATOR_LIBRARY=/home/ubuntu/afl-libprotobuf-mutator/practicing/4_libprotobuf_aflpp_custom_mutator/lpm_aflpp_custom_mutator.so

afl-fuzz -i ./in -o ./out ./vuln

然后说个坑点吧,AFL_CUSTOM_MUTATOR_LIBRARY的设置最好用绝对路径

fuzz之后查看crash与.cur_input,发现与预期相符

练习5:libprotobuf_aflpp_custom_mutator_input

这一练习貌似是侧重于Mutator类的编写了

写了一个继承Mutator类的MyMutator,然后还注册了一个变异函数

如下:

1
2
3
4
5
6
7
#include "libprotobuf-mutator/src/mutator.h"
#include "test.pb.h"

#include <bits/stdc++.h>

class MyMutator : public protobuf_mutator::Mutator {
};

然后就是最关键的lpm_aflpp_custom_mutator_input.cc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include "lpm_aflpp_custom_mutator_input.h"

using std::cin;
using std::cout;
using std::endl;

std::string ProtoToData(const TEST &test_proto) {
std::stringstream all;
const auto &aa = test_proto.a();
const auto &bb = test_proto.b();
all.write((const char*)&aa, sizeof(aa));
if(bb.size() != 0) {
all.write(bb.c_str(), bb.size());
}

std::string res = all.str();
if (bb.size() != 0 && res.size() != 0) {
// set PROTO_FUZZER_DUMP_PATH env to dump the serialized protobuf
if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
std::ofstream of(dump_path);
of.write(res.data(), res.size());
}
}
return res;
}

/**
* Initialize this custom mutator
*
* @param[in] afl a pointer to the internal state object. Can be ignored for
* now.
* @param[in] seed A seed for this mutator - the same seed should always mutate
* in the same way.
* @return Pointer to the data object this custom mutator instance should use.
* There may be multiple instances of this mutator in one afl-fuzz run!
* Return NULL on error.
*/
extern "C" MyMutator *afl_custom_init(void *afl, unsigned int seed) {
MyMutator *mutator = new MyMutator();

mutator->RegisterPostProcessor(
TEST::descriptor(),
[](google::protobuf::Message* message, unsigned int seed) {
// libprotobuf-mutator's built-in mutator is kind of....crappy :P
// Even a dumb fuzz like `TEST.a = rand();` is better in this case... Q_Q
// We register a post processor to apply our dumb fuzz

TEST *t = static_cast<TEST *>(message);
t->set_a(rand());
});

srand(seed);
return mutator;
}

/**
* Perform custom mutations on a given input
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[in] buf Pointer to input data to be mutated
* @param[in] buf_size Size of input data
* @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on
* error.
* @param[in] add_buf Buffer containing the additional test case
* @param[in] add_buf_size Size of the additional test case
* @param[in] max_size Maximum size of the mutated output. The mutation must not
* produce data larger than max_size.
* @return Size of the mutated output.
*/
extern "C" size_t afl_custom_fuzz(MyMutator *mutator, // return value from afl_custom_init
uint8_t *buf, size_t buf_size, // input data to be mutated
uint8_t **out_buf, // output buffer
uint8_t *add_buf, size_t add_buf_size, // add_buf can be NULL
size_t max_size) {
// This function can be named either "afl_custom_fuzz" or "afl_custom_mutator"
// A simple test shows that "buf" will be the content of the current test case
// "add_buf" will be the next test case ( from AFL++'s input queue )

TEST input;
// parse input data to TEST
// Notice that input data should be a serialized protobuf data
// Check ./in/ii and test_protobuf_serializer for more detail
bool parse_ok = input.ParseFromArray(buf, buf_size);
if(!parse_ok) {
// Invalid serialize protobuf data. Don't mutate.
// Return a dummy buffer. Also mutated_size = 0
static uint8_t *dummy = new uint8_t[10]; // dummy buffer with no data
*out_buf = dummy;
return 0;
}
// mutate the protobuf
mutator->Mutate(&input, max_size);

// Convert protobuf to raw data
const TEST *p = &input;
std::string s = ProtoToData(*p);
// Copy to a new buffer ( mutated_out )
size_t mutated_size = s.size() <= max_size ? s.size() : max_size; // check if raw data's size is larger than max_size
uint8_t *mutated_out = new uint8_t[mutated_size+1];
memcpy(mutated_out, s.c_str(), mutated_size); // copy the mutated data
// Assign the mutated data and return mutated_size
*out_buf = mutated_out;
return mutated_size;
}

/**
* Deinitialize everything
*
* @param data The data ptr from afl_custom_init
*/
extern "C" void afl_custom_deinit(void *data) {
// Honestly I don't know what to do with this...
return;
}

注意这里跟前面一个不同在哪呢?

这里通过ParseFromArray从buf中读取数据,而前面一个是直接设置值

然后就是通过创建Mutator类来变异,变异策略就是afl_custom_init中实现的,将a设置为随机数

Makefile也与前面的类似,修改相应TARGET即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
TARGET=lpm_aflpp_custom_mutator_input
PROTOC=../../external/bin/protoc
CC=clang
CXX=clang++
AFLCC=afl-clang-fast
INC=-I../../ -I../../external/include -I../../external/include/libprotobuf-mutator
CXXFLAGS=-O3 -fPIC $(INC)
LD=$(CXX)
#include头文件路径也是图省力

PROTOBUF_DIR=../../build/external.protobuf
PROTOBUF_LIB=../../external/lib/libprotobuf-mutator-libfuzzer.a ../../external/lib/libprotobuf-mutator.a $(PROTOBUF_DIR)/lib/libprotobuf.a
#相应的库路径,为了图省力一下子全写进来了

all: $(TARGET).so

$(TARGET).so: $(TARGET).cc test.pb.cc
$(CXX) -fPIC -c $^ $(INC)
$(CXX) -shared -Wall -O3 -o $@ *.o $(PROTOBUF_LIB)

test.pb.cc test.pb.h: test.proto
$(PROTOC) --cpp_out=. $<

vuln: vuln.c
$(AFLCC) -o $@ $<

.PHONY: clean
clean:
rm *.pb.*
rm *.o
rm $(TARGET).so
rm vuln

由于这里是将数据读入buf提供给我们,所以我们的fuzz样本不能再那么写了,而应将其序列化

test_proto_serializer.cc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
* A simple program to serialize TEST protobuf
* This is useful for generating the input data for our cutomized mutator
*
* */

#include "test.pb.h"
#include <bits/stdc++.h>

using std::cin;
using std::cout;
using std::endl;

int main(int argc, char *argv[])
{
TEST t;
t.set_a(100);
t.set_b("aaaa");
std::string s;
t.SerializeToString(&s);

std::ofstream out("./in/ii");
out << s;
out.close();
return 0;
}