21 Commits

Author SHA1 Message Date
4ling.cn
af67397fc9 fix compile warning 2024-03-12 10:55:51 +00:00
4ling.cn
7a2d2b5020 update fp.json 2024-03-12 10:47:28 +00:00
zhuzhenjun
13721781f7 code: clean up 2023-10-26 13:48:41 +08:00
zhuzhenjun
c9247d713d code: fix memory leak 2023-10-25 10:41:32 +08:00
zhuzhenjun
8832411f29 ci: never test osfp_sample 2023-10-24 17:18:35 +08:00
zhuzhenjun
4cd2fdb881 code: clean up 2023-10-24 16:28:54 +08:00
zhuzhenjun
e80ae01d68 test: show prefilter stats 2023-10-23 14:57:38 +08:00
zhuzhenjun
50e455bf8e prefilter: fix fields buffered order 2023-10-23 14:52:21 +08:00
zhuzhenjun
7a5b2e9f07 score_db: support hash prefilter 2023-10-23 09:54:02 +08:00
zhuzhenjun
c0cfa2e61d compile: delete Makefile.am 2023-10-23 09:52:46 +08:00
zhuzhenjun
77a47deb92 example: print fingerprints 2023-10-23 09:52:21 +08:00
zhuzhenjun
1de666a56e test: print confusion matrix 2023-10-17 23:37:20 +08:00
zhuzhenjun
168f931da6 fingerprint/score: bring back tcp_options_ordered 2023-10-17 23:36:10 +08:00
zhuzhenjun
e62ff39d81 api: MESA_osfp_xx -> osfp_xx
header: MESA_osfp.h -> osfp.h
2023-10-13 14:08:55 +08:00
zhuzhenjun
cfaf003d4f package: rpm name MESA_osfp -> libosfp
library: libMESA_osfp -> libosfp
2023-10-13 10:24:25 +08:00
zhuzhenjun
8b8627b0d8 symbol: set public symbols prefix 2023-10-12 15:36:31 +08:00
zhuzhenjun
7e1849233b compile: migrate to cmake
ci: migrate to MESA ci template
2023-10-12 14:48:10 +08:00
zhuzhenjun
b7ee724244 profile: init profile stats 2023-10-11 15:08:32 +08:00
zhuzhenjun
9056ffb00f ci: init gitlab ci 2023-10-10 19:04:34 +08:00
zhuzhenjun
f6af0204eb src: linux/*.h -> netinet/*.h 2023-10-10 17:14:34 +08:00
zhuzhenjun
f203d15fae example: fix sample.c 2023-10-10 10:55:49 +08:00
47 changed files with 3075 additions and 68246 deletions

4
.gitignore vendored
View File

@@ -21,3 +21,7 @@ missing
compile
libosfp-config
osfp_match
osfp_example
osfp_test
sample
osfp_test.log

257
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,257 @@
variables:
GIT_STRATEGY: "clone"
BUILD_IMAGE_CENTOS7: "git.mesalab.cn:7443/mesa_platform/build-env:master"
BUILD_IMAGE_CENTOS8: "git.mesalab.cn:7443/mesa_platform/build-env:rockylinux"
BUILD_PADDING_PREFIX: /tmp/padding_for_CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX/
INSTALL_DEPENDENCY_LIBRARY: libasan libpcap-devel
stages:
- build
- test
.build_before_script:
before_script:
- mkdir -p $BUILD_PADDING_PREFIX/$CI_PROJECT_NAMESPACE/
- ln -s $CI_PROJECT_DIR $BUILD_PADDING_PREFIX/$CI_PROJECT_PATH
- cd $BUILD_PADDING_PREFIX/$CI_PROJECT_PATH
- chmod +x ./ci/travis.sh
- yum makecache
- yum install -y elfutils-libelf-devel
.build_by_travis_for_centos7:
stage: build
image: $BUILD_IMAGE_CENTOS7
extends: .build_before_script
script:
- yum install -y libmnl-devel
- yum install -y libnfnetlink-devel
- ./ci/travis.sh
- cd build
tags:
- share
.build_by_travis_for_centos8:
stage: build
image: $BUILD_IMAGE_CENTOS8
extends: .build_before_script
script:
- dnf --enablerepo=powertools install -y libmnl-devel
- dnf --enablerepo=powertools install -y libnfnetlink-devel
- ./ci/travis.sh
tags:
- share
run_test_for_centos7:
stage: test
image: $BUILD_IMAGE_CENTOS7
extends: .build_by_travis_for_centos7
script:
- yum makecache
- ./ci/travis.sh
- cd build
- ctest --verbose
run_test_for_centos8:
stage: test
image: $BUILD_IMAGE_CENTOS8
extends: .build_by_travis_for_centos8
script:
- yum makecache
- ./ci/travis.sh
- cd build
- ctest --verbose
branch_build_debug_for_centos7:
stage: build
extends: .build_by_travis_for_centos7
variables:
BUILD_TYPE: Debug
except:
- /^develop.*$/i
- /^master.*$/i
- /^dev*.*$/i
- /^rel*.*$/i
- tags
branch_build_release_for_centos7:
stage: build
variables:
BUILD_TYPE: RelWithDebInfo
extends: .build_by_travis_for_centos7
except:
- /^develop.*$/i
- /^master.*$/i
- /^dev*.*$/i
- /^rel*.*$/i
- tags
develop_build_debug_for_centos7:
stage: build
extends: .build_by_travis_for_centos7
variables:
BUILD_TYPE: Debug
PACKAGE: 1
UPLOAD_RPM: 1
ASAN_OPTION: ADDRESS
TESTING_VERSION_BUILD: 1
PULP3_REPO_NAME: framework-testing-x86_64.el7
PULP3_DIST_NAME: framework-testing-x86_64.el7
artifacts:
name: "libosfp-$CI_COMMIT_REF_NAME-debug"
paths:
- build/*.rpm
only:
- /^develop.*$/i
- /^master.*$/i
- /^dev*.*$/i
- /^rel*.*$/i
develop_build_release_for_centos7:
stage: build
extends: .build_by_travis_for_centos7
variables:
BUILD_TYPE: RelWithDebInfo
PACKAGE: 1
UPLOAD_RPM: 1
TESTING_VERSION_BUILD: 1
PULP3_REPO_NAME: framework-testing-x86_64.el7
PULP3_DIST_NAME: framework-testing-x86_64.el7
artifacts:
name: "libosfp-$CI_COMMIT_REF_NAME-release"
paths:
- build/*.rpm
only:
- /^develop.*$/i
- /^master.*$/i
- /^dev*.*$/i
- /^rel*.*$/i
release_build_debug_for_centos7:
stage: build
variables:
BUILD_TYPE: Debug
PACKAGE: 1
UPLOAD_RPM: 1
PULP3_REPO_NAME: framework-stable-x86_64.el7
PULP3_DIST_NAME: framework-stable-x86_64.el7
extends: .build_by_travis_for_centos7
artifacts:
name: "libosfp-$CI_COMMIT_REF_NAME-debug"
paths:
- build/*.rpm
only:
- tags
release_build_release_for_centos7:
stage: build
variables:
BUILD_TYPE: RelWithDebInfo
PACKAGE: 1
UPLOAD_RPM: 1
PULP3_REPO_NAME: framework-stable-x86_64.el7
PULP3_DIST_NAME: framework-stable-x86_64.el7
extends: .build_by_travis_for_centos7
artifacts:
name: "libosfp-$CI_COMMIT_REF_NAME-release"
paths:
- build/*.rpm
only:
- tags
branch_build_debug_for_centos8:
stage: build
extends: .build_by_travis_for_centos8
variables:
BUILD_TYPE: Debug
except:
- /^develop.*$/i
- /^master.*$/i
- /^dev*.*$/i
- /^rel*.*$/i
- tags
branch_build_release_for_centos8:
stage: build
variables:
BUILD_TYPE: RelWithDebInfo
extends: .build_by_travis_for_centos8
except:
- /^develop.*$/i
- /^master.*$/i
- /^dev*.*$/i
- /^rel*.*$/i
- tags
develop_build_debug_for_centos8:
stage: build
extends: .build_by_travis_for_centos8
variables:
BUILD_TYPE: Debug
PACKAGE: 1
UPLOAD_RPM: 1
ASAN_OPTION: ADDRESS
TESTING_VERSION_BUILD: 1
PULP3_REPO_NAME: framework-testing-x86_64.el8
PULP3_DIST_NAME: framework-testing-x86_64.el8
artifacts:
name: "libosfp-$CI_COMMIT_REF_NAME-debug"
paths:
- build/*.rpm
only:
- /^develop.*$/i
- /^master.*$/i
- /^dev*.*$/i
- /^rel*.*$/i
develop_build_release_for_centos8:
stage: build
extends: .build_by_travis_for_centos8
variables:
BUILD_TYPE: RelWithDebInfo
PACKAGE: 1
UPLOAD_RPM: 1
TESTING_VERSION_BUILD: 1
PULP3_REPO_NAME: framework-testing-x86_64.el8
PULP3_DIST_NAME: framework-testing-x86_64.el8
artifacts:
name: "libosfp-$CI_COMMIT_REF_NAME-release"
paths:
- build/*.rpm
only:
- /^develop.*$/i
- /^master.*$/i
- /^dev*.*$/i
- /^rel*.*$/i
release_build_debug_for_centos8:
stage: build
variables:
BUILD_TYPE: Debug
PACKAGE: 1
UPLOAD_RPM: 1
PULP3_REPO_NAME: framework-stable-x86_64.el8
PULP3_DIST_NAME: framework-stable-x86_64.el8
extends: .build_by_travis_for_centos8
artifacts:
name: "libosfp-$CI_COMMIT_REF_NAME-debug"
paths:
- build/*.rpm
only:
- tags
release_build_release_for_centos8:
stage: build
variables:
BUILD_TYPE: RelWithDebInfo
PACKAGE: 1
UPLOAD_RPM: 1
PULP3_REPO_NAME: framework-stable-x86_64.el8
PULP3_DIST_NAME: framework-stable-x86_64.el8
extends: .build_by_travis_for_centos8
artifacts:
name: "libosfp-$CI_COMMIT_REF_NAME-release"
paths:
- build/*.rpm
only:
- tags

81
CMakeLists.txt Normal file
View File

@@ -0,0 +1,81 @@
cmake_minimum_required (VERSION 2.8)
set(lib_name osfp)
project (${lib_name})
set(LIB_MAJOR_VERSION 1)
set(LIB_MINOR_VERSION 3)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
include(Version)
set(CMAKE_MACOSX_RPATH 0)
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -Wall)
set(CMAKE_INSTALL_PREFIX /opt/MESA)
#for ASAN
option(ENABLE_SANITIZE_ADDRESS "Enable AddressSanitizer" FALSE)
option(ENABLE_SANITIZE_THREAD "Enable ThreadSanitizer" FALSE)
if(ENABLE_SANITIZE_ADDRESS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
elseif(ENABLE_SANITIZE_THREAD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fno-omit-frame-pointer")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
endif()
# end of for ASAN
include_directories(${PROJECT_SOURCE_DIR}/third_party/cJSON/)
include_directories(${PROJECT_SOURCE_DIR}/third_party/uthash/)
include_directories(${PROJECT_SOURCE_DIR}/src/)
aux_source_directory(${PROJECT_SOURCE_DIR}/third_party/cJSON/ SRCLIST)
aux_source_directory(${PROJECT_SOURCE_DIR}/src SRCLIST)
# Shared Library Output
add_library(${lib_name}_shared SHARED ${SRCLIST})
set_target_properties(${lib_name}_shared PROPERTIES LINK_FLAGS
"-Wl,--version-script=${PROJECT_SOURCE_DIR}/src/version.map")
if(DEFINED MESA_SHARED_INSTALL_DIR)
set_target_properties(${lib_name}_shared PROPERTIES OUTPUT_NAME ${lib_name} LIBRARY_OUTPUT_DIRECTORY ${MESA_SHARED_INSTALL_DIR})
else()
set_target_properties(${lib_name}_shared PROPERTIES OUTPUT_NAME ${lib_name})
endif()
set_target_properties(${lib_name}_shared PROPERTIES VERSION ${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION})
set_target_properties(${lib_name}_shared PROPERTIES SOVERSION ${LIB_MAJOR_VERSION})
# static Library Output
add_library(${lib_name}_static STATIC ${SRCLIST})
set_target_properties(${lib_name}_static PROPERTIES OUTPUT_NAME ${lib_name})
set(CMAKE_INSTALL_PREFIX /opt/MESA)
install(FILES src/osfp.h DESTINATION
${CMAKE_INSTALL_PREFIX}/include/MESA COMPONENT devel)
install(TARGETS ${lib_name}_shared LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib COMPONENT LIBRARIES)
install(FILES src/osfp.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/MESA COMPONENT HEADER)
install(FILES fp.json DESTINATION /var/lib/libosfp COMPONENT PROFILE)
add_executable(${lib_name}_sample example/sample.c)
target_link_libraries(${lib_name}_sample ${lib_name}_shared)
add_executable(${lib_name}_example example/osfp_example.c)
target_link_libraries(${lib_name}_example ${lib_name}_static)
target_link_libraries(${lib_name}_example pcap)
add_executable(${lib_name}_test test/test.c)
target_link_libraries(${lib_name}_test ${lib_name}_static)
enable_testing()
add_test(NAME test COMMAND ${lib_name}_test -f ../fp.json -t ../data.json)
include(Package)

View File

@@ -1,22 +0,0 @@
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 ./lib64 USE_SOURCE_PERMISSIONS FILES_MATCHING PATTERN "libosfp*")
install(DIRECTORY @prefix@/bin DESTINATION . USE_SOURCE_PERMISSIONS FILES_MATCHING PATTERN "osfp*")
install(FILES fp.json DESTINATION /var/lib/libosfp)
include(CPack)

View File

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

View File

@@ -7,20 +7,19 @@ Libosfp is a C library for OS fingerprinting.
```
# osfp_example depends on libpcap
yum install -y libpcap-devel
# build and install
./build.sh
./package.sh
yum install package/*.rpm
BUILD_TYPE=Debug PACKAGE=1 ./ci/travis.sh
yum install -y ./build/*.rpm
```
## library usage
```
gcc example/sample.c -o sample -losfp; cat example/sample.c
gcc -I./src example/sample.c -o sample -L./build -losfp; cat example/sample.c
LD_LIBRARY_PATH=${PWD}/build ./sample
```
## run example
```
# load the fingerprint file and capture on eth0, filter tcp port 8888
osfp_example -f /var/lib/libosfp/fp.json -i eth0 "tcp port 8888"
./build/osfp_example -f /var/lib/libosfp/fp.json -i eth0 "tcp port 8888"
```

View File

@@ -1,14 +0,0 @@
#!/bin/sh
# Run this to generate all the initial makefiles, etc.
if which libtoolize > /dev/null; then
echo "Found libtoolize"
libtoolize -c
elif which glibtoolize > /dev/null; then
echo "Found glibtoolize"
glibtoolize -c
else
echo "Failed to find libtoolize or glibtoolize, please ensure it is installed and accessible via your PATH env variable"
exit 1
fi;
autoreconf -ifv || exit 1
echo "You can now run \"./configure\" and then \"make\"."

32
autorelease.sh Normal file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
if [ $# -lt 6 ] ; then
echo "USAGE: ./autorelease.sh [API_V4_URL] [PROJECT_URL]
[PROJECT_ID] [TOKEN]
[COMMIT_TAG] [JOB] [PROJECT_NAME]"
exit 1;
fi
CI_API_V4_URL=$1
CI_PROJECT_URL=$2
CI_PROJECT_ID=$3
CI_TOKEN=$4
CI_COMMIT_TAG=$5
ARTIFACTS_JOB=$6
CI_PROJECT_NAME=$7
res=`echo -e "curl --header \"PRIVATE-TOKEN: $CI_TOKEN\" $CI_API_V4_URL/projects/$CI_PROJECT_ID/releases/$CI_COMMIT_TAG -o /dev/null -s -w %{http_code}"| /bin/bash`
if [[ $res == "200" ]]; then
eval $(echo -e "curl --request POST --header \"PRIVATE-TOKEN: $CI_TOKEN\" \
--data name=\"$CI_PROJECT_NAME-$CI_COMMIT_TAG-artifacts.zip\" \
--data url=\"$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_TAG/download?job=$ARTIFACTS_JOB\"\
$CI_API_V4_URL/projects/$CI_PROJECT_ID/releases/$CI_COMMIT_TAG/assets/links")
else
eval $(echo -e "curl --header 'Content-Type: application/json' --header \
\"PRIVATE-TOKEN: $CI_TOKEN\" --data '{ \"name\": \"$CI_COMMIT_TAG\", \
\"tag_name\": \"$CI_COMMIT_TAG\", \"description\": \"auto_release\",\
\"assets\": { \"links\": [{ \"name\": \
\"$CI_PROJECT_NAME-$CI_COMMIT_TAG-artifacts.zip\", \"url\": \
\"$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_TAG/download?job=$ARTIFACTS_JOB\"\
}] } }' --request POST $CI_API_V4_URL/projects/$CI_PROJECT_ID/releases/")
fi

1268
autorevision.sh Normal file

File diff suppressed because it is too large Load Diff

View File

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

48
ci/get-nprocessors.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Copyright 2017 Google Inc.
# All Rights Reserved.
#
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This file is typically sourced by another script.
# if possible, ask for the precise number of processors,
# otherwise take 2 processors as reasonable default; see
# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
if [ -x /usr/bin/getconf ]; then
NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
else
NPROCESSORS=2
fi
# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
# crashes if parallelized too much (maybe memory consumption problem),
# so limit to 4 processors for the time being.
if [ $NPROCESSORS -gt 4 ] ; then
echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
NPROCESSORS=4
fi

3
ci/perpare_pulp3_netrc.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env sh
set -evx
echo "machine ${PULP3_SERVER_URL}\nlogin ${PULP3_SERVER_LOGIN}\npassword ${PULP3_SERVER_PASSWORD}\n" > ~/.netrc

71
ci/travis.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/usr/bin/env sh
set -evx
chmod +x ci/get-nprocessors.sh
. ci/get-nprocessors.sh
# if possible, ask for the precise number of processors,
# otherwise take 2 processors as reasonable default; see
# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
if [ -x /usr/bin/getconf ]; then
NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
else
NPROCESSORS=2
fi
# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
# crashes if parallelized too much (maybe memory consumption problem),
# so limit to 4 processors for the time being.
if [ $NPROCESSORS -gt 4 ] ; then
echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
NPROCESSORS=4
fi
# Tell make to use the processors. No preceding '-' required.
MAKEFLAGS="j${NPROCESSORS}"
export MAKEFLAGS
env | sort
# Set default values to OFF for these variables if not specified.
: "${NO_EXCEPTION:=OFF}"
: "${NO_RTTI:=OFF}"
: "${COMPILER_IS_GNUCXX:=OFF}"
# Install dependency from YUM
if [ -n "${INSTALL_DEPENDENCY_LIBRARY}" ]; then
yum install -y $INSTALL_DEPENDENCY_LIBRARY
fi
if [ $ASAN_OPTION ] && [ -f "/opt/rh/devtoolset-7/enable" ] ;then
source /opt/rh/devtoolset-7/enable
fi
mkdir build || true
cd build
cmake3 -DCMAKE_CXX_FLAGS=$CXX_FLAGS \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \
-DENABLE_DEVEL=$ENABLE_DEVEL_SWITCH \
-DASAN_OPTION=$ASAN_OPTION \
-DVERSION_DAILY_BUILD=$TESTING_VERSION_BUILD \
..
make
if [ -n "${PACKAGE}" ]; then
make package
fi
if [ -n "${UPLOAD_RPM}" ]; then
cp ~/rpm_upload_tools.py ./
python3 rpm_upload_tools.py ${PULP3_REPO_NAME} ${PULP3_DIST_NAME} *.rpm
fi
if [ -n "${UPLOAD_SYMBOL_FILES}" ]; then
rpm -i $SYMBOL_TARGET*debuginfo*.rpm
_symbol_file=`find /usr/lib/debug/ -name "$SYMBOL_TARGET*.so*.debug"`
cp $_symbol_file ${_symbol_file}info.${CI_COMMIT_SHORT_SHA}
sentry-cli upload-dif -t elf ${_symbol_file}info.${CI_COMMIT_SHORT_SHA}
fi

57
cmake/Package.cmake Normal file
View File

@@ -0,0 +1,57 @@
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(MY_RPM_NAME_PREFIX "lib${lib_name}-debug")
else()
set(MY_RPM_NAME_PREFIX "lib${lib_name}")
endif()
message(STATUS "Package: ${MY_RPM_NAME_PREFIX}")
set(CPACK_PACKAGE_VECDOR "MESA")
set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}.${VERSION_BUILD}")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_BUILD}")
execute_process(COMMAND sh changelog.sh ${CMAKE_BINARY_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/cmake)
SET(CPACK_RPM_CHANGELOG_FILE ${CMAKE_BINARY_DIR}/changelog.txt)
# RPM Build
set(CPACK_GENERATOR "RPM")
set(CPACK_RPM_AUTO_GENERATED_FILE_NAME ON)
set(CPACK_RPM_FILE_NAME "RPM-DEFAULT")
set(CPACK_RPM_PACKAGE_VENDOR "MESA")
set(CPACK_RPM_PACKAGE_AUTOREQPROV "yes")
set(CPACK_RPM_PACKAGE_RELEASE_DIST "on")
set(CPACK_RPM_DEBUGINFO_PACKAGE "on")
set(CPACK_RPM_COMPONENT_INSTALL ON)
set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP)
set(CPACK_COMPONENT_HEADER_DISPLAY_NAME "develop")
set(CPACK_COMPONENT_LIBRARIES_REQUIRED TRUE)
set(CPACK_RPM_LIBRARIES_PACKAGE_NAME ${MY_RPM_NAME_PREFIX})
set(CPACK_COMPONENT_LIBRARIES_GROUP "LIBRARIES")
set(CPACK_COMPONENT_EXECUTABLE_GROUP "LIBRARIES")
set(CPACK_COMPONENT_PROFILE_GROUP "LIBRARIES")
set(CPACK_COMPONENT_HEADER_REQUIRED TRUE)
set(CPACK_RPM_HEADER_PACKAGE_NAME "${MY_RPM_NAME_PREFIX}-devel")
set(CPACK_COMPONENT_HEADER_GROUP "header")
set(CPACK_RPM_HEADER_PACKAGE_REQUIRES_PRE ${CPACK_RPM_LIBRARIES_PACKAGE_NAME})
set(CPACK_RPM_HEADER_PACKAGE_CONFLICTS ${CPACK_RPM_HEADER_PACKAGE_NAME})
set(CPACK_COMPONENTS_ALL LIBRARIES HEADER EXECUTABLE PROFILE)
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostInstall.in)
set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostInstall.in)
set(CPACK_BUILD_SOURCE_DIRS "${CMAKE_SOURCE_DIR}")
# Must uninstall the debug package before install release package
set(CPACK_RPM_PACKAGE_CONFLICTS ${MY_RPM_NAME_PREFIX})
# set(CPACK_STRIP_FILES TRUE)
include(CPack)

49
cmake/Version.cmake Normal file
View File

@@ -0,0 +1,49 @@
# Using autorevision.sh to generate version information
set(__SOURCE_AUTORESIVISION ${CMAKE_SOURCE_DIR}/autorevision.sh)
set(__AUTORESIVISION ${CMAKE_BINARY_DIR}/autorevision.sh)
set(__VERSION_CACHE ${CMAKE_BINARY_DIR}/version.txt)
set(__VERSION_CONFIG ${CMAKE_BINARY_DIR}/version.cmake)
file(COPY ${__SOURCE_AUTORESIVISION} DESTINATION ${CMAKE_BINARY_DIR}
FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
# execute autorevision.sh to generate version information
execute_process(COMMAND ${__AUTORESIVISION} -t cmake -o ${__VERSION_CACHE}
OUTPUT_FILE ${__VERSION_CONFIG} ERROR_QUIET)
include(${__VERSION_CONFIG})
# extract major, minor, patch version from git tag
string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" VERSION_MAJOR "${VCS_TAG}")
string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${VCS_TAG}")
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${VCS_TAG}")
if(NOT VERSION_MAJOR)
set(VERSION_MAJOR 1)
endif()
if(NOT VERSION_MINOR)
set(VERSION_MINOR 0)
endif()
if(NOT VERSION_PATCH)
set(VERSION_PATCH 0)
endif()
set(VERSION "${VERSION_MAJOR}_${VERSION_MINOR}_${VERSION_PATCH}")
set(VERSION_BUILD "${VCS_SHORT_HASH}")
# print information
message(STATUS "Version: ${VERSION}-${VERSION_BUILD}")
option(DEFINE_GIT_VERSION "Set DEFINE_GIT_VERSION to TRUE or FALSE" TRUE)
if(DEFINE_GIT_VERSION)
set(GIT_VERSION
"${VERSION}-${CMAKE_BUILD_TYPE}-${VERSION_BUILD}-${VCS_BRANCH}-${VCS_TAG}-${VCS_DATE}")
string(REGEX REPLACE "[-:+/\\.]" "_" GIT_VERSION ${GIT_VERSION})
add_definitions(-DGIT_VERSION=${GIT_VERSION})
endif()

4
cmake/changelog.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
work_path=$1
branch=`git status | grep branch | awk '{print $NF}'`
git log --branches=$branch --no-merges --date=local --show-signature --pretty="* %ad %an %ae %nhash: %H%ncommit:%n%B" | awk -F"-" '{print "- "$0}' | sed 's/- \*/\*/g' | sed 's/- $//g' | sed 's/-/ -/g' | sed 's/[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}//g' > $work_path/changelog.txt

View File

@@ -1,26 +0,0 @@
AC_INIT([libosfp],[1.0.0],[zhuzhenjun@geedgenetworks.com])
AM_INIT_AUTOMAKE([foreign])
#m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])])AM_SILENT_RULES([yes])
AC_CONFIG_MACRO_DIR([m4])
AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable debug info])], [enable_debug=$enableval], [enable_debug=no])
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
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_LIBTOOL
AC_CONFIG_FILES([CMakeLists.txt libosfp-config Makefile src/Makefile example/Makefile test/Makefile])
AC_OUTPUT

View File

@@ -1,14 +0,0 @@
bin_PROGRAMS = osfp_example
osfp_example_SOURCES = \
osfp_example.c
osfp_example_LDADD = \
../src/.libs/libosfp.la
osfp_example_LDFLAGS = \
-lpcap
osfp_example_CFLAGS = \
-I../src

View File

@@ -3,12 +3,7 @@
#include <string.h>
#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 <signal.h>
#include <sys/socket.h>
@@ -30,6 +25,7 @@
#define VLAN_MAX_LAYER 2
struct osfp_db *g_osfp_db;
/* Port is just a uint16_t */
typedef uint16_t Port;
@@ -52,14 +48,6 @@ typedef struct Address_ {
#define addr_data8 address.address_un_data8
#define addr_in6addr address.address_un_in6
#define COPY_ADDRESS(a, b) do { \
(b)->family = (a)->family; \
(b)->addr_data32[0] = (a)->addr_data32[0]; \
(b)->addr_data32[1] = (a)->addr_data32[1]; \
(b)->addr_data32[2] = (a)->addr_data32[2]; \
(b)->addr_data32[3] = (a)->addr_data32[3]; \
} while (0)
/* Set the IPv4 addresses into the Addrs of the Packet.
* Make sure p->ip4h is initialized and validated.
*
@@ -85,18 +73,18 @@ typedef struct Address_ {
* Make sure p->ip6h is initialized and validated. */
#define SET_IPV6_SRC_ADDR(p, a) do { \
(a)->family = AF_INET6; \
(a)->addr_data32[0] = (p)->ip6h->saddr.s6_addr32[0]; \
(a)->addr_data32[1] = (p)->ip6h->saddr.s6_addr32[1]; \
(a)->addr_data32[2] = (p)->ip6h->saddr.s6_addr32[2]; \
(a)->addr_data32[3] = (p)->ip6h->saddr.s6_addr32[3]; \
(a)->addr_data32[0] = (p)->ip6h->ip6_src.__in6_u.__u6_addr32[0]; \
(a)->addr_data32[1] = (p)->ip6h->ip6_src.__in6_u.__u6_addr32[1]; \
(a)->addr_data32[2] = (p)->ip6h->ip6_src.__in6_u.__u6_addr32[2]; \
(a)->addr_data32[3] = (p)->ip6h->ip6_src.__in6_u.__u6_addr32[3]; \
} while (0)
#define SET_IPV6_DST_ADDR(p, a) do { \
(a)->family = AF_INET6; \
(a)->addr_data32[0] = (p)->ip6h->daddr.s6_addr32[0]; \
(a)->addr_data32[1] = (p)->ip6h->daddr.s6_addr32[1]; \
(a)->addr_data32[2] = (p)->ip6h->daddr.s6_addr32[2]; \
(a)->addr_data32[3] = (p)->ip6h->daddr.s6_addr32[3]; \
(a)->addr_data32[0] = (p)->ip6h->ip6_dst.__in6_u.__u6_addr32[0]; \
(a)->addr_data32[1] = (p)->ip6h->ip6_dst.__in6_u.__u6_addr32[1]; \
(a)->addr_data32[2] = (p)->ip6h->ip6_dst.__in6_u.__u6_addr32[2]; \
(a)->addr_data32[3] = (p)->ip6h->ip6_dst.__in6_u.__u6_addr32[3]; \
} while (0)
#define TCP_GET_RAW_SRC_PORT(tcph) ntohs((tcph)->source)
@@ -131,7 +119,7 @@ typedef struct Address_ {
typedef struct Packet_ {
struct ethhdr *ethh;
struct iphdr *iph;
struct ipv6hdr *ip6h;
struct ip6_hdr *ip6h;
struct tcphdr *tcph;
char srcip[46];
@@ -159,6 +147,11 @@ typedef struct Packet_ {
int vlan_layer;
} Packet;
typedef struct EthernetHdr_ {
uint8_t eth_dst[6];
uint8_t eth_src[6];
uint16_t eth_type;
} __attribute__((__packed__)) EthernetHdr;
unsigned char *fp_file_path;
unsigned char *fp_output_file_path;
@@ -173,6 +166,12 @@ pcap_t *pcap_handle;
int processed_packet;
int link_type;
struct osfp_profile_counter identify_profile;
unsigned int identify_failed_count = 0;
unsigned int identify_count = 0;
unsigned int result_os_count[OSFP_OS_CLASS_MAX];
void usage(void) {
fprintf(stderr,
"Usage: osfp_match [ ...options... ] [ 'filter rule' ]\n"
@@ -186,13 +185,6 @@ void usage(void) {
exit(1);
}
typedef struct EthernetHdr_ {
uint8_t eth_dst[6];
uint8_t eth_src[6];
uint16_t eth_type;
} __attribute__((__packed__)) EthernetHdr;
int packet_decode_tcp(Packet *p, const unsigned char *data, unsigned int len)
{
int ret = -1;
@@ -268,15 +260,15 @@ int packet_decode_ipv6(Packet *p, const unsigned char *data, unsigned int len)
int ret = -1;
unsigned short ip6_payload_len;
unsigned char ip6_nexthdr;
struct ipv6hdr *ip6h;
struct ip6_hdr *ip6h;
if (len < IPV6_HEADER_LEN) {
goto exit;
}
ip6h = (struct ipv6hdr *)data;
ip6_payload_len = ntohs(ip6h->payload_len);
ip6_nexthdr = ip6h->nexthdr;
ip6h = (struct ip6_hdr *)data;
ip6_payload_len = ntohs(ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen);
ip6_nexthdr = ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt;
if (len < IPV6_HEADER_LEN + ip6_payload_len) {
goto exit;
@@ -440,21 +432,33 @@ const char *PrintInet(int af, const void *src, char *dst, socklen_t size)
void example_detect(struct osfp_db *osfp_db, Packet *p)
{
int ret;
char str_buf[1024];
char str_buf[2048] = "";
//unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h);
struct iphdr *iph;
struct ipv6hdr *ip6h;
struct ip6_hdr *ip6h;
struct tcphdr *tcph;
unsigned int tcph_len;
struct osfp_result *result = NULL;
printf("Example ipv4 header detect: --------------------------\n");
iph = (struct iphdr *)p->iph;
ip6h = (struct ipv6hdr *)p->ip6h;
ip6h = (struct ip6_hdr *)p->ip6h;
tcph = (struct tcphdr *)p->tcph;
tcph_len = tcph->doff << 2;
osfp_profile_cycle(c1);
osfp_profile_cycle(c2);
struct osfp_fingerprint fp = {0};
if (iph) {
osfp_fingerprinting((unsigned char*)iph, (unsigned char*)tcph, tcph_len, &fp, 4);
} else if (ip6h) {
osfp_fingerprinting((unsigned char*)iph, (unsigned char*)tcph, tcph_len, &fp, 6);
} else {
goto exit;
}
osfp_profile_get_cycle(c1);
if (iph) {
result = osfp_ipv4_identify(osfp_db, iph, tcph, tcph_len);
} else if (ip6h) {
@@ -462,17 +466,38 @@ void example_detect(struct osfp_db *osfp_db, Packet *p)
} else {
goto exit;
}
osfp_profile_get_cycle(c2);
osfp_profile_counter_update(&identify_profile, c2 - c1);
identify_count++;
if (result == NULL) {
identify_failed_count++;
printf("osfp header match failed, erro: %s\n", "?");
goto exit;
}
printf("Connection info: %s:%d -> %s:%d\n", p->srcip, p->sp, p->dstip, p->dp);
printf("Most likely os class: %s\n", osfp_result_os_name_get(result));
result_os_count[result->likely_os_class]++;
printf("Details:\n");
printf("%s\n", osfp_result_score_detail_export(result));
char *json = osfp_result_score_detail_export(result);
if (debug_enable) {
if (p->tcph->ack) {
printf("--------------------------- SYN/ACK\n");
} else {
printf("--------------------------- SYN\n");
}
osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0);
printf("%s\n", str_buf);
printf("Example ipv4 header detect: --------------------------\n");
printf("Connection info: %s:%d -> %s:%d\n", p->srcip, p->sp, p->dstip, p->dp);
printf("Most likely os class: %s\n", osfp_result_os_name_get(result));
printf("Details:\n");
printf("%s\n", json);
fflush(stdout);
}
free(json);
exit:
if (result) {
@@ -498,11 +523,6 @@ void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
goto exit;
}
if (p->tcph->ack) {
printf("--------------------------- SYN/ACK\n");
} else {
printf("--------------------------- SYN\n");
}
if (p->iph) {
PrintInet(AF_INET, (const void *)&(p->src.addr_data32[0]), p->srcip, sizeof(p->srcip));
@@ -513,7 +533,10 @@ void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
}
// tcp/ip header detect example for user
example_detect(osfp_db, p);
int i;
for (i = 0; i < 1000; i++) {
example_detect(osfp_db, p);
}
printf("--------------------------- processed packet count %d\n", ++processed_packet);
@@ -521,10 +544,39 @@ exit:
return;
}
static void signal_handler(int signum)
{
printf("profile identify: avg: %lu max: %lu min: %lu curr: %lu total: %lu count: %lu\n",
identify_profile.total_cycle / identify_profile.count,
identify_profile.max_cycle,
identify_profile.min_cycle,
identify_profile.curr_cycle,
identify_profile.total_cycle,
identify_profile.count);
osfp_profile_print_stats();
printf("total %u, failed %u\n",
identify_count, identify_failed_count);
int i;
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
printf("%s: %u\n", osfp_os_class_id_to_name(i), result_os_count[i]);
}
fflush(stdout);
osfp_db_free(g_osfp_db);
exit(0);
}
int main(int argc, char *argv[])
{
int r;
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
while ((r = getopt(argc, argv, "+f:i:r:o:d")) != -1) {
switch(r) {
case 'f':
@@ -635,13 +687,15 @@ int main(int argc, char *argv[])
osfp_log_level_set(OSFP_LOG_LEVEL_DEBUG);
}
osfp_profile_set(1);
struct osfp_db *osfp_db = osfp_db_new(fp_file_path);
if (osfp_db == NULL) {
printf("could not create osfp context. fingerprints file: %s\n", fp_file_path);
exit(1);
}
osfp_score_db_debug_print(osfp_db->score_db);
g_osfp_db = osfp_db;
// loop
while (1) {

View File

@@ -1,5 +1,6 @@
#include "stdio.h"
#include "libosfp/osfp.h"
#include <stdio.h>
#include <stdlib.h>
#include "osfp.h"
char iph[] = {
0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00,
@@ -17,6 +18,7 @@ char tcph[] = {
int main(int argc, char **argv)
{
const char *json_file_path = "./fp.json";
char *detail_json;
struct iphdr *l3_hdr = (struct iphdr *)iph;
struct tcphdr *l4_hdr = (struct tcphdr *)tcph;
@@ -27,9 +29,11 @@ int main(int argc, char **argv)
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));
detail_json = osfp_result_score_detail_export(result);
printf("details: \n%s\n", detail_json);
free(detail_json);
osfp_result_free(result);
osfp_db_free(db);
}
osfp_db_free(db);
}
}

18
fp.json
View File

@@ -72590,5 +72590,21 @@
"tcp_flags": 2,
"ip_tos": 0,
"os": "Linux"
},
{
"tcp_options": "M1460,N,W3,N,N,T0,S,E,E,",
"tcp_options_ordered": "MNWNNTSEE",
"ip_total_length": 64,
"tcp_off": 44,
"tcp_window_scaling": 3,
"tcp_window_size": 31744,
"ip_ttl": 64,
"ip_id": 0,
"tcp_timestamp": 1,
"tcp_timestamp_echo_reply": 0,
"tcp_mss": 1460,
"tcp_flags": 2,
"ip_tos": 0,
"os": "iOS"
}
]
]

View File

@@ -1,4 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@

View File

@@ -1,8 +0,0 @@
#!/bin/bash
rm -rf package
mkdir package
cd package
cmake ..
cpack
cd -

BIN
pcap/synmerge.pcap Normal file

Binary file not shown.

View File

@@ -1,23 +0,0 @@
lib_LTLIBRARIES = libosfp.la
libosfp_la_SOURCES = \
utarray.h \
uthash.h \
utlist.h \
utringbuffer.h \
utstack.h \
utstring.h \
cJSON.h \
cJSON.c \
osfp_common.h \
osfp_common.c \
osfp.h \
osfp.c \
osfp_fingerprint.h \
osfp_fingerprint.c \
osfp_log.h \
osfp_log.c \
osfp_score_db.h \
osfp_score_db.c
pkginclude_HEADERS = osfp.h osfp_fingerprint.h osfp_score_db.h osfp_common.h

View File

@@ -1,3 +1,8 @@
#include <sys/fcntl.h>
#include <unistd.h>
#include "cJSON.h"
#include "osfp_common.h"
#include "osfp.h"
@@ -5,71 +10,6 @@
#include "osfp_score_db.h"
#include "osfp_log.h"
#define OSFP_DEFAULT_RESULT_BUFLEN_MAX 512
#define OSFP_LOWEST_SCORE_LIMIT 20
static struct osfp_result *osfp_result_build(struct osfp_os_class_score *os_class_score)
{
int i;
unsigned int tmp_score;
unsigned int sum_score;
unsigned int likely_score;
enum osfp_os_class_id likely_os_class;
struct osfp_result *result;
result = calloc(1, sizeof(struct osfp_result));
if (result == NULL) {
goto exit;
}
likely_score = 0;
likely_os_class = OSFP_OS_CLASS_OTHERS;
sum_score = 0;
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
tmp_score = os_class_score->scores[i];
if (likely_score < tmp_score) {
likely_score = tmp_score;
likely_os_class = i;
}
result->details[i].score = tmp_score;
sum_score += tmp_score;
}
if (sum_score) {
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
result->details[i].possibility = OSFP_PERCENTILE * result->details[i].score / sum_score;
}
}
if (likely_score < OSFP_LOWEST_SCORE_LIMIT) {
likely_os_class = OSFP_OS_CLASS_OTHERS;
} else {
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
if (likely_os_class == i) {
continue;
}
if (likely_score == os_class_score->scores[i]) {
if (likely_os_class == OSFP_OS_CLASS_LINUX && i == OSFP_OS_CLASS_ANDROID) {
continue;
} else if (likely_os_class == OSFP_OS_CLASS_MAC_OS && i == OSFP_OS_CLASS_IOS) {
continue;
} else {
likely_os_class = OSFP_OS_CLASS_UNKNOWN;
break;
}
}
}
}
result->likely_os_class = likely_os_class;
return result;
exit:
return NULL;
}
const char *osfp_result_os_name_get(struct osfp_result *result)
{
enum osfp_os_class_id os_class;
@@ -89,20 +29,21 @@ 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;
char *result_str;
cJSON *root;
cJSON *array;
cJSON *os_score;
cJSON *matched;
osfp_profile_cycle(c1);
osfp_profile_cycle(c2);
osfp_profile_get_cycle(c1);
if (result == NULL) {
goto exit;
}
if (result->json_str != NULL) {
result_str = result->json_str;
goto exit;
}
root = cJSON_CreateObject();
if (root == NULL) {
goto exit;
@@ -128,142 +69,104 @@ char *osfp_result_score_detail_export(struct osfp_result *result)
}
}
if (result->matched) {
matched = cJSON_AddObjectToObject(root, "matched");
if (matched) {
cJSON_AddStringToObject(matched, "fingerprint", result->matched);
}
}
result_str = cJSON_Print(root);
if (result_str == NULL) {
goto exit;
}
result->json_str = result_str;
cJSON_Delete(root);
osfp_profile_get_cycle(c2);
osfp_profile_counter_update(&osfp_profile_result_export, c2 - c1);
return result_str;
exit:
if (root) {
cJSON_Delete(root);
}
return result_str;
return NULL;
}
void osfp_result_free(struct osfp_result *result)
{
if (result) {
if (result->json_str) {
free(result->json_str);
}
free(result);
}
}
struct osfp_result *osfp_ip_identify(struct osfp_db *db, unsigned char *l3_hdr, unsigned char *l4_hdr, unsigned int l4_hdr_len, unsigned int ip_version)
{
int ret;
struct osfp_fingerprint fp;
struct osfp_os_class_score os_class_score;
struct osfp_result *result;
const char *matched;
if (db == NULL || l3_hdr == NULL || l4_hdr == NULL || l4_hdr_len == 0) {
goto exit;
}
ret = osfp_fingerprinting(l3_hdr, l4_hdr, l4_hdr_len, &fp, ip_version);
if (ret != 0) {
goto exit;
}
matched = osfp_score_db_prefilter(db->score_db, &fp, &os_class_score);
if (matched == NULL) {
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, matched);
if (result == NULL) {
goto exit;
}
return result;
exit:
return NULL;
}
struct osfp_result *osfp_ipv4_identify(struct osfp_db *db, struct iphdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len)
{
int ret = OSFP_EINVAL;
struct osfp_fingerprint fp;
struct osfp_os_class_score os_class_score;
struct osfp_result *result;
if (db == NULL || l3_hdr == NULL || l4_hdr == NULL || l4_hdr == 0) {
goto exit;
}
ret = osfp_fingerprinting((unsigned char *)l3_hdr, (unsigned char *)l4_hdr, (unsigned int)l4_hdr_len, &fp, 4);
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;
return osfp_ip_identify(db, (unsigned char *)l3_hdr, (unsigned char *)l4_hdr, (unsigned int)l4_hdr_len, 4);
}
struct osfp_result *osfp_ipv6_identify(struct osfp_db *db, struct ipv6hdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len)
struct osfp_result *osfp_ipv6_identify(struct osfp_db *db, struct ip6_hdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len)
{
int ret = OSFP_EINVAL;
struct osfp_fingerprint fp;
struct osfp_os_class_score os_class_score;
struct osfp_result *result;
if (db == NULL || l3_hdr == NULL || l4_hdr == NULL) {
goto exit;
}
ret = osfp_fingerprinting((unsigned char *)l3_hdr, (unsigned char *)l4_hdr, (unsigned int)l4_hdr_len, &fp, 6);
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;
return osfp_ip_identify(db, (unsigned char *)l3_hdr, (unsigned char *)l4_hdr, (unsigned int)l4_hdr_len, 6);
}
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)
struct osfp_db *osfp_db_new(const char *fp_path)
{
int ret;
struct osfp_db *db;
if (fp_path == NULL) {
goto exit;
}
if (0 != access(fp_path, R_OK)) {
goto exit;
}
db = calloc(1, sizeof(struct osfp_db));
if (db == NULL) {
goto exit;
}
if (db_json_file != NULL) {
if (0 != access(db_json_file, R_OK)) {
goto exit;
}
db->db_json_path = strdup((const char*)db_json_file);
if (db->db_json_path == NULL) {
goto exit;
}
db->db_json_path = strdup((const char*)fp_path);
if (db->db_json_path == NULL) {
goto exit;
}
db->score_db = (void *)osfp_score_db_create();

View File

@@ -2,61 +2,20 @@
#define __OSFP_H__
#include <stddef.h>
#include <linux/in.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#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"
#define OSFP_OS_CLASS_NAME_MAC_OS "Mac OS"
#define OSFP_OS_CLASS_NAME_IOS "iOS"
#define OSFP_OS_CLASS_NAME_ANDROID "Android"
#define OSFP_OS_CLASS_NAME_OTHERS "Others"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief 枚举表示不同的操作系统类别。
*/
enum osfp_os_class_id {
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,
};
/**
* @brief 结构体用于 osfp_result 中的详细结果。
*/
struct osfp_result_detail {
unsigned int score; // 得分
unsigned int possibility; // 可能性
};
/**
* @brief 结构体用于表示操作系统识别结果。
*/
struct osfp_result {
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; // 操作系统指纹库 JSON 文件路径
void *score_db; // 分数数据库指针
};
struct osfp_result_detail;
struct osfp_result;
struct osfp_db;
/**
* @brief 创建一个新的操作系统指纹库。
@@ -73,6 +32,18 @@ struct osfp_db *osfp_db_new(const char *db_json_path);
*/
void osfp_db_free(struct osfp_db *db);
/**
* @brief 通过 IP 头部和 TCP 头部识别操作系统。
*
* @param db 操作系统指纹库。
* @param l3_hdr 指向 IP 头部的指针。
* @param l4_hdr 指向 TCP 头部的指针。
* @param l4_hdr_len TCP 头部的长度注意包含TCP选项部分
* @param ip_version IP头版本号4 或 6
* @return 指向操作系统识别结果的指针。
*/
struct osfp_result *osfp_ip_identify(struct osfp_db *db, unsigned char *l3_hdr, unsigned char *l4_hdr, unsigned int l4_hdr_len, unsigned int ip_version);
/**
* @brief 通过 IPv4 头部和 TCP 头部识别操作系统。
*
@@ -93,16 +64,7 @@ struct osfp_result *osfp_ipv4_identify(struct osfp_db *db, struct iphdr* l3_hdr,
* @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);
struct osfp_result *osfp_ipv6_identify(struct osfp_db *db, struct ip6_hdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len);
/**
* @brief 获取操作系统识别结果的操作系统名称。
@@ -127,4 +89,7 @@ char *osfp_result_score_detail_export(struct osfp_result *result);
*/
void osfp_result_free(struct osfp_result *result);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,8 +1,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include "osfp_common.h"
#include "osfp.h"
#include "osfp_log.h"
const char *os_class_name[OSFP_OS_CLASS_MAX] = {
unsigned int osfp_profile_enable;
const char *osfp_os_class_name[OSFP_OS_CLASS_MAX] = {
OSFP_OS_CLASS_NAME_UNKNOWN,
OSFP_OS_CLASS_NAME_WINDOWS,
OSFP_OS_CLASS_NAME_LINUX,
@@ -12,29 +22,185 @@ const char *os_class_name[OSFP_OS_CLASS_MAX] = {
OSFP_OS_CLASS_NAME_OTHERS
};
enum osfp_os_class_id osfp_os_class_name_to_id(char *name)
{
enum osfp_os_class_id os_class;
if (0 == strncmp(name, OSFP_OS_CLASS_NAME_WINDOWS, strlen(OSFP_OS_CLASS_NAME_WINDOWS))) {
os_class = OSFP_OS_CLASS_WINDOWS;
} else if (0 == strncmp(name, OSFP_OS_CLASS_NAME_LINUX, strlen(OSFP_OS_CLASS_NAME_LINUX))) {
os_class = OSFP_OS_CLASS_LINUX;
} else if (0 == strncmp(name, OSFP_OS_CLASS_NAME_MAC_OS, strlen(OSFP_OS_CLASS_NAME_MAC_OS))) {
os_class = OSFP_OS_CLASS_MAC_OS;
} else if (0 == strncmp(name, OSFP_OS_CLASS_NAME_IOS, strlen(OSFP_OS_CLASS_NAME_IOS))) {
os_class = OSFP_OS_CLASS_IOS;
} else if (0 == strncmp(name, OSFP_OS_CLASS_NAME_ANDROID, strlen(OSFP_OS_CLASS_NAME_ANDROID))) {
os_class = OSFP_OS_CLASS_ANDROID;
} else {
os_class = OSFP_OS_CLASS_MAX;
}
return os_class;
}
struct osfp_profile_counter osfp_profile_fingerprinting;
struct osfp_profile_counter osfp_profile_prefilter;
struct osfp_profile_counter osfp_profile_score;
struct osfp_profile_counter osfp_profile_result_build;
struct osfp_profile_counter osfp_profile_result_export;
const char *osfp_os_class_id_to_name(enum osfp_os_class_id os_class)
{
return os_class_name[os_class];
return osfp_os_class_name[os_class];
}
enum osfp_os_class_id osfp_os_class_name_to_id(char *name)
{
int i, namelen;
const char *os_class_name;
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
os_class_name = osfp_os_class_id_to_name(i);
if (0 == strncmp(name, os_class_name, strlen(os_class_name))) {
return (enum osfp_os_class_id)i;
}
}
return OSFP_OS_CLASS_MAX;
}
struct osfp_result *osfp_result_build(struct osfp_os_class_score *os_class_score, const char *matched)
{
int i;
unsigned int tmp_score;
unsigned int likely_score;
enum osfp_os_class_id likely_os_class;
struct osfp_result *result;
result = calloc(1, sizeof(struct osfp_result));
if (result == NULL) {
goto exit;
}
likely_score = 0;
likely_os_class = OSFP_OS_CLASS_OTHERS;
// likely os score
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
tmp_score = os_class_score->scores[i];
if (likely_score < tmp_score) {
likely_score = tmp_score;
likely_os_class = i;
}
result->details[i].score = tmp_score;
}
// prefiltered
if (likely_score == OSFP_PERCENTILE) {
goto end;
}
// too low to tell os class
if (likely_score < OSFP_LOWEST_SCORE_LIMIT) {
likely_os_class = OSFP_OS_CLASS_OTHERS;
goto end;
}
// when the tied likely scores appear between win/apple-like/unix-like, we throw unknown
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
if (likely_os_class == i) {
continue;
}
if (likely_score != os_class_score->scores[i]) {
continue;
}
if (likely_os_class == OSFP_OS_CLASS_LINUX && i == OSFP_OS_CLASS_ANDROID) {
continue;
}
if (likely_os_class == OSFP_OS_CLASS_MAC_OS && i == OSFP_OS_CLASS_IOS) {
continue;
}
likely_os_class = OSFP_OS_CLASS_UNKNOWN;
break;
}
end:
result->likely_os_class = likely_os_class;
result->matched = matched;
return result;
exit:
return NULL;
}
void osfp_profile_counter_print(struct osfp_profile_counter *profile, const char *name)
{
printf("profile %s: avg: %lu max: %lu min: %lu curr: %lu total: %lu count: %lu\n",
name,
profile->total_cycle / profile->count,
profile->max_cycle,
profile->min_cycle,
profile->curr_cycle,
profile->total_cycle,
profile->count);
}
void osfp_profile_counter_update(struct osfp_profile_counter *profile, unsigned long long curr_cycle)
{
profile->count++;
profile->curr_cycle = curr_cycle;
profile->total_cycle += curr_cycle;
if (profile->min_cycle == 0) {
profile->min_cycle = curr_cycle;
} else {
if (profile->min_cycle > curr_cycle) {
profile->min_cycle = curr_cycle;
}
}
if (profile->max_cycle < curr_cycle) {
profile->max_cycle = curr_cycle;
}
}
void osfp_profile_print_stats(void)
{
osfp_profile_counter_print(&osfp_profile_fingerprinting, "fingerprinting");
osfp_profile_counter_print(&osfp_profile_prefilter, "prefilter");
osfp_profile_counter_print(&osfp_profile_score, "score");
osfp_profile_counter_print(&osfp_profile_result_build, "result build");
osfp_profile_counter_print(&osfp_profile_result_export, "result export");
}
void osfp_profile_set(unsigned int enabled)
{
osfp_profile_enable = enabled;
}
char *osfp_read_file(char *fp_file)
{
int ret = -1;
char *file_buffer = NULL;
unsigned int file_len = 0;
FILE *fp = NULL;
struct stat st;
if (0 > stat(fp_file, &st)) {
osfp_log_error("stat() failed on '%s'.\n", fp_file);
goto exit;
}
if (st.st_size == 0) {
osfp_log_error("Empty file: '%s'.\n", fp_file);
goto exit;
}
file_len = (unsigned int)st.st_size;
file_buffer = malloc(file_len + 1);
if (file_buffer == NULL) {
osfp_log_error("Not enough memory for file buffer. file name: '%s'\n",fp_file);
goto exit;
}
fp = fopen(fp_file, "r");
if (fp == NULL) {
osfp_log_error("Cannot open '%s' for reading.\n", fp_file);
goto exit;
}
ret = fread(file_buffer, 1, file_len, fp);
if (ret < 0) {
osfp_log_error("fread() failed on '%s'.\n", fp_file);
free(file_buffer);
fclose(fp);
goto exit;
}
fclose(fp);
file_buffer[file_len] = 0;
return file_buffer;
exit:
return NULL;
}

View File

@@ -1,35 +1,55 @@
#ifndef __OSFP_COMMON_H__
#define __OSFP_COMMON_H__
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <stdarg.h>
#include <time.h>
static inline unsigned long long osfp_rdtsc(void)
{
union {
unsigned long long tsc_64;
struct {
unsigned int lo_32;
unsigned int hi_32;
};
} tsc;
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
asm volatile("rdtsc" :
"=a" (tsc.lo_32),
"=d" (tsc.hi_32));
return tsc.tsc_64;
}
#include "utarray.h"
#include "uthash.h"
#include "utlist.h"
#include "utringbuffer.h"
#include "utstack.h"
#include "utstring.h"
extern unsigned int osfp_profile_enable;
#include "cJSON.h"
#define osfp_profile_cycle(x) volatile unsigned long long x = 0
#define osfp_profile_get_cycle(x) do { \
if (__builtin_expect(!!(osfp_profile_enable), 0)) { \
x = osfp_rdtsc(); \
} else { \
x = 0; \
} \
} while(0)
#include "osfp.h"
struct osfp_profile_counter {
unsigned long long count;
unsigned long long curr_cycle;
unsigned long long max_cycle;
unsigned long long min_cycle;
unsigned long long total_cycle;
};
#define OSFP_TCP_OPTLENMAX 64
#define OSFP_TCP_OPTMAX 20
extern struct osfp_profile_counter osfp_profile_fingerprinting;
extern struct osfp_profile_counter osfp_profile_prefilter;
extern struct osfp_profile_counter osfp_profile_score;
extern struct osfp_profile_counter osfp_profile_result_build;
extern struct osfp_profile_counter osfp_profile_result_export;
void osfp_profile_counter_update(struct osfp_profile_counter *profile, unsigned long long curr_cycle);
void osfp_profile_print_stats(void);
void osfp_profile_set(unsigned int enabled);
#define OSFP_BIT_U32(n) (1UL << (n))
#define OSFP_PERCENTILE 100
#define OSFP_ETHERNET_HEADER_LEN 14
#define OSFP_VLAN_HEADER_LEN 4
@@ -37,8 +57,8 @@
#define OSFP_IPV6_HEADER_LEN 40
#define OSFP_TCP_HEADER_LEN 20
#define OSFP_TCP_DATA_OFF_MAX 60
#define OSFP_TCP_OPTLENMAX 64
#define OSFP_TCP_OPTMAX 20
//# TCP Options (opt_type) - http://www.iana.org/assignments/tcp-parameters
#define OSFP_TCP_OPT_EOL 0 //# end of option list
#define OSFP_TCP_OPT_NOP 1 //# no operation
@@ -74,7 +94,6 @@
#define OSFP_TCP_OPY_ENCNEG 69 //# Encryption Negotiation (TCP-ENO) [RFC8547]
#define OSFP_TCP_OPT_EXP1 253 //# RFC3692-style Experiment 1 (also improperly used for shipping products)
#define OSFP_TCP_OPT_EXP2 254 //# RFC3692-style Experiment 2 (also improperly used for shipping products)
#define OSFP_TCP_OPT_SACKOK_LEN 2
#define OSFP_TCP_OPT_WS_LEN 3
#define OSFP_TCP_OPT_TS_LEN 10
@@ -84,28 +103,40 @@
#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 */
static inline unsigned long long osfp_rdtsc(void)
{
union {
unsigned long long tsc_64;
struct {
unsigned int lo_32;
unsigned int hi_32;
};
} tsc;
asm volatile("rdtsc" :
"=a" (tsc.lo_32),
"=d" (tsc.hi_32));
return tsc.tsc_64;
}
/**
* @brief 定义操作系统类别的名称常量。
*/
#define OSFP_OS_CLASS_NAME_UNKNOWN "Unknown"
#define OSFP_OS_CLASS_NAME_WINDOWS "Windows"
#define OSFP_OS_CLASS_NAME_LINUX "Linux"
#define OSFP_OS_CLASS_NAME_MAC_OS "Mac OS"
#define OSFP_OS_CLASS_NAME_IOS "iOS"
#define OSFP_OS_CLASS_NAME_ANDROID "Android"
#define OSFP_OS_CLASS_NAME_OTHERS "Others"
#define osfp_profile_cycle(x) volatile unsigned long long x = 0
#define osfp_profile_get_cycle(x) do { \
x = osfp_rdtsc(); \
} while(0)
/**
* @brief 枚举表示不同的操作系统类别。
*/
enum osfp_os_class_id {
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,
};
#define OSFP_BIT_U32(n) (1UL << (n))
#define OSFP_OS_CLASS_FLAG_WINDOWS OSFP_BIT_U32(OSFP_OS_CLASS_WINDOWS)
#define OSFP_OS_CLASS_FLAG_LINUX OSFP_BIT_U32(OSFP_OS_CLASS_LINUX)
#define OSFP_OS_CLASS_FLAG_MAC_OS OSFP_BIT_U32(OSFP_OS_CLASS_MAC_OS)
#define OSFP_OS_CLASS_FLAG_IOS OSFP_BIT_U32(OSFP_OS_CLASS_IOS)
#define OSFP_OS_CLASS_FLAG_ANDROID OSFP_BIT_U32(OSFP_OS_CLASS_ANDROID)
#define OSFP_LOWEST_SCORE_LIMIT 20
enum osfp_error_code {
OSFP_NOERR,
@@ -116,18 +147,40 @@ enum osfp_error_code {
OSFP_ERR_SCORE_DB_UNSUPPORTED,
OSFP_ERR_FINGERPRINTING_UNSUPPORTED,
};
#define OSFP_OS_CLASS_FLAG_WINDOWS OSFP_BIT_U32(OSFP_OS_CLASS_WINDOWS)
#define OSFP_OS_CLASS_FLAG_LINUX OSFP_BIT_U32(OSFP_OS_CLASS_LINUX)
#define OSFP_OS_CLASS_FLAG_MAC_OS OSFP_BIT_U32(OSFP_OS_CLASS_MAC_OS)
#define OSFP_OS_CLASS_FLAG_IOS OSFP_BIT_U32(OSFP_OS_CLASS_IOS)
#define OSFP_OS_CLASS_FLAG_ANDROID OSFP_BIT_U32(OSFP_OS_CLASS_ANDROID)
struct osfp_os_class_score {
unsigned int scores[OSFP_OS_CLASS_MAX];
};
#define OSFP_PERCENTILE 100
/**
* @brief 结构体用于 osfp_result 中的详细结果。
*/
struct osfp_result_detail {
unsigned int score; // 得分
unsigned int possibility; // 可能性
};
/**
* @brief 结构体用于表示操作系统识别结果。
*/
struct osfp_result {
enum osfp_os_class_id likely_os_class; // 最可能的操作系统类别
struct osfp_result_detail details[OSFP_OS_CLASS_MAX]; // 详细结果数组
const char *matched; // 精确匹配到的指纹特征
};
/**
* @brief 结构体用于表示操作系统指纹库。
*/
struct osfp_db {
char *db_json_path; // 操作系统指纹库 JSON 文件路径
void *score_db; // 分数数据库指针
};
const char *osfp_os_class_id_to_name(enum osfp_os_class_id os_class);
enum osfp_os_class_id osfp_os_class_name_to_id(char *name);
const char *osfp_os_class_id_to_name(enum osfp_os_class_id os_class);
struct osfp_result *osfp_result_build(struct osfp_os_class_score *os_class_score, const char *matched);
char *osfp_read_file(char *fp_file);
#endif

View File

@@ -1,3 +1,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "cJSON.h"
#include "osfp_common.h"
#include "osfp.h"
@@ -21,6 +28,27 @@
#define OSFP_FINGERPRINT_DEFAULT_OS_CLASS_NAME "OSFP_UNKNOWN"
#define OSFP_FP_SET_FIELD(fp, field_id, v, l) do { \
(fp)->fields[(field_id)].name = osfp_fingerprint_get_field_name(field_id); \
(fp)->fields[(field_id)].enabled = 1; \
if ((fp)->value_buffer_used + (l) <= sizeof((fp)->value_buffer)) { \
memcpy(fp->value_buffer + (fp)->value_buffer_used, (v), (l)); \
(fp)->fields[(field_id)].value = (fp)->value_buffer + (fp)->value_buffer_used; \
(fp)->fields[(field_id)].value_len = (l); \
(fp)->value_buffer_used += (l); \
} else { \
(fp)->fields[(field_id)].value = NULL; \
(fp)->fields[(field_id)].value_len = 0; \
} \
} while (0)
#define OSFP_FP_INIT_FIELD(fp, field_id) do { \
(fp)->fields[(field_id)].name = osfp_fingerprint_get_field_name((field_id)); \
(fp)->fields[(field_id)].enabled = 0; \
(fp)->fields[(field_id)].value = NULL; \
(fp)->fields[(field_id)].value_len = 0; \
} while (0)
struct osfp_tcp_opt {
unsigned char type;
unsigned char len;
@@ -40,7 +68,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, 0, OSFP_FIELD_TYPE_STRING, 250, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED, 1, OSFP_FIELD_TYPE_STRING, 250, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_OS, 0, OSFP_FIELD_TYPE_STRING, 0, NULL, 0},
};
@@ -125,6 +153,39 @@ static unsigned int decode_tcp_options(struct osfp_tcp_opt *tcp_opts, unsigned i
return tcp_opt_cnt;
}
static char *osfp_fingerprint_tcp_options_to_ordered(char *tcp_options, unsigned int len)
{
int i;
char *tcp_options_ordered;
unsigned tcp_options_ordered_offset;
unsigned tcp_options_offset;
if (tcp_options == NULL && len == 0) {
goto exit;
}
tcp_options_ordered = malloc(len + 1);
if (tcp_options_ordered == NULL) {
goto exit;
}
tcp_options_offset = 0;
tcp_options_ordered_offset = 0;
while(tcp_options_offset < len) {
if (isalpha(tcp_options[tcp_options_offset])) {
tcp_options_ordered[tcp_options_ordered_offset] = tcp_options[tcp_options_offset];
tcp_options_ordered_offset++;
}
tcp_options_offset++;
}
tcp_options_ordered[tcp_options_ordered_offset] = 0;
return tcp_options_ordered;
exit:
return NULL;
}
int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsigned int buf_len, unsigned int format)
{
int rlen = 0, ret, i;
@@ -167,6 +228,75 @@ int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsi
return strlen(strbuf) + 1;
}
struct osfp_fingerprint *osfp_fingerprint_from_cjson(void *cjson_obj)
{
int i;
cJSON *root;
cJSON *field;
void *value_ptr;
unsigned int value_len;
struct osfp_fingerprint *fp;
root = (cJSON *)cjson_obj;
if (root == NULL) {
goto exit;
}
fp = calloc(1, sizeof(struct osfp_fingerprint));
if (fp == NULL) {
goto exit;
}
for (i = 0; i < OSFP_FIELD_OS; i++) {
if (0 == osfp_fingerprint_get_field_enabled(i)) {
OSFP_FP_INIT_FIELD(fp, i);
continue;
}
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(i));
if (field == NULL) {
OSFP_FP_INIT_FIELD(fp, i);
continue;
}
switch (field->type) {
case cJSON_Number:
value_ptr = (void *)&field->valueint;
value_len = sizeof(field->valueint);
OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len);
if (i == OSFP_FIELD_IP_ID ||
i == OSFP_FIELD_TCP_TIMESTAMP ||
i == OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY) {
*(unsigned int*)(fp->fields[i].value) = !!field->valueint;
}
if (i == OSFP_FIELD_IP_TTL) {
*(unsigned int*)(fp->fields[i].value) = compute_ip_ttl((unsigned int)field->valueint);
}
break;
case cJSON_String:
value_ptr = (void *)field->valuestring;
value_len = strlen(field->valuestring) + 1;
OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len);
break;
case cJSON_NULL:
//osfp_log_debug("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root));
break;
default:
goto exit;
}
}
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_OS);
return fp;
exit:
if (fp) {
free(fp);
}
return NULL;
}
int osfp_fingerprint_from_json(struct osfp_fingerprint *fp, char *json_str)
{
int ret, i;
@@ -188,11 +318,25 @@ int osfp_fingerprint_from_json(struct osfp_fingerprint *fp, char *json_str)
goto exit;
}
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS_ORDERED));
if (field == NULL) {
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS));
if (field) {
char *tcp_options_ordered_str = osfp_fingerprint_tcp_options_to_ordered(field->valuestring, strlen(field->valuestring));
if (tcp_options_ordered_str) {
cJSON_AddItemToObject(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS_ORDERED),
cJSON_CreateString(tcp_options_ordered_str));
free(tcp_options_ordered_str);
}
}
}
for (i = 0; i < OSFP_FIELD_OS; i++) {
if (!fp_fields[i].enabled) {
if (0 == osfp_fingerprint_get_field_enabled(i)) {
OSFP_FP_INIT_FIELD(fp, i);
continue;
}
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(i));
if (field == NULL) {
goto exit;
@@ -202,68 +346,44 @@ int osfp_fingerprint_from_json(struct osfp_fingerprint *fp, char *json_str)
case cJSON_Number:
value_ptr = (void *)&field->valueint;
value_len = sizeof(field->valueint);
osfp_fingerprint_setup_field(fp, i, value_ptr, value_len);
OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len);
if (i == OSFP_FIELD_IP_ID ||
i == OSFP_FIELD_TCP_TIMESTAMP ||
i == OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY) {
*(unsigned int*)(fp->fields[i].value) = !!field->valueint;
}
if (i == OSFP_FIELD_IP_TTL) {
*(unsigned int*)(fp->fields[i].value) = compute_ip_ttl((unsigned int)field->valueint);
}
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);
OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len);
break;
case cJSON_NULL:
//printf("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root));
//osfp_log_debug("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root));
break;
default:
goto exit;
}
}
return 0;
ret = 0;
exit:
if (root) {
cJSON_Delete(root);
}
return ret;
}
unsigned int osfp_fingerprint_get_field_enabled(enum osfp_field_id field_id)
{
return fp_fields[field_id].enabled;
}
unsigned int osfp_fingerprint_get_field_importance(enum osfp_field_id field_id)
{
return fp_fields[field_id].importance;
}
const char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id)
{
return fp_fields[field_id].name;
}
unsigned int osfp_fingerprint_get_field_type(enum osfp_field_id field_id)
{
return fp_fields[field_id].type;
}
void osfp_fingerprint_setup_field(struct osfp_fingerprint *fp, enum osfp_field_id field_id, void *value, unsigned int len)
{
fp->fields[field_id].name = osfp_fingerprint_get_field_name(field_id);
fp->fields[field_id].enabled = 1;
if (fp->value_buffer_used + len <= sizeof(fp->value_buffer)) {
memcpy(fp->value_buffer + fp->value_buffer_used, value, len);
fp->fields[field_id].value = fp->value_buffer + fp->value_buffer_used;
fp->fields[field_id].value_len = len;
fp->value_buffer_used += len;
} else {
fp->fields[field_id].value = NULL;
fp->fields[field_id].value_len = 0;
}
}
int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len, struct osfp_fingerprint *fp)
static int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len, struct osfp_fingerprint *fp)
{
int ret,i;
unsigned int tcp_mss;
unsigned int tcp_ws;
unsigned int tcp_ts;
unsigned int tcp_ter;
unsigned int tcp_opt_cnt;
struct osfp_tcp_opt tcp_opts[OSFP_TCP_OPTMAX];
@@ -297,7 +417,7 @@ int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len
break;
}
tcp_mss = (unsigned int)ntohs(*(unsigned short *)opt->data);
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_MSS, &tcp_mss, sizeof(tcp_mss));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_MSS, &tcp_mss, sizeof(tcp_mss));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_mss);
if (ret < 0 || offset + ret > maxoffset) {
break;
@@ -309,7 +429,7 @@ int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len
break;
}
tcp_ws = (unsigned int)*(unsigned char *)opt->data;
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_WINDOW_SCALING, &tcp_ws, sizeof(tcp_ws));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_WINDOW_SCALING, &tcp_ws, sizeof(tcp_ws));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_ws);
if (ret < 0 || offset + ret > maxoffset) {
break;
@@ -320,8 +440,10 @@ int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len
if (opt->len != OSFP_TCP_OPT_TS_LEN) {
break;
}
tcp_ter = ntohl(*(unsigned int *)(opt->data + 4));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY, &tcp_ter, sizeof(tcp_ter));
tcp_ts = !!ntohl(*(unsigned int *)(opt->data));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP, &tcp_ts, sizeof(tcp_ts));
tcp_ter = !!ntohl(*(unsigned int *)(opt->data + 4));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY, &tcp_ter, sizeof(tcp_ter));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_ter);
if (ret < 0 || offset + ret > maxoffset) {
break;
@@ -348,15 +470,15 @@ int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len
options_ordered[ordered_offset] = 0;
}
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);
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OPTIONS, options, strlen(options) + 1);
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OPTIONS_ORDERED, options_ordered, strlen(options_ordered) + 1);
return 0;
exit:
return -1;
}
int osfp_fingerprinting_tcp(struct tcphdr *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp)
static int osfp_fingerprinting_tcp(struct tcphdr *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp)
{
unsigned int tcp_off;
unsigned int tcp_window_size;
@@ -368,15 +490,22 @@ int osfp_fingerprinting_tcp(struct tcphdr *tcph, unsigned int tcph_len, struct o
tcp_off = tcph->doff << 2;
tcp_window_size = ntohs(tcph->window);
tcp_flags = *((unsigned char *)&tcph->window - 1);
tcp_flags = *((unsigned char *)&tcph->ack_seq + 5);
if (tcp_off != tcph_len) {
goto exit;
}
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_OFF, &tcp_off, sizeof(tcp_off));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_WINDOW_SIZE, &tcp_window_size, sizeof(tcp_window_size));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_FLAGS, &tcp_flags, sizeof(tcp_flags));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OFF, &tcp_off, sizeof(tcp_off));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_WINDOW_SIZE, &tcp_window_size, sizeof(tcp_window_size));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_FLAGS, &tcp_flags, sizeof(tcp_flags));
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP);
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY);
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_WINDOW_SCALING);
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_MSS);
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_OPTIONS);
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_OPTIONS_ORDERED);
// tcp options
if (tcp_off > OSFP_TCP_HEADER_LEN) {
@@ -388,7 +517,7 @@ exit:
return -1;
}
int osfp_fingerprinting_ipv4(struct iphdr *iph, struct osfp_fingerprint *fp)
static int osfp_fingerprinting_ipv4(struct iphdr *iph, struct osfp_fingerprint *fp)
{
if (iph == NULL || fp == NULL) {
goto exit;
@@ -399,17 +528,17 @@ int osfp_fingerprinting_ipv4(struct iphdr *iph, struct osfp_fingerprint *fp)
unsigned int ip_total_length = ntohs(iph->tot_len);
unsigned int ip_ttl = compute_ip_ttl(iph->ttl);
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
return 0;
exit:
return -1;
}
int osfp_fingerprinting_ipv6(struct ipv6hdr *iph, struct osfp_fingerprint *fp)
static int osfp_fingerprinting_ipv6(struct ip6_hdr *iph, struct osfp_fingerprint *fp)
{
if (iph == NULL || fp == NULL) {
goto exit;
@@ -417,13 +546,14 @@ int osfp_fingerprinting_ipv6(struct ipv6hdr *iph, struct osfp_fingerprint *fp)
//unsigned int ip_id = 0;
//unsigned int ip_tos = 0;
unsigned int ip_total_length = OSFP_IPV6_HEADER_LEN + ntohs(iph->payload_len);
unsigned int ip_ttl = compute_ip_ttl(iph->hop_limit);
unsigned int ip_total_length = OSFP_IPV6_HEADER_LEN + ntohs(iph->ip6_ctlun.ip6_un1.ip6_un1_plen);
unsigned int ip_ttl = compute_ip_ttl(iph->ip6_ctlun.ip6_un1.ip6_un1_hlim);
//osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
//osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_IP_ID);
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_IP_TOS);
return 0;
exit:
@@ -434,18 +564,18 @@ int osfp_fingerprinting(unsigned char *iph, unsigned char *tcph, unsigned int tc
{
int ret = OSFP_EINVAL;
if (iph == NULL || tcph == NULL || fp == NULL) {
if (iph == NULL || tcph == NULL || tcph_len == 0 || fp == NULL) {
goto exit;
}
memset(fp, 0, sizeof(struct osfp_fingerprint));
fp->value_buffer_used = 0;
switch (ip_version) {
case 4:
ret = osfp_fingerprinting_ipv4((struct iphdr *)iph, fp);
break;
case 6:
ret = osfp_fingerprinting_ipv6((struct ipv6hdr *)iph, fp);
ret = osfp_fingerprinting_ipv6((struct ip6_hdr *)iph, fp);
break;
default:
ret = -1;
@@ -461,14 +591,13 @@ int osfp_fingerprinting(unsigned char *iph, unsigned char *tcph, unsigned int tc
goto exit;
}
osfp_fingerprint_setup_field(fp, OSFP_FIELD_OS, OSFP_FINGERPRINT_DEFAULT_OS_CLASS_NAME, strlen(OSFP_FINGERPRINT_DEFAULT_OS_CLASS_NAME) + 1);
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_OS);
return 0;
exit:
return -1;
}
#ifdef UNITTEST
int test_osfp_fingerprinting_ipv4(void)
{
int ret;
@@ -486,7 +615,7 @@ int test_osfp_fingerprinting_ipv4(void)
};
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\"}";
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\":null}";
struct osfp_fingerprint fp = {0};
ret = osfp_fingerprinting(iph, tcph, 32, &fp, 4);
@@ -499,6 +628,7 @@ int test_osfp_fingerprinting_ipv4(void)
goto exit;
}
ret = -1;
if (0 != memcmp(str_buf, target, strlen(target))) {
goto exit;
}
@@ -525,7 +655,7 @@ int test_osfp_fingerprinting_ipv6(void)
};
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\"}";
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\":null}";
struct osfp_fingerprint fp = {0};
ret = osfp_fingerprinting(iph, tcph, 32, &fp, 4);
@@ -538,6 +668,7 @@ int test_osfp_fingerprinting_ipv6(void)
goto exit;
}
ret = -1;
if (0 != memcmp(str_buf, target, strlen(target))) {
goto exit;
}
@@ -566,6 +697,7 @@ int test_osfp_fingerprinting_tcp_option(void)
goto exit;
}
ret = -1;
if (fp.fields[OSFP_FIELD_TCP_OPTIONS].value_len != strlen(target_options) + 1)
{
goto exit;
@@ -587,4 +719,3 @@ int test_osfp_fingerprinting_tcp_option(void)
exit:
return ret;
}
#endif

View File

@@ -43,24 +43,35 @@ struct osfp_fingerprint {
unsigned int value_buffer_used;
};
extern struct osfp_fingerprint_field fp_fields[OSFP_FIELD_MAX];
static inline unsigned int osfp_fingerprint_get_field_enabled(enum osfp_field_id field_id)
{
return fp_fields[field_id].enabled;
}
static inline unsigned int osfp_fingerprint_get_field_importance(enum osfp_field_id field_id)
{
return fp_fields[field_id].importance;
}
static inline const char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id)
{
return fp_fields[field_id].name;
}
static inline unsigned int osfp_fingerprint_get_field_type(enum osfp_field_id field_id)
{
return fp_fields[field_id].type;
}
struct osfp_fingerprint *osfp_fingerprint_from_cjson(void *root);
int osfp_fingerprint_from_json(struct osfp_fingerprint *fp, char *json_str);
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_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_ipv4(void);
int test_osfp_fingerprinting_ipv6(void);
int test_osfp_fingerprinting_tcp_option(void);
#endif
#endif

View File

@@ -1,3 +1,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include "osfp_common.h"
#include "osfp_log.h"

View File

@@ -1,3 +1,8 @@
#include <stdio.h>
#include <sys/param.h>
#include "cJSON.h"
#include "osfp_common.h"
#include "osfp.h"
@@ -5,20 +10,29 @@
#include "osfp_score_db.h"
#include "osfp_log.h"
#define PERFECT_SCORE_EXPECTED_RATE 0.5f
/*
* 一个字段的值,最多能命中单个操作系统的,指纹总数的百分比
* 例如linux 总指纹数量 100其中 ip_id 字段值为 1 的指纹数为80那么 ip_id
* 字段的得分最大为 FIELD_VALUE_DUP_RATE_MAX * 100 = 50
*/
#define FIELD_VALUE_DUP_RATE_MAX 0.5f
/*
* 由于 FIELD_VALUE_DUP_RATE_MAX 设置了单个字段得分的最大百分比,所以在归一化时,应除以这个百分比
*/
#define PERFECT_SCORE_EXPECTED_RATE (FIELD_VALUE_DUP_RATE_MAX)
#define OSFP_SCORE_DB_FIELD_UINT_VALUE_MAX 65536
struct osfp_score_db_array_data {
struct osfp_os_class_score *array_head;
struct osfp_field_value_count *array_head;
unsigned int array_len;
};
struct osfp_score_db_hash_element {
char *key;
unsigned int keylen;
struct osfp_os_class_score *score;
struct osfp_field_value_count *fvc;
UT_hash_handle hh;
};
@@ -26,13 +40,13 @@ struct osfp_score_db_hash_data {
struct osfp_score_db_hash_element *hash_head;
};
int osfp_score_db_array_add(void *data, struct osfp_os_class_score *os_class_score, void *value, unsigned int len)
static int osfp_score_db_array_add(void *data, struct osfp_field_value_count *fvc, void *value, unsigned int len)
{
int ret = -1, i;
unsigned int index;
struct osfp_score_db_array_data *array_data = (struct osfp_score_db_array_data *)data;
if (array_data == NULL || os_class_score == NULL || value == NULL || len != sizeof(unsigned int)) {
if (array_data == NULL || fvc == NULL || value == NULL || len != sizeof(unsigned int)) {
goto exit;
}
@@ -47,7 +61,7 @@ int osfp_score_db_array_add(void *data, struct osfp_os_class_score *os_class_sco
}
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
array_data->array_head[index].scores[i] += os_class_score->scores[i];
array_data->array_head[index].counts[i] += fvc->counts[i];
}
return 0;
@@ -55,7 +69,7 @@ exit:
return ret;
}
struct osfp_os_class_score *osfp_score_db_array_match(void *data, void *value, unsigned int len)
static struct osfp_field_value_count *osfp_score_db_array_match(void *data, void *value, unsigned int len)
{
unsigned int index;
struct osfp_score_db_array_data *array_data = (struct osfp_score_db_array_data *)data;
@@ -77,14 +91,14 @@ struct osfp_os_class_score *osfp_score_db_array_match(void *data, void *value, u
return &((array_data->array_head)[index]);
}
void *osfp_score_db_array_create(void)
static void *osfp_score_db_array_create(void)
{
struct osfp_score_db_array_data *array_data = calloc(1, sizeof(struct osfp_score_db_array_data));
if (array_data == NULL) {
return NULL;
}
array_data->array_head = calloc(OSFP_SCORE_DB_FIELD_UINT_VALUE_MAX, sizeof(struct osfp_os_class_score));
array_data->array_head = calloc(OSFP_SCORE_DB_FIELD_UINT_VALUE_MAX, sizeof(struct osfp_field_value_count));
if (array_data->array_head == NULL) {
free(array_data);
return NULL;
@@ -95,7 +109,7 @@ void *osfp_score_db_array_create(void)
return (void *)array_data;
}
void osfp_score_db_array_destroy(void *data) {
static void osfp_score_db_array_destroy(void *data) {
struct osfp_score_db_array_data *array_data = (struct osfp_score_db_array_data *)data;
if (array_data) {
@@ -106,13 +120,13 @@ void osfp_score_db_array_destroy(void *data) {
}
}
int osfp_score_db_hash_add(void *data, struct osfp_os_class_score *os_class_score, void *value, unsigned int len)
static int osfp_score_db_hash_add(void *data, struct osfp_field_value_count *fvc, void *value, unsigned int len)
{
int ret = -1, i;
struct osfp_score_db_hash_data *hash_data = (struct osfp_score_db_hash_data *)data;
struct osfp_score_db_hash_element *element = NULL;
if (hash_data == NULL || os_class_score == NULL || value == NULL || len == 0) {
if (hash_data == NULL || fvc == NULL || value == NULL || len == 0) {
goto exit;
}
@@ -124,8 +138,8 @@ int osfp_score_db_hash_add(void *data, struct osfp_os_class_score *os_class_scor
}
element->key = strdup(value);
element->keylen = len;
element->score = (struct osfp_os_class_score *)calloc(1, sizeof(struct osfp_os_class_score));
if (element->score == NULL) {
element->fvc = (struct osfp_field_value_count *)calloc(1, sizeof(struct osfp_field_value_count));
if (element->fvc == NULL) {
free(element);
element = NULL;
goto exit;
@@ -134,7 +148,7 @@ int osfp_score_db_hash_add(void *data, struct osfp_os_class_score *os_class_scor
}
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
element->score->scores[i] += os_class_score->scores[i];
element->fvc->counts[i] += fvc->counts[i];
}
return 0;
@@ -142,7 +156,7 @@ exit:
return ret;
}
struct osfp_os_class_score *osfp_score_db_hash_match(void *data, void *value, unsigned int len)
static struct osfp_field_value_count *osfp_score_db_hash_match(void *data, void *value, unsigned int len)
{
struct osfp_score_db_hash_data *hash_data = (struct osfp_score_db_hash_data *)data;
struct osfp_score_db_hash_element *element = NULL;
@@ -160,15 +174,15 @@ struct osfp_os_class_score *osfp_score_db_hash_match(void *data, void *value, un
return NULL;
}
return element->score;
return element->fvc;
}
void *osfp_score_db_hash_create(void)
static void *osfp_score_db_hash_create(void)
{
return (void*)calloc(1, sizeof(struct osfp_score_db_hash_data));
}
void osfp_score_db_hash_destroy(void *data) {
static void osfp_score_db_hash_destroy(void *data) {
struct osfp_score_db_hash_data *hash_data = (struct osfp_score_db_hash_data *)data;
struct osfp_score_db_hash_element *element = NULL;
struct osfp_score_db_hash_element *tmp = NULL;
@@ -181,8 +195,8 @@ void osfp_score_db_hash_destroy(void *data) {
if (element->key) {
free(element->key);
}
if (element->score) {
free(element->score);
if (element->fvc) {
free(element->fvc);
}
free(element);
}
@@ -192,58 +206,136 @@ void osfp_score_db_hash_destroy(void *data) {
}
}
char *osfp_score_db_read_file(char *fp_file)
void osfp_score_db_debug_print(struct osfp_score_db *score_db)
{
int ret = -1;
char *file_buffer = NULL;
unsigned int file_len = 0;
FILE *fp = NULL;
struct stat st;
int i;
printf("score_db:\n");
printf("entry_count: %u\n", score_db->entry_count);
printf("total_weight: %u\n", score_db->total_weight);
if (0 > stat(fp_file, &st)) {
osfp_log_error("stat() failed on '%s'.\n", fp_file);
goto exit;
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
const char *name = osfp_os_class_id_to_name(i);
printf("os class %s ", name);
printf("entry_count: %u\n", score_db->os_entry_count[i]);
printf("os class %s entry_count: %u\n", osfp_os_class_id_to_name(i), score_db->os_entry_count[i]);
}
if (st.st_size == 0) {
osfp_log_error("Empty file: '%s'.\n", fp_file);
goto exit;
for (i = 0; i < OSFP_FIELD_MAX; i++) {
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);
}
file_len = (unsigned int)st.st_size;
file_buffer = malloc(file_len + 1);
if (file_buffer == NULL) {
osfp_log_error("Not enough memory for file buffer. file name: '%s'\n",fp_file);
goto exit;
}
fp = fopen(fp_file, "r");
if (fp == NULL) {
osfp_log_error("Cannot open '%s' for reading.\n", fp_file);
goto exit;
}
ret = fread(file_buffer, 1, file_len, fp);
if (ret < 0) {
osfp_log_error("fread() failed on '%s'.\n", fp_file);
free(file_buffer);
fclose(fp);
goto exit;
}
fclose(fp);
file_buffer[file_len] = 0;
return file_buffer;
exit:
return NULL;
}
int osfp_score_db_load_field(struct osfp_field_score_db *db, cJSON *field, enum osfp_os_class_id os_class)
const char *osfp_score_db_prefilter(struct osfp_score_db *score_db, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score)
{
int ret, i;
unsigned int value_buffer_used = 0;
char value_buffer[OSFP_FINGERPRINT_VALUE_BUFFER_MAX];
struct osfp_prefilter_hash_element *element = NULL;
if (score_db->prefilter_head == NULL) {
return NULL;
}
for (i = 0; i < OSFP_FIELD_OS; i++) {
if (0 == osfp_fingerprint_get_field_enabled(i)) {
continue;
}
if (fp->fields[i].value && fp->fields[i].value_len != 0) {
memcpy(value_buffer + value_buffer_used, fp->fields[i].value, fp->fields[i].value_len);
value_buffer_used += fp->fields[i].value_len;
}
}
HASH_FIND(hh, score_db->prefilter_head, value_buffer, value_buffer_used, element);
if (element == NULL) {
return NULL;
}
if (element->repeated) {
return NULL;
}
memset(result_score, 0, sizeof(struct osfp_os_class_score));
result_score->scores[element->os_class] = OSFP_PERCENTILE;
return (const char *)element->fp_json;
}
int osfp_score_db_score(struct osfp_score_db *score_db, unsigned int flags, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score)
{
int ret = -1, i, j;
unsigned int coefficient;
unsigned int matched_count;
unsigned int os_entry_count;
unsigned int total_weight;
unsigned int field_weight;
struct osfp_fingerprint_field *field;
struct osfp_field_value_count *fvc; // field_value_count
struct osfp_field_score_db *fdb;
if (score_db == NULL || fp == NULL || result_score == NULL) {
goto exit;
}
// score
memset(result_score, 0, sizeof(struct osfp_os_class_score));
total_weight = score_db->total_weight;
if (total_weight == 0) {
goto exit;
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
fdb = &score_db->field_score_dbs[i];
if (!fdb->enabled) {
continue;
}
field = &fp->fields[i];
if (!field->enabled) {
continue;
}
fvc = fdb->match(fdb->data, field->value, field->value_len);
if (fvc == NULL) {
continue;
}
coefficient = fdb->coefficient;
for (j = 0; j < OSFP_OS_CLASS_MAX; j++) {
os_entry_count = score_db->os_entry_count[j];
matched_count = MIN(fvc->counts[j], os_entry_count * FIELD_VALUE_DUP_RATE_MAX);
if (os_entry_count == 0 || matched_count == 0) {
continue;
}
if (0 == flags || flags & OSFP_BIT_U32(j)) {
result_score->scores[j] += coefficient * matched_count / os_entry_count;
}
}
// if tcp options matched tcp options ordered is not needed
if (i == OSFP_FIELD_TCP_OPTIONS) {
i++;
}
}
return 0;
exit:
return ret;
}
static int osfp_score_db_load_field(struct osfp_field_score_db *db, cJSON *field, enum osfp_os_class_id os_class)
{
int ret = -1;
struct osfp_os_class_score os_class_score = {0};
struct osfp_field_value_count field_value_count = {0};
void *value_ptr;
unsigned int value_len;
@@ -264,9 +356,9 @@ int osfp_score_db_load_field(struct osfp_field_score_db *db, cJSON *field, enum
goto exit;
}
os_class_score.scores[os_class] = 1;
field_value_count.counts[os_class] = 1;
ret = db->add(db->data, &os_class_score, value_ptr, value_len);
ret = db->add(db->data, &field_value_count, value_ptr, value_len);
if (ret != 0) {
goto exit;
}
@@ -278,7 +370,7 @@ exit:
return ret;
}
int osfp_score_db_load_entry(struct osfp_score_db *score_db, cJSON *entry)
static int osfp_score_db_load_entry(struct osfp_score_db *score_db, cJSON *entry)
{
int ret = -1, i;
cJSON *field = NULL;
@@ -289,6 +381,7 @@ int osfp_score_db_load_entry(struct osfp_score_db *score_db, cJSON *entry)
goto exit;
}
// get os class
field = cJSON_GetObjectItem(entry, osfp_fingerprint_get_field_name(OSFP_FIELD_OS));
if (field == NULL || field->valuestring == NULL) {
goto exit;
@@ -299,6 +392,31 @@ int osfp_score_db_load_entry(struct osfp_score_db *score_db, cJSON *entry)
goto exit;
}
// prefileter
struct osfp_prefilter_hash_element *element = NULL;
struct osfp_fingerprint *fp = osfp_fingerprint_from_cjson(entry);
if (fp == NULL) {
goto exit;
}
HASH_FIND(hh, score_db->prefilter_head, fp->value_buffer, fp->value_buffer_used, element);
if (element == NULL) {
element = (struct osfp_prefilter_hash_element *)calloc(1, sizeof(struct osfp_prefilter_hash_element));
if (element == NULL) {
free(fp);
goto exit;
}
element->fp_json = cJSON_Print(entry);
element->fp = fp;
element->os_class = os_class;
HASH_ADD_KEYPTR(hh, score_db->prefilter_head, fp->value_buffer, fp->value_buffer_used, element);
} else {
// TODO: same fingerprints with different os should not insert into prefilter hash table, now just tag
element->repeated++;
free(fp);
}
// field score db
for (i = 0; i < OSFP_FIELD_OS; i++) {
db = &score_db->field_score_dbs[i];
if (db == NULL) {
@@ -325,7 +443,7 @@ int osfp_score_db_load_entry(struct osfp_score_db *score_db, cJSON *entry)
}
score_db->entry_count++;
score_db->os_class_entry_count[os_class]++;
score_db->os_entry_count[os_class]++;
return 0;
exit:
@@ -334,148 +452,49 @@ exit:
int osfp_score_db_load(struct osfp_score_db *score_db, char *fp_file)
{
int ret = OSFP_EINVAL, i, count;
int ret = -1, i, count;
char *file_buffer;
cJSON *root = NULL;
cJSON *entry;
cJSON *root = NULL;
struct osfp_field_score_db *field_score_db;
if (score_db == NULL) {
if (score_db == NULL || fp_file == NULL) {
goto exit;
}
file_buffer = osfp_score_db_read_file(fp_file);
file_buffer = osfp_read_file(fp_file);
if (file_buffer == NULL) {
osfp_log_error("read file: '%s'\n", fp_file);
ret = OSFP_ERR_SCORE_DB_READ_FILE;
goto exit;
}
root = cJSON_Parse(file_buffer);
if (root == NULL) {
osfp_log_error("parse json: '%s'\n", fp_file);
ret = OSFP_ERR_SCORE_DB_PARSE_FILE;
goto exit;
}
count = cJSON_GetArraySize(root);
for (i = 0; i < count; i++) {
entry = cJSON_GetArrayItem(root, i);
if (entry) {
ret = osfp_score_db_load_entry(score_db, entry);
if (ret != 0) {
osfp_log_debug("json entry load failed.\n%s\n", cJSON_Print(entry));
continue;
}
ret = osfp_score_db_load_entry(score_db, entry);
if (ret != 0) {
osfp_log_debug("json entry load failed.\n%s\n", cJSON_Print(entry));
continue;
}
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
field_score_db = &score_db->field_score_dbs[i];
if (field_score_db->enabled && i != OSFP_FIELD_TCP_OPTIONS) {
score_db->perfect_score += osfp_fingerprint_get_field_importance(i);
}
}
score_db->perfect_score = score_db->perfect_score * PERFECT_SCORE_EXPECTED_RATE;
ret = OSFP_NOERR;
ret = 0;
exit:
if (root) {
cJSON_Delete(root);
}
if (file_buffer) {
free((char*)file_buffer);
free(file_buffer);
}
return ret;
}
int osfp_score_db_score(struct osfp_score_db *score_db, unsigned int flags, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score)
{
int ret = OSFP_EINVAL, i, j;
unsigned int tmp_score;
unsigned int perfect_score;
unsigned int entry_count;
unsigned int importance;
struct osfp_os_class_score *os_class_score_matched;
enum osfp_os_class_id os_class_id;
struct osfp_fingerprint_field *field;
struct osfp_field_score_db *field_score_db;
if (score_db == NULL || fp == NULL || result_score == NULL) {
goto exit;
}
memset(result_score, 0, sizeof(struct osfp_os_class_score));
perfect_score = score_db->perfect_score;
if (perfect_score == 0) {
goto exit;
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
field_score_db = &score_db->field_score_dbs[i];
if (!field_score_db->enabled) {
continue;
}
field = &fp->fields[i];
if (!field->enabled) {
continue;
}
os_class_score_matched = field_score_db->match(field_score_db->data, field->value, field->value_len);
if (os_class_score_matched == NULL) {
continue;
}
importance = osfp_fingerprint_get_field_importance(i);
for (j = 0; j < OSFP_OS_CLASS_MAX; j++) {
entry_count = score_db->os_class_entry_count[j];
tmp_score = os_class_score_matched->scores[j];
tmp_score = tmp_score < (entry_count * FIELD_VALUE_DUP_RATE_MAX) ? tmp_score : (entry_count * FIELD_VALUE_DUP_RATE_MAX);
if (entry_count == 0 || tmp_score == 0) {
continue;
}
if (0 == flags || flags & OSFP_BIT_U32(j)) {
osfp_log_debug("%s %s: ((%d * %u / %u) * %u ) / %u\n", osfp_fingerprint_get_field_name(i), osfp_os_class_id_to_name(j), OSFP_PERCENTILE, importance, perfect_score, os_class_score_matched->scores[j], entry_count);
result_score->scores[j] += ((OSFP_PERCENTILE * importance / perfect_score) * tmp_score) / entry_count;
}
}
}
return OSFP_NOERR;
exit:
return ret;
}
void osfp_score_db_debug_print(struct osfp_score_db *score_db)
{
int i;
printf("score_db:\n");
printf("entry_count: %u\n", score_db->entry_count);
printf("perfect_score: %u\n", score_db->perfect_score);
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
const char *name = osfp_os_class_id_to_name(i);
printf("os class %s ", name);
printf("entry_count: %u\n", score_db->os_class_entry_count[i]);
printf("os class %s entry_count: %u\n", osfp_os_class_id_to_name(i), score_db->os_class_entry_count[i]);
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
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);
}
}
struct osfp_score_db *osfp_score_db_create(void)
{
int i;
@@ -496,6 +515,12 @@ struct osfp_score_db *osfp_score_db_create(void)
continue;
}
db->weight = osfp_fingerprint_get_field_importance(i);
// tcp options ordered and tcp options overlap
if (i != OSFP_FIELD_TCP_OPTIONS_ORDERED) {
score_db->total_weight += db->weight;
}
db->type = osfp_fingerprint_get_field_type(i);
switch (db->type) {
case OSFP_FIELD_TYPE_UINT:
@@ -511,17 +536,22 @@ struct osfp_score_db *osfp_score_db_create(void)
db->match = osfp_score_db_hash_match;
break;
default:
osfp_log_debug("fingerprint field unsupported type: %u", db->type);
osfp_log_error("fingerprint field unsupported type: %u", db->type);
goto exit;
}
db->data = db->create();
if (db->data == NULL) {
osfp_log_debug("field db create failed. field: %s", osfp_fingerprint_get_field_name(i));
osfp_log_error("field db create failed. field: %s", osfp_fingerprint_get_field_name(i));
goto exit;
}
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
db = &score_db->field_score_dbs[i];
db->coefficient = (OSFP_PERCENTILE / PERFECT_SCORE_EXPECTED_RATE) * ((float)db->weight / (float)score_db->total_weight);
}
return score_db;
exit:
if (score_db) {
@@ -534,8 +564,27 @@ void osfp_score_db_destroy(struct osfp_score_db *score_db)
{
int i;
struct osfp_field_score_db *db;
struct osfp_prefilter_hash_element *element = NULL;
struct osfp_prefilter_hash_element *tmp = NULL;
if (score_db) {
// prefilter
if (score_db->prefilter_head) {
HASH_ITER(hh, score_db->prefilter_head, element, tmp) {
HASH_DELETE(hh, score_db->prefilter_head, element);
if (element) {
if (element->fp) {
free(element->fp);
}
if (element->fp_json) {
free(element->fp_json);
}
free(element);
}
}
}
// field score db
for (i = 0; i < OSFP_FIELD_MAX; i++) {
db = &score_db->field_score_dbs[i];
if (db->destroy && db->data) {
@@ -547,7 +596,6 @@ void osfp_score_db_destroy(struct osfp_score_db *score_db)
}
}
#ifdef UNITTEST
int test_osfp_score_db(void)
{
int ret, i;
@@ -596,7 +644,7 @@ int test_osfp_score_db(void)
goto exit;
}
if (db->os_class_entry_count[OSFP_OS_CLASS_ANDROID] != 1) {
if (db->os_entry_count[OSFP_OS_CLASS_ANDROID] != 1) {
goto exit;
}
@@ -618,5 +666,3 @@ int test_osfp_score_db(void)
exit:
return -1;
}
#endif

View File

@@ -1,11 +1,14 @@
#ifndef __OSFP_SCORE_DB_H__
#define __OSFP_SCORE_DB_H__
#include "uthash.h"
#include "osfp.h"
#include "osfp_fingerprint.h"
#include "osfp_common.h"
struct osfp_os_class_score {
unsigned int scores[OSFP_OS_CLASS_MAX];
struct osfp_field_value_count {
unsigned int counts[OSFP_OS_CLASS_MAX];
};
struct osfp_field_score_db {
@@ -13,32 +16,48 @@ struct osfp_field_score_db {
unsigned int type;
unsigned int entry_count;
unsigned int weight;
float coefficient;
void *data;
void *(*create)(void);
void (*destroy)(void *);
int (*add)(void *data, struct osfp_os_class_score *, void *, unsigned int);
struct osfp_os_class_score *(*match)(void *, void *, unsigned int);
int (*add)(void *data, struct osfp_field_value_count *, void *, unsigned int);
struct osfp_field_value_count *(*match)(void *, void *, unsigned int);
};
struct osfp_prefilter_hash_element {
unsigned int os_class;
unsigned int repeated;
struct osfp_fingerprint *fp;
char *fp_json;
UT_hash_handle hh;
};
struct osfp_score_db {
struct osfp_prefilter_hash_element *prefilter_head;
unsigned int entry_count;
unsigned int perfect_score;
unsigned int os_class_entry_count[OSFP_OS_CLASS_MAX];
unsigned int os_entry_count[OSFP_OS_CLASS_MAX];
unsigned int total_weight;
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);
const char *osfp_score_db_prefilter(struct osfp_score_db *score_db, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score);
int osfp_score_db_load(struct osfp_score_db *score_db, char *fp_file);
int osfp_score_db_score(struct osfp_score_db *score_db, unsigned int flags, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score);
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

12
src/version.map Normal file
View File

@@ -0,0 +1,12 @@
{
global:
osfp_db_new;
osfp_db_free;
osfp_ip_identify;
osfp_ipv4_identify;
osfp_ipv6_identify;
osfp_result_os_name_get;
osfp_result_score_detail_export;
osfp_result_free;
local: *;
};

View File

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

View File

@@ -1,228 +0,0 @@
#! /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

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include "cJSON.h"
@@ -10,6 +11,7 @@
#include "osfp_fingerprint.h"
#include "osfp_score_db.h"
#include "osfp_log.h"
#include "osfp_common.h"
#define DATA_FILE_PATH "./data.json"
@@ -17,6 +19,9 @@
#define TEST_FILE_PATH "./test.json"
#define LOG_FILE_PATH "./osfp_test.log"
#define OSFP_OS_CLASS_MERGED_MAX (OSFP_OS_CLASS_MAX - 2)
#define EntryWidth 8
unsigned char *data_file_path = DATA_FILE_PATH;
unsigned char *db_file_path;
unsigned char *test_file_path;
@@ -28,12 +33,141 @@ FILE *log_file_ptr;
unsigned int debug_enable;
//enum osfp_os_class_id {
// 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,
//};
// merged classes: unknown 0 windows-like 1 unix-like 2 apple-like 3 others 4
unsigned int testresult[OSFP_OS_CLASS_MAX][OSFP_OS_CLASS_MAX] = {0};
unsigned int testresult_merged[OSFP_OS_CLASS_MERGED_MAX][OSFP_OS_CLASS_MERGED_MAX] = {0};
static const char *class_to_merged_name(unsigned int class)
{
switch (class) {
case 0:
return "Unknown";
case 1:
return "Windows-Like";
case 2:
return "Unix-Like";
case 3:
return "Apple-Like";
case 4:
return "Others";
}
return NULL;
}
static unsigned int class_to_merged_class(unsigned int class)
{
switch (class) {
case OSFP_OS_CLASS_UNKNOWN:
return 0;
case OSFP_OS_CLASS_WINDOWS:
return 1;
case OSFP_OS_CLASS_LINUX:
return 2;
case OSFP_OS_CLASS_MAC_OS:
return 3;
case OSFP_OS_CLASS_IOS:
return 3;
case OSFP_OS_CLASS_ANDROID:
return 2;
case OSFP_OS_CLASS_OTHERS:
return 4;
}
return 0;
}
static void testresult_class_merge()
{
int i,j;
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
for (j = 0; j < OSFP_OS_CLASS_MAX; j++) {
testresult_merged[class_to_merged_class(i)][class_to_merged_class(j)] += testresult[i][j];
}
}
}
static void print_confusion_matrix(unsigned int *result, unsigned int os_class_max, const char *(*get_name)(unsigned int))
{
int i,j;
int matched = 0, missed = 0;
for (i = 0; i < os_class_max; i++) {
printf("%*s(%c)", EntryWidth-3, " ", 'a' + i);
}
printf(" <-" " classified as" "\n");
for (i = 0; i < os_class_max; i++) {
printf("%*.*s", EntryWidth, EntryWidth-2, "----------");
}
printf("\n");
for (i = 0; i < os_class_max; i++) {
for (j = 0; j < os_class_max; j++) {
if (i == j) {
matched += *(result + os_class_max * i + j);
} else {
missed += *(result + os_class_max * i + j);
}
printf(" %*d", EntryWidth-1, *(result + os_class_max * i + j));
}
printf(" (%c): " "class" " %s\n", 'a' + i, get_name(i));
}
printf("miss rate: %u%%\n", 100 * missed / (matched + missed));
}
struct osfp_result *test_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;
const char *matched;
if (db == NULL || json_str == NULL) {
goto exit;
}
ret = osfp_fingerprint_from_json(&fp, (char *)json_str);
if (ret != 0) {
goto exit;
}
matched = osfp_score_db_prefilter(db->score_db, &fp, &os_class_score);
if (matched == NULL) {
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, matched);
if (result == NULL) {
goto exit;
}
return result;
exit:
return NULL;
}
void test_data_prepare()
{
char *file_buffer;
if (test_file_path == NULL) {
file_buffer = osfp_score_db_read_file(data_file_path);
file_buffer = osfp_read_file(data_file_path);
if (file_buffer == NULL) {
osfp_log_error("read file: '%s'\n", data_file_path);
exit(1);
@@ -82,12 +216,14 @@ void test_miss_rate()
unsigned int other_count = 0;
unsigned int unknown_count = 0;
unsigned int identify_failed_count = 0;
unsigned int prefiltered_count = 0;
unsigned int prefiltered_wrong_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);
file_buffer = osfp_read_file(test_file_path);
if (file_buffer == NULL) {
osfp_log_error("read file: '%s'\n", test_file_path);
exit(1);
@@ -122,18 +258,33 @@ void test_miss_rate()
const char *fp_str = cJSON_PrintUnformatted(entry);
struct osfp_result *result = osfp_json_identify(osfp_db, fp_str);
struct osfp_fingerprint fp = {0};
osfp_fingerprint_from_json(&fp, (char*)fp_str);
char str_buf[2048] = "";
osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0);
fprintf(log_file_ptr, "%s\n", str_buf);
struct osfp_result *result = test_osfp_json_identify(osfp_db, fp_str);
if (result == NULL) {
identify_failed_count++;
continue;
}
testresult[result->likely_os_class][os_class]++;
if (result->details[result->likely_os_class].score == 100) {
prefiltered_count++;
}
if (os_class == result->likely_os_class) {
verified_count++;
osfp_result_free(result);
continue;
}
if (result->details[result->likely_os_class].score == 100) {
prefiltered_wrong_count++;
}
wrong_count++;
if (result->likely_os_class == OSFP_OS_CLASS_OTHERS) {
@@ -144,11 +295,12 @@ void test_miss_rate()
unknown_count++;
}
fprintf(log_file_ptr, "expect: %s, result: %s\n", os_class_json->valuestring, osfp_result_os_name_get(result));
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);
free(result_json);
} else {
fprintf(log_file_ptr, "result detail error:%p\n", result);
}
@@ -157,10 +309,16 @@ void test_miss_rate()
}
}
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("total %u, failed %u, pass %u, prefiltered %u (wrong: %u), wrong %u, other %u, unknown %u\n",
fingerprint_count, identify_failed_count, verified_count, prefiltered_count, prefiltered_wrong_count, wrong_count, other_count, unknown_count);
printf("miss rate: %d%%\n", 100 - (verified_count * 100 / fingerprint_count));
//printf("miss rate: %d%%\n", 100 - (verified_count * 100 / fingerprint_count));
testresult_class_merge();
print_confusion_matrix((unsigned int *)testresult, OSFP_OS_CLASS_MAX, osfp_os_class_id_to_name);
print_confusion_matrix((unsigned int *)testresult_merged, OSFP_OS_CLASS_MERGED_MAX, class_to_merged_name);
printf("details in: %s\n", LOG_FILE_PATH);