This commit is contained in:
zhuzhenjun
2023-10-09 15:23:31 +08:00
parent 1a559eba99
commit 56e3dec5f0
29 changed files with 433055 additions and 184 deletions

22
CMakeLists.txt.in Normal file
View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 2.8)
project(libosfp)
# 添加其他CMake配置
# 生成RPM包
set(CPACK_GENERATOR "RPM")
set(CPACK_PACKAGE_NAME "libosfp")
set(CPACK_PACKAGE_VENDOR "Geedge")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Libosfp is a C library for OS fingerprinting.")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_RELEASE 1)
set(CPACK_PACKAGE_CONTACT "zhuzhenjun@geedgenetworks.com")
set(CPACK_RPM_PACKAGE_AUTOREQPROV "no")
install(DIRECTORY @prefix@/include DESTINATION . USE_SOURCE_PERMISSIONS)
install(DIRECTORY @prefix@/lib DESTINATION /usr/lib64 USE_SOURCE_PERMISSIONS)
install(DIRECTORY @prefix@/bin DESTINATION . USE_SOURCE_PERMISSIONS)
install(FILES fp.json DESTINATION var/lib/libosfp)
include(CPack)

2288
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,2 @@
SUBDIRS = src example
SUBDIRS = src example test
ACLOCAL_AMFLAGS=-I m4

View File

@@ -1,83 +1,20 @@
# libosfp
Libosfp is a C libaray for OS fingerprinting.
Libosfp is a C library for OS fingerprinting.
## install
```
# osfp_example depends on libpcap
yum install -y libpcap-devel
# build and install to ./target
./autogen.sh; ./configure --prefix="$(pwd)/target"; make clean; make install
# build and install
./package.sh
yum install package/*.rpm
```
## run example
```
# load the fingerprint file ./fp.json and capture on eth0, filter tcp port 8888
./target/bin/osfp_example -f ./fp.json -i eth0 "tcp port 8888"
# outputs like this
# --------------------------- SYN
# Example ipv4 header detect: --------------------------
# Connection info: 114.64.231.114:57570 -> 172.21.0.10:8888
# Most likely os class: Windows
# Details:
# {
# "likely": {
# "name": "Windows",
# "score": 20
# },
# "detail": [{
# "name": "Windows",
# "score": 20
# }, {
# "name": "Linux",
# "score": 10
# }, {
# "name": "Mac OS",
# "score": 1
# }, {
# "name": "iOS",
# "score": 0
# }, {
# "name": "Android",
# "score": 7
# }]
# }
```
## sample
```
#include "stdio.h"
#include "osfp.h"
char iph[] = {
0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00,
0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08,
0x6a, 0xb9, 0x23, 0x6e
};
char tcph[] = {
0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60,
0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00,
0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec,
0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02
};
int main(int argc, char **argv)
{
const char *json_file_path = "./fp.json";
struct iphdr *l3_hdr = (struct iphdr *)iph;
struct tcphdr *l4_hdr = (struct tcphdr *)tcph;
size_t l4_hdr_len = sizeof(tcph);
struct osfp_db *db = osfp_db_new(json_file_path);
if (db) {
struct osfp_result *result = osfp_ipv4_identify(db, l3_hdr, l4_hdr, l4_hdr_len);
if (result) {
printf("likely os: %s\n", osfp_result_os_name_get(result));
printf("details: \n%s\n", osfp_result_score_detail_export(result));
osfp_db_free(db);
}
}
}
# load the fingerprint file and capture on eth0, filter tcp port 8888
osfp_example -f /usr/var/lib/libosfp/fp.json -i eth0 "tcp port 8888"
```

6
build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
./autogen.sh;
./configure --prefix=$(pwd)/target;
make -j
make install

View File

@@ -1,4 +1,4 @@
AC_INIT([libosfp],[0.0.4],[zhuzhenjun@geedgenetworks.com])
AC_INIT([libosfp],[0.0.5],[zhuzhenjun@geedgenetworks.com])
AM_INIT_AUTOMAKE([foreign])
#m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])])AM_SILENT_RULES([yes])
@@ -10,6 +10,10 @@ AS_IF([test "x$enable_debug" = xyes],
[CFLAGS="-DDEBUGLOG -ggdb3 -O0 -fsanitize=address -fno-omit-frame-pointer"],
[CFLAGS="-g -O2"])
AC_ARG_ENABLE([test], [AS_HELP_STRING([--enable-test], [enable test])], [enable_test=$enableval], [enable_test=yes])
AS_IF([test "x$enable_test" = xyes],
[CFLAGS="${CFLAGS} -DUNITTEST"])
AC_PROG_CC
AC_PROG_CPP
AC_PROG_INSTALL
@@ -18,5 +22,5 @@ AC_PROG_MAKE_SET
AC_PROG_LIBTOOL
AC_CONFIG_FILES([libosfp-config Makefile src/Makefile example/Makefile])
AC_CONFIG_FILES([CMakeLists.txt libosfp-config Makefile src/Makefile example/Makefile test/Makefile])
AC_OUTPUT

362639
data.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -442,21 +442,27 @@ void example_detect(struct osfp_db *osfp_db, Packet *p)
int ret;
char str_buf[1024];
//unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h);
struct iphdr *iph;
struct ipv6hdr *ip6h;
struct tcphdr *tcph;
unsigned int tcph_len;
struct osfp_result *result;
unsigned int os_class_flags = OSFP_OS_CLASS_FLAG_WINDOWS | OSFP_OS_CLASS_FLAG_LINUX | OSFP_OS_CLASS_FLAG_MAC_OS;
struct osfp_result *result = NULL;
printf("Example ipv4 header detect: --------------------------\n");
if (p->iph == NULL) {
goto exit;
}
iph = (struct iphdr *)p->iph;
ip6h = (struct ipv6hdr *)p->ip6h;
tcph = (struct tcphdr *)p->tcph;
tcph_len = tcph->doff << 2;
result = osfp_ipv4_identify(osfp_db, p->iph, tcph, tcph_len);
if (iph) {
result = osfp_ipv4_identify(osfp_db, iph, tcph, tcph_len);
} else if (ip6h) {
result = osfp_ipv6_identify(osfp_db, ip6h, tcph, tcph_len);
} else {
goto exit;
}
if (result == NULL) {
printf("osfp header match failed, erro: %s\n", "?");
goto exit;
@@ -468,9 +474,10 @@ void example_detect(struct osfp_db *osfp_db, Packet *p)
printf("Details:\n");
printf("%s\n", osfp_result_score_detail_export(result));
osfp_result_free(result);
exit:
if (result) {
osfp_result_free(result);
}
return;
}

View File

@@ -28,6 +28,7 @@ int main(int argc, char **argv)
if (result) {
printf("likely os: %s\n", osfp_result_os_name_get(result));
printf("details: \n%s\n", osfp_result_score_detail_export(result));
osfp_result_free(result);
osfp_db_free(db);
}
}

12
package.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
./autogen.sh;
./configure --prefix=$(pwd)/target;
make install;
rm -rf package
mkdir package
cd package
cmake ..
cpack
cd -

BIN
pcap/linux_ipv4_syn.pcap Normal file

Binary file not shown.

Binary file not shown.

BIN
pcap/unknown_ipv6_syn.pcap Normal file

Binary file not shown.

Binary file not shown.

BIN
pcap/windows_ipv4_syn.pcap Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -33,24 +33,18 @@ static struct osfp_result *osfp_result_build(struct osfp_os_class_score *os_clas
likely_score = tmp_score;
likely_os_class = i;
}
result->detail.scores[i] = tmp_score;
result->details[i].score = tmp_score;
sum_score += tmp_score;
}
if (sum_score) {
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
result->detail.possibility[i] = OSFP_PERCENTILE * result->detail.scores[i] / sum_score;
result->details[i].possibility = OSFP_PERCENTILE * result->details[i].score / sum_score;
}
}
result->likely_score = likely_score;
result->likely_os_class = likely_os_class;
result->likely_possibility = result->detail.possibility[likely_os_class];
if (likely_score < OSFP_LOWEST_SCORE_LIMIT) {
result->likely_os_class = OSFP_OS_CLASS_OTHERS;
result->likely_score = 0;
result->likely_possibility = 0;
likely_os_class = OSFP_OS_CLASS_OTHERS;
} else {
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
if (likely_os_class == i) {
@@ -62,15 +56,15 @@ static struct osfp_result *osfp_result_build(struct osfp_os_class_score *os_clas
} else if (likely_os_class == OSFP_OS_CLASS_MAC_OS && i == OSFP_OS_CLASS_IOS) {
continue;
} else {
result->likely_os_class = OSFP_OS_CLASS_UNKNOWN;
result->likely_score = 0;
result->likely_possibility = 0;
likely_os_class = OSFP_OS_CLASS_UNKNOWN;
break;
}
}
}
}
result->likely_os_class = likely_os_class;
return result;
exit:
return NULL;
@@ -95,6 +89,7 @@ const char *osfp_result_os_name_get(struct osfp_result *result)
char *osfp_result_score_detail_export(struct osfp_result *result)
{
int i;
char *result_str = NULL;
cJSON *root = NULL;
cJSON *array;
cJSON *os_score;
@@ -104,6 +99,7 @@ char *osfp_result_score_detail_export(struct osfp_result *result)
}
if (result->json_str != NULL) {
result_str = result->json_str;
goto exit;
}
@@ -115,37 +111,35 @@ char *osfp_result_score_detail_export(struct osfp_result *result)
os_score = cJSON_AddObjectToObject(root, "likely");
if (os_score) {
cJSON_AddStringToObject(os_score, "name", osfp_os_class_id_to_name(result->likely_os_class));
cJSON_AddNumberToObject(os_score, "score", result->likely_score);
//cJSON_AddNumberToObject(os_score, "possibility", result->likely_possibility);
cJSON_AddNumberToObject(os_score, "score", result->details[result->likely_os_class].score);
//cJSON_AddNumberToObject(os_score, "possibility", result->details[result->likely_os_class].possibility);
}
array = cJSON_AddArrayToObject(root, "detail");
array = cJSON_AddArrayToObject(root, "details");
if (array) {
for (i = OSFP_OS_CLASS_WINDOWS; i < OSFP_OS_CLASS_OTHERS; i++) {
os_score = cJSON_CreateObject();
if (os_score) {
cJSON_AddStringToObject(os_score, "name", osfp_os_class_id_to_name(i));
cJSON_AddNumberToObject(os_score, "score", result->detail.scores[i]);
//cJSON_AddNumberToObject(os_score, "possibility", result->detail.possibility[i]);
cJSON_AddNumberToObject(os_score, "score", result->details[i].score);
//cJSON_AddNumberToObject(os_score, "possibility", result->details[i].possibility);
cJSON_AddItemToArray(array, os_score);
}
}
}
result->json_str = malloc(OSFP_DEFAULT_RESULT_BUFLEN_MAX);
if (result->json_str == NULL) {
result_str = cJSON_Print(root);
if (result_str == NULL) {
goto exit;
}
if (!cJSON_PrintPreallocated(root, result->json_str, OSFP_DEFAULT_RESULT_BUFLEN_MAX, 1)) {
goto exit;
}
result->json_str = result_str;
exit:
if (root) {
cJSON_Delete(root);
}
return result->json_str;
return result_str;
}
void osfp_result_free(struct osfp_result *result)
@@ -220,6 +214,37 @@ exit:
return NULL;
}
struct osfp_result *osfp_json_identify(struct osfp_db *db, const char *json_str)
{
int ret = OSFP_EINVAL;
struct osfp_fingerprint fp;
struct osfp_os_class_score os_class_score;
struct osfp_result *result;
if (db == NULL) {
goto exit;
}
ret = osfp_fingerprint_from_json(&fp, json_str);
if (ret != 0) {
goto exit;
}
ret = osfp_score_db_score(db->score_db, 0, &fp, &os_class_score);
if (ret != 0) {
goto exit;
}
result = osfp_result_build(&os_class_score);
if (result == NULL) {
goto exit;
}
return result;
exit:
return NULL;
}
struct osfp_db *osfp_db_new(const char *db_json_file)
{
int ret;

View File

@@ -8,6 +8,9 @@
#include <linux/ipv6.h>
#include <linux/tcp.h>
/**
* @brief 定义操作系统类别的名称常量。
*/
#define OSFP_OS_CLASS_NAME_UNKNOWN "Unknown"
#define OSFP_OS_CLASS_NAME_WINDOWS "Windows"
#define OSFP_OS_CLASS_NAME_LINUX "Linux"
@@ -16,45 +19,112 @@
#define OSFP_OS_CLASS_NAME_ANDROID "Android"
#define OSFP_OS_CLASS_NAME_OTHERS "Others"
/**
* @brief 枚举表示不同的操作系统类别。
*/
enum osfp_os_class_id {
OSFP_OS_CLASS_UNKNOWN,
OSFP_OS_CLASS_WINDOWS,
OSFP_OS_CLASS_LINUX,
OSFP_OS_CLASS_MAC_OS,
OSFP_OS_CLASS_IOS,
OSFP_OS_CLASS_ANDROID,
OSFP_OS_CLASS_OTHERS,
OSFP_OS_CLASS_UNKNOWN, // 未知
OSFP_OS_CLASS_WINDOWS, // Windows
OSFP_OS_CLASS_LINUX, // Linux
OSFP_OS_CLASS_MAC_OS, // Mac OS
OSFP_OS_CLASS_IOS, // iOS
OSFP_OS_CLASS_ANDROID, // Android
OSFP_OS_CLASS_OTHERS, // 其他
OSFP_OS_CLASS_MAX,
};
struct osfp_os_result_detail {
unsigned int scores[OSFP_OS_CLASS_MAX];
unsigned int possibility[OSFP_OS_CLASS_MAX];
/**
* @brief 结构体用于 osfp_result 中的详细结果。
*/
struct osfp_result_detail {
unsigned int score; // 得分
unsigned int possibility; // 可能性
};
/**
* @brief 结构体用于表示操作系统识别结果。
*/
struct osfp_result {
char *json_str;
enum osfp_os_class_id likely_os_class;
unsigned int likely_score;
unsigned int likely_possibility;
struct osfp_os_result_detail detail;
char *json_str; // JSON 字符串
enum osfp_os_class_id likely_os_class; // 最可能的操作系统类别
struct osfp_result_detail details[OSFP_OS_CLASS_MAX]; // 详细结果数组
};
/**
* @brief 结构体用于表示操作系统指纹库。
*/
struct osfp_db {
char *db_json_path;
void *score_db;
char *db_json_path; // 操作系统指纹库 JSON 文件路径
void *score_db; // 分数数据库指针
};
/**
* @brief 创建一个新的操作系统指纹库。
*
* @param db_json_path 操作系统指纹库 JSON 文件的路径。
* @return 指向新创建的操作系统指纹库的指针。
*/
struct osfp_db *osfp_db_new(const char *db_json_path);
/**
* @brief 释放操作系统指纹库占用的内存。
*
* @param db 指向要释放的操作系统指纹库的指针。
*/
void osfp_db_free(struct osfp_db *db);
/**
* @brief 通过 IPv4 头部和 TCP 头部识别操作系统。
*
* @param db 操作系统指纹库。
* @param l3_hdr 指向 IPv4 头部的指针。
* @param l4_hdr 指向 TCP 头部的指针。
* @param l4_hdr_len TCP 头部的长度注意包含TCP选项部分
* @return 指向操作系统识别结果的指针。
*/
struct osfp_result *osfp_ipv4_identify(struct osfp_db *db, struct iphdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len);
/**
* @brief 通过 IPv6 头部和 TCP 头部识别操作系统。
*
* @param db 操作系统指纹库。
* @param l3_hdr 指向 IPv6 头部的指针。
* @param l4_hdr 指向 TCP 头部的指针。
* @param l4_hdr_len TCP 头部的长度注意包含TCP选项部分
* @return 指向操作系统识别结果的指针(注意:内存需要使用者释放)。
*/
struct osfp_result *osfp_ipv6_identify(struct osfp_db *db, struct ipv6hdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len);
/**
* @brief 通过 json 格式的指纹识别操作系统。
*
* @param db 操作系统指纹库。
* @param json_str 指纹字符串。
* @return 指向操作系统识别结果的指针(注意:内存需要使用者释放)。
*/
struct osfp_result *osfp_json_identify(struct osfp_db *db, const char *json_str);
/**
* @brief 获取操作系统识别结果的操作系统名称。
*
* @param result 操作系统识别结果。
* @return 指向操作系统名称的常量字符指针注意这块内存将由osfp_result_free释放
*/
const char *osfp_result_os_name_get(struct osfp_result *result);
/**
* @brief 导出操作系统识别结果的得分详情。
*
* @param result 操作系统识别结果。
* @return 指向得分详情字符串的指针(注意:内存需要使用者释放)。
*/
char *osfp_result_score_detail_export(struct osfp_result *result);
/**
* @brief 释放操作系统识别结果占用的内存。
*
* @param result 操作系统识别结果。
*/
void osfp_result_free(struct osfp_result *result);
#endif

View File

@@ -84,20 +84,6 @@
#define OSFP_TCP_OPT_TFO_MIN_LEN 4 /* kind, len, 2 bytes cookie: 4 */
#define OSFP_TCP_OPT_TFO_MAX_LEN 18 /* kind, len, 18 */
#define OSFP_WRITE_STRING_TO_BUF(ret, buf, size, off, ...) do { \
ret = snprintf((char *)buf + off, \
size - off, \
__VA_ARGS__); \
if (ret >= 0) { \
if ( (off + ret) >= size) { \
off = size - 1; \
} else { \
off += ret; \
} \
} \
} while (0)
static inline unsigned long long osfp_rdtsc(void)
{
union {

View File

@@ -40,7 +40,7 @@ struct osfp_fingerprint_field fp_fields[OSFP_FIELD_MAX] = {
{OSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS, 1, OSFP_FIELD_TYPE_UINT, 25, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_MSS, 1, OSFP_FIELD_TYPE_UINT, 150, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS, 1, OSFP_FIELD_TYPE_STRING, 400, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED, 1, OSFP_FIELD_TYPE_STRING, 250, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED, 0, OSFP_FIELD_TYPE_STRING, 250, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_OS, 0, OSFP_FIELD_TYPE_STRING, 0, NULL, 0},
};
@@ -134,6 +134,8 @@ int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsi
return 0;
}
strbuf[0] = 0;
root = cJSON_CreateObject();
if (root == NULL) {
return 0;
@@ -165,6 +167,61 @@ int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsi
return strlen(strbuf) + 1;
}
int osfp_fingerprint_from_json(struct osfp_fingerprint *fp, char *json_str)
{
int ret, i;
cJSON *root;
cJSON *field;
void *value_ptr;
unsigned int value_len;
if (fp == NULL || json_str == NULL) {
goto exit;
}
memset(fp, 0, sizeof(struct osfp_fingerprint));
root = cJSON_Parse(json_str);
if (root == NULL) {
osfp_log_error("parse json: '%s'\n", json_str);
goto exit;
}
for (i = 0; i < OSFP_FIELD_OS; i++) {
if (!fp_fields[i].enabled) {
continue;
}
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(i));
if (field == NULL) {
goto exit;
}
switch (field->type) {
case cJSON_Number:
value_ptr = (void *)&field->valueint;
value_len = sizeof(field->valueint);
osfp_fingerprint_setup_field(fp, i, value_ptr, value_len);
break;
case cJSON_String:
value_ptr = (void *)field->valuestring;
value_len = strlen(field->valuestring) + 1;
osfp_fingerprint_setup_field(fp, i, value_ptr, value_len);
break;
case cJSON_NULL:
//printf("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root));
break;
default:
goto exit;
}
}
return 0;
exit:
return ret;
}
unsigned int osfp_fingerprint_get_field_enabled(enum osfp_field_id field_id)
{
return fp_fields[field_id].enabled;
@@ -175,7 +232,7 @@ unsigned int osfp_fingerprint_get_field_importance(enum osfp_field_id field_id)
return fp_fields[field_id].importance;
}
char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id)
const char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id)
{
return fp_fields[field_id].name;
}
@@ -201,7 +258,7 @@ void osfp_fingerprint_setup_field(struct osfp_fingerprint *fp, enum osfp_field_i
}
}
void osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len, struct osfp_fingerprint *fp)
int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len, struct osfp_fingerprint *fp)
{
int ret,i;
@@ -218,6 +275,10 @@ void osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_le
unsigned int ordered_offset = 0;
unsigned int ordered_maxoffset = sizeof(options_ordered) - 1;
if (opt_data == NULL || opt_len == 0 || fp == NULL) {
goto exit;
}
tcp_opt_cnt = decode_tcp_options(tcp_opts, OSFP_TCP_OPTMAX, opt_data, opt_len);
for (i = 0; i < tcp_opt_cnt && offset < maxoffset && ordered_offset < ordered_maxoffset; i++) {
@@ -290,7 +351,9 @@ void osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_le
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_OPTIONS, options, strlen(options) + 1);
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_OPTIONS_ORDERED, options_ordered, strlen(options_ordered) + 1);
return;
return 0;
exit:
return -1;
}
int osfp_fingerprinting_tcp(struct tcphdr *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp)
@@ -406,27 +469,27 @@ exit:
}
#ifdef UNITTEST
int test_osfp_fingerprinting(void)
int test_osfp_fingerprinting_ipv4(void)
{
int ret;
char iph[] = {
0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00,
0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08,
0x6a, 0xb9, 0x23, 0x6e
0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00,
0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08,
0x6a, 0xb9, 0x23, 0x6e
};
char tcph[] = {
0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60,
0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00,
0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec,
0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02
0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60,
0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00,
0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec,
0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02
};
char str_buf[2048] = "";
const char *target_buf = "{\"ip_id\":1,\"ip_tos\":0,\"ip_total_length\":52,\"ip_ttl\":128,\"tcp_off\":32,\"tcp_timestamp\":null,\"tcp_timestamp_echo_reply\":null,\"tcp_window_scaling\":8,\"tcp_window_size\":8192,\"tcp_flags\":2,\"tcp_mss\":1260,\"tcp_options\":\"M1260,N,W8,N,N,S,\",\"tcp_options_ordered\":\"MNWNNS\",\"os\":\"OSFP_UNKNOWN\"}";
const char *target = "{\"ip_id\":1,\"ip_tos\":0,\"ip_total_length\":52,\"ip_ttl\":128,\"tcp_off\":32,\"tcp_timestamp\":null,\"tcp_timestamp_echo_reply\":null,\"tcp_window_scaling\":8,\"tcp_window_size\":8192,\"tcp_flags\":2,\"tcp_mss\":1260,\"tcp_options\":\"M1260,N,W8,N,N,S,\",\"tcp_options_ordered\":\"MNWNNS\",\"os\":\"OSFP_UNKNOWN\"}";
struct osfp_fingerprint fp = {0};
ret = osfp_fingerprinting(iph, tcph, &fp);
ret = osfp_fingerprinting(iph, tcph, 32, &fp, 4);
if (ret != 0) {
goto exit;
}
@@ -436,7 +499,87 @@ int test_osfp_fingerprinting(void)
goto exit;
}
if (0 != memcmp(str_buf, target_buf, strlen(target_buf))) {
if (0 != memcmp(str_buf, target, strlen(target))) {
goto exit;
}
return 0;
exit:
return ret;
}
int test_osfp_fingerprinting_ipv6(void)
{
int ret;
char iph[] = {
0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00,
0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08,
0x6a, 0xb9, 0x23, 0x6e
};
char tcph[] = {
0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60,
0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00,
0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec,
0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02
};
char str_buf[2048] = "";
const char *target = "{\"ip_id\":1,\"ip_tos\":0,\"ip_total_length\":52,\"ip_ttl\":128,\"tcp_off\":32,\"tcp_timestamp\":null,\"tcp_timestamp_echo_reply\":null,\"tcp_window_scaling\":8,\"tcp_window_size\":8192,\"tcp_flags\":2,\"tcp_mss\":1260,\"tcp_options\":\"M1260,N,W8,N,N,S,\",\"tcp_options_ordered\":\"MNWNNS\",\"os\":\"OSFP_UNKNOWN\"}";
struct osfp_fingerprint fp = {0};
ret = osfp_fingerprinting(iph, tcph, 32, &fp, 4);
if (ret != 0) {
goto exit;
}
ret = osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0);
if (ret <= 0) {
goto exit;
}
if (0 != memcmp(str_buf, target, strlen(target))) {
goto exit;
}
return 0;
exit:
return ret;
}
int test_osfp_fingerprinting_tcp_option(void)
{
int ret;
char tcp_opt[] = {
0x02, 0x04, 0x04, 0xec,0x01, 0x03, 0x03, 0x08,
0x01, 0x01, 0x04, 0x02
};
char str_buf[2048] = "";
const char *target_options = "M1260,N,W8,N,N,S,";
const char *target_options_ordered = "MNWNNS";
struct osfp_fingerprint fp = {0};
ret = osfp_fingerprinting_tcp_option((unsigned char *)tcp_opt, 12, &fp);
if (ret != 0) {
goto exit;
}
if (fp.fields[OSFP_FIELD_TCP_OPTIONS].value_len != strlen(target_options) + 1)
{
goto exit;
}
if (0 != memcmp(fp.fields[OSFP_FIELD_TCP_OPTIONS].value, target_options, strlen(target_options) + 1)) {
goto exit;
}
if (fp.fields[OSFP_FIELD_TCP_OPTIONS_ORDERED].value_len != strlen(target_options_ordered) + 1) {
goto exit;
}
if (0 != memcmp(fp.fields[OSFP_FIELD_TCP_OPTIONS_ORDERED].value, target_options_ordered, strlen(target_options_ordered) + 1)) {
goto exit;
}

View File

@@ -29,7 +29,7 @@ enum osfp_field_type {
};
struct osfp_fingerprint_field {
char *name;
const char *name;
unsigned int enabled;
unsigned int type;
unsigned int importance;
@@ -43,21 +43,24 @@ struct osfp_fingerprint {
unsigned int value_buffer_used;
};
char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id);
int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsigned int buf_len, unsigned int format);
void osfp_fingerprint_setup_field(struct osfp_fingerprint *fp, enum osfp_field_id field_id, void *value, unsigned int len);
const char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id);
unsigned int osfp_fingerprint_get_field_enabled(enum osfp_field_id field_id);
unsigned int osfp_fingerprint_get_field_importance(enum osfp_field_id field_id);
unsigned int osfp_fingerprint_get_field_type(enum osfp_field_id field_id);
int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsigned int buf_len, unsigned int format);
void osfp_fingerprint_setup_field(struct osfp_fingerprint *fp, enum osfp_field_id field_id, void *value, unsigned int len);
void osfp_fingerprinting_tcp_option(unsigned char *pkt, unsigned int pktlen, struct osfp_fingerprint *fp);
int osfp_fingerprinting_tcp_option(unsigned char *pkt, unsigned int pktlen, struct osfp_fingerprint *fp);
int osfp_fingerprinting_tcp(struct tcphdr *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp);
int osfp_fingerprinting_ipv4(struct iphdr *iph, struct osfp_fingerprint *fp);
int osfp_fingerprinting_ipv6(struct ipv6hdr *iph, struct osfp_fingerprint *fp);
int osfp_fingerprinting(unsigned char *iph, unsigned char *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp, unsigned int ip_version);
#ifdef UNITTEST
int test_osfp_fingerprinting(void);
int test_osfp_fingerprinting_ipv4(void);
int test_osfp_fingerprinting_ipv6(void);
int test_osfp_fingerprinting_tcp_option(void);
#endif
#endif

View File

@@ -192,15 +192,6 @@ void osfp_score_db_hash_destroy(void *data) {
}
}
struct osfp_os_class_score *osfp_score_db_filed_match(struct osfp_field_score_db *db, void *value, unsigned int len)
{
if (db == NULL || value == NULL || len == 0) {
return NULL;
}
return db->match(db->data, value, len);
}
char *osfp_score_db_read_file(char *fp_file)
{
int ret = -1;
@@ -343,7 +334,7 @@ exit:
int osfp_score_db_load(struct osfp_score_db *score_db, char *fp_file)
{
int ret = OSFP_EINVAL, i;
int ret = OSFP_EINVAL, i, count;
char *file_buffer;
cJSON *root = NULL;
cJSON *entry;
@@ -367,9 +358,9 @@ int osfp_score_db_load(struct osfp_score_db *score_db, char *fp_file)
goto exit;
}
score_db->entry_count = cJSON_GetArraySize(root);
count = cJSON_GetArraySize(root);
for (i = 0; i < score_db->entry_count; i++) {
for (i = 0; i < count; i++) {
entry = cJSON_GetArrayItem(root, i);
if (entry) {
ret = osfp_score_db_load_entry(score_db, entry);
@@ -377,7 +368,7 @@ int osfp_score_db_load(struct osfp_score_db *score_db, char *fp_file)
osfp_log_debug("json entry load failed.\n%s\n", cJSON_Print(entry));
continue;
}
}
}
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
@@ -437,7 +428,7 @@ int osfp_score_db_score(struct osfp_score_db *score_db, unsigned int flags, stru
continue;
}
os_class_score_matched = osfp_score_db_filed_match(field_score_db, field->value, field->value_len);
os_class_score_matched = field_score_db->match(field_score_db->data, field->value, field->value_len);
if (os_class_score_matched == NULL) {
continue;
}
@@ -482,7 +473,6 @@ void osfp_score_db_debug_print(struct osfp_score_db *score_db)
printf("field %s enabled: %u\n", osfp_fingerprint_get_field_name(i), score_db->field_score_dbs[i].enabled);
printf("field %s type: %u\n", osfp_fingerprint_get_field_name(i), score_db->field_score_dbs[i].type);
printf("field %s entry_count: %u\n", osfp_fingerprint_get_field_name(i), score_db->field_score_dbs[i].entry_count);
printf("field %s enabled: %u\n", osfp_fingerprint_get_field_name(i), score_db->field_score_dbs[i].data);
}
}
@@ -556,3 +546,77 @@ void osfp_score_db_destroy(struct osfp_score_db *score_db)
free(score_db);
}
}
#ifdef UNITTEST
int test_osfp_score_db(void)
{
int ret, i;
struct osfp_score_db *db;
const char *test_file_path = "./.fp.json";
const char *fingerprint_file_content = "["
" {"
" \"tcp_options\": \"M1432,S,T,N,W9,\","
" \"tcp_options_ordered\": \"MSTNW\","
" \"ip_total_length\": 60,"
" \"tcp_off\": 10,"
" \"tcp_window_scaling\": 9,"
" \"tcp_window_size\": 65535,"
" \"ip_ttl\": 64,"
" \"ip_id\": 1,"
" \"tcp_timestamp\": 1,"
" \"tcp_timestamp_echo_reply\": 0,"
" \"tcp_mss\": 1432,"
" \"tcp_flags\": 2,"
" \"ip_tos\": 0,"
" \"os\": \"Android\""
" }"
"]";
db = (void *)osfp_score_db_create();
if (db == NULL) {
goto exit;
}
FILE *fingerprint_file_ptr = fopen(test_file_path, "w");
if (fingerprint_file_ptr == NULL) {
goto exit;
}
fprintf(fingerprint_file_ptr, "%s", fingerprint_file_content);
fflush(fingerprint_file_ptr);
fclose(fingerprint_file_ptr);
ret = osfp_score_db_load(db, (char *)test_file_path);
remove(test_file_path);
if (ret != 0) {
goto exit;
}
if (db->entry_count != 1) {
goto exit;
}
if (db->os_class_entry_count[OSFP_OS_CLASS_ANDROID] != 1) {
goto exit;
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
if (db->field_score_dbs[i].enabled != osfp_fingerprint_get_field_enabled(i)) {
goto exit;
}
if (db->field_score_dbs[i].enabled && db->field_score_dbs[i].type != osfp_fingerprint_get_field_type(i)) {
goto exit;
}
if (db->field_score_dbs[i].enabled && db->field_score_dbs[i].entry_count != 1) {
goto exit;
}
}
osfp_score_db_destroy(db);
return 0;
exit:
return -1;
}
#endif

View File

@@ -28,6 +28,7 @@ struct osfp_score_db {
struct osfp_field_score_db field_score_dbs[OSFP_FIELD_MAX];
};
char *osfp_score_db_read_file(char *fp_file);
void osfp_score_db_debug_print(struct osfp_score_db *score_db);
int osfp_score_db_load(struct osfp_score_db *score_db, char *fp_file);
@@ -36,4 +37,8 @@ int osfp_score_db_score(struct osfp_score_db *score_db, unsigned int flags, stru
struct osfp_score_db *osfp_score_db_create(void);
void osfp_score_db_destroy(struct osfp_score_db *score_db);
#ifdef UNITTEST
int test_osfp_score_db(void);
#endif
#endif

11
test/Makefile.am Normal file
View File

@@ -0,0 +1,11 @@
bin_PROGRAMS = osfp_test
osfp_test_SOURCES = \
test.c
osfp_test_LDADD = \
../src/.libs/libosfp.la
osfp_test_CFLAGS = \
-I../src

11
test/README.md Normal file
View File

@@ -0,0 +1,11 @@
# test
```
./.libs/osfp_test -f ../fp.json -t ../data.json
```
# output
```
total 12505, failed 0, pass 9582, wrong 2923, other 107, unknown 37
miss rate: 24%
details in: ./osfp_test.log
```

228
test/osfp_test Executable file
View File

@@ -0,0 +1,228 @@
#! /bin/sh
# osfp_test - temporary wrapper script for .libs/osfp_test
# Generated by libtool (GNU libtool) 2.4.2
#
# The osfp_test program cannot be directly executed until all the libtool
# libraries that it depends on are installed.
#
# This wrapper script should never be moved out of the build directory.
# If it is, it will not operate correctly.
# Sed substitution that helps us do robust quoting. It backslashifies
# metacharacters that are still active within double-quoted strings.
sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
# Be Bourne compatible
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
emulate sh
NULLCMD=:
# Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
fi
BIN_SH=xpg4; export BIN_SH # for Tru64
DUALCASE=1; export DUALCASE # for MKS sh
# The HP-UX ksh and POSIX shell print the target directory to stdout
# if CDPATH is set.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
relink_command="(cd /root/geedge/libosfp/test; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/root/.cargo/bin:/usr/lib64/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin; export PATH; gcc -I../src -g -O2 -DUNITTEST -o \$progdir/\$file osfp_test-test.o ../src/.libs/libosfp.so -Wl,-rpath -Wl,/root/geedge/libosfp/src/.libs -Wl,-rpath -Wl,/root/geedge/libosfp/target/lib)"
# This environment variable determines our operation mode.
if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
# install mode needs the following variables:
generated_by_libtool_version='2.4.2'
notinst_deplibs=' ../src/.libs/libosfp.la'
else
# When we are sourced in execute mode, $file and $ECHO are already set.
if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
file="$0"
# A function that is used when there is no print builtin or printf.
func_fallback_echo ()
{
eval 'cat <<_LTECHO_EOF
$1
_LTECHO_EOF'
}
ECHO="printf %s\\n"
fi
# Very basic option parsing. These options are (a) specific to
# the libtool wrapper, (b) are identical between the wrapper
# /script/ and the wrapper /executable/ which is used only on
# windows platforms, and (c) all begin with the string --lt-
# (application programs are unlikely to have options which match
# this pattern).
#
# There are only two supported options: --lt-debug and
# --lt-dump-script. There is, deliberately, no --lt-help.
#
# The first argument to this parsing function should be the
# script's ../libtool value, followed by no.
lt_option_debug=
func_parse_lt_options ()
{
lt_script_arg0=$0
shift
for lt_opt
do
case "$lt_opt" in
--lt-debug) lt_option_debug=1 ;;
--lt-dump-script)
lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
cat "$lt_dump_D/$lt_dump_F"
exit 0
;;
--lt-*)
$ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
exit 1
;;
esac
done
# Print the debug banner immediately:
if test -n "$lt_option_debug"; then
echo "osfp_test:osfp_test:${LINENO}: libtool wrapper (GNU libtool) 2.4.2" 1>&2
fi
}
# Used when --lt-debug. Prints its arguments to stdout
# (redirection is the responsibility of the caller)
func_lt_dump_args ()
{
lt_dump_args_N=1;
for lt_arg
do
$ECHO "osfp_test:osfp_test:${LINENO}: newargv[$lt_dump_args_N]: $lt_arg"
lt_dump_args_N=`expr $lt_dump_args_N + 1`
done
}
# Core function for launching the target application
func_exec_program_core ()
{
if test -n "$lt_option_debug"; then
$ECHO "osfp_test:osfp_test:${LINENO}: newargv[0]: $progdir/$program" 1>&2
func_lt_dump_args ${1+"$@"} 1>&2
fi
exec "$progdir/$program" ${1+"$@"}
$ECHO "$0: cannot exec $program $*" 1>&2
exit 1
}
# A function to encapsulate launching the target application
# Strips options in the --lt-* namespace from $@ and
# launches target application with the remaining arguments.
func_exec_program ()
{
case " $* " in
*\ --lt-*)
for lt_wr_arg
do
case $lt_wr_arg in
--lt-*) ;;
*) set x "$@" "$lt_wr_arg"; shift;;
esac
shift
done ;;
esac
func_exec_program_core ${1+"$@"}
}
# Parse options
func_parse_lt_options "$0" ${1+"$@"}
# Find the directory that this script lives in.
thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
test "x$thisdir" = "x$file" && thisdir=.
# Follow symbolic links until we get to the real thisdir.
file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
while test -n "$file"; do
destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
# If there was a directory component, then change thisdir.
if test "x$destdir" != "x$file"; then
case "$destdir" in
[\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
*) thisdir="$thisdir/$destdir" ;;
esac
fi
file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
done
# Usually 'no', except on cygwin/mingw when embedded into
# the cwrapper.
WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
# special case for '.'
if test "$thisdir" = "."; then
thisdir=`pwd`
fi
# remove .libs from thisdir
case "$thisdir" in
*[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
.libs ) thisdir=. ;;
esac
fi
# Try to get the absolute directory name.
absdir=`cd "$thisdir" && pwd`
test -n "$absdir" && thisdir="$absdir"
program=lt-'osfp_test'
progdir="$thisdir/.libs"
if test ! -f "$progdir/$program" ||
{ file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /usr/bin/sed 1q`; \
test "X$file" != "X$progdir/$program"; }; then
file="$$-$program"
if test ! -d "$progdir"; then
mkdir "$progdir"
else
rm -f "$progdir/$file"
fi
# relink executable if necessary
if test -n "$relink_command"; then
if relink_command_output=`eval $relink_command 2>&1`; then :
else
printf %s\n "$relink_command_output" >&2
rm -f "$progdir/$file"
exit 1
fi
fi
mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null ||
{ rm -f "$progdir/$program";
mv -f "$progdir/$file" "$progdir/$program"; }
rm -f "$progdir/$file"
fi
if test -f "$progdir/$program"; then
if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
# Run the actual program with our arguments.
func_exec_program ${1+"$@"}
fi
else
# The program doesn't exist.
$ECHO "$0: error: \`$progdir/$program' does not exist" 1>&2
$ECHO "This script is just a wrapper for $program." 1>&2
$ECHO "See the libtool documentation for more information." 1>&2
exit 1
fi
fi

67229
test/osfp_test.log Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,16 +4,196 @@
#include <unistd.h>
#include <time.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <sys/socket.h>
#include <pcap.h>
#include "cJSON.h"
#include "osfp.h"
#include "osfp_fingerprint.h"
#include "osfp_score_db.h"
#include "osfp_log.h"
#include "osfp_common.h"
#define DATA_FILE_PATH "./data.json"
#define DB_FILE_PATH "./db.json"
#define TEST_FILE_PATH "./test.json"
#define LOG_FILE_PATH "./osfp_test.log"
unsigned char *data_file_path = DATA_FILE_PATH;
unsigned char *db_file_path;
unsigned char *test_file_path;
FILE *data_file_ptr;
FILE *db_file_ptr;
FILE *test_file_ptr;
FILE *log_file_ptr;
unsigned int debug_enable;
void test_data_prepare()
{
char *file_buffer;
if (test_file_path == NULL) {
file_buffer = osfp_score_db_read_file(data_file_path);
if (file_buffer == NULL) {
osfp_log_error("read file: '%s'\n", data_file_path);
exit(1);
}
cJSON *data;
data = cJSON_Parse(file_buffer);
if (data == NULL) {
exit(1);
}
cJSON *test;
test = cJSON_CreateArray();
unsigned int data_count = cJSON_GetArraySize(data);
unsigned int db_count = data_count * 8 / 10;
unsigned int test_count = data_count - db_count;
srand(time(0));
int i;
for (i = 0; i < test_count; i++) {
cJSON_AddItemToArray(test, cJSON_DetachItemFromArray(data, rand() % data_count));
data_count--;
}
db_file_ptr = fopen(DB_FILE_PATH, "w");
test_file_ptr = fopen(TEST_FILE_PATH, "w");
fprintf(db_file_ptr, "%s", cJSON_Print(data));
fprintf(test_file_ptr, "%s", cJSON_Print(test));
fflush(db_file_ptr);
fflush(test_file_ptr);
db_file_path = DB_FILE_PATH;
test_file_path = TEST_FILE_PATH;
}else{
db_file_path = data_file_path;
}
}
void test_miss_rate()
{
int i;
unsigned int other_count = 0;
unsigned int unknown_count = 0;
unsigned int identify_failed_count = 0;
unsigned int wrong_count = 0;
unsigned int verified_count = 0;
unsigned int fingerprint_count = 0;
char *file_buffer;
file_buffer = osfp_score_db_read_file(test_file_path);
if (file_buffer == NULL) {
osfp_log_error("read file: '%s'\n", test_file_path);
exit(1);
}
cJSON *root = cJSON_Parse(file_buffer);
if (root == NULL) {
exit(1);
}
fingerprint_count = cJSON_GetArraySize(root);
if (debug_enable) {
osfp_log_level_set(OSFP_LOG_LEVEL_DEBUG);
}
struct osfp_db *osfp_db = osfp_db_new(db_file_path);
if (osfp_db == NULL) {
printf("could not create osfp context. fingerprints file: %s\n", db_file_path);
exit(1);
}
//osfp_score_db_debug_print(osfp_db->score_db);
FILE *log_file_ptr = fopen(LOG_FILE_PATH, "w");
for (i = 0; i < fingerprint_count; i++) {
cJSON *entry = cJSON_GetArrayItem(root, i);
if (entry) {
cJSON *os_class_json = cJSON_GetObjectItem(entry, "os");
int os_class = osfp_os_class_name_to_id(os_class_json->valuestring);
const char *fp_str = cJSON_PrintUnformatted(entry);
struct osfp_result *result = osfp_json_identify(osfp_db, fp_str);
if (result == NULL) {
identify_failed_count++;
continue;
}
if (os_class == result->likely_os_class) {
verified_count++;
osfp_result_free(result);
continue;
}
wrong_count++;
if (result->likely_os_class == OSFP_OS_CLASS_OTHERS) {
other_count++;
}
if (result->likely_os_class == OSFP_OS_CLASS_UNKNOWN) {
unknown_count++;
}
fprintf(log_file_ptr, "expect: %s, result: %s\n", os_class_json->valuestring, osfp_result_os_name_get(result));
char *result_json = osfp_result_score_detail_export(result);
if (result_json) {
fprintf(log_file_ptr, "%s\n", result_json);
} else {
fprintf(log_file_ptr, "result detail error:%p\n", result);
}
fflush(log_file_ptr);
osfp_result_free(result);
}
}
printf("total %u, failed %u, pass %u, wrong %u, other %u, unknown %u\n",
fingerprint_count, identify_failed_count, verified_count, wrong_count, other_count, unknown_count);
printf("miss rate: %d%%\n", 100 - (verified_count * 100 / fingerprint_count));
printf("details in: %s\n", LOG_FILE_PATH);
osfp_db_free(osfp_db);
}
int main(int argc, char **argv)
{
int r;
while ((r = getopt(argc, argv, "+f:t:o:d")) != -1) {
switch(r) {
case 'f':
data_file_path = (unsigned char*)optarg;
break;
case 't':
test_file_path = (unsigned char*)optarg;
break;
case 'd':
debug_enable = 1;
break;
default:
break;
}
}
assert(0 == test_osfp_fingerprinting_ipv4());
assert(0 == test_osfp_fingerprinting_ipv6());
assert(0 == test_osfp_fingerprinting_tcp_option());
assert(0 == test_osfp_score_db());
test_data_prepare();
test_miss_rate();
return 0;
}