Using HP Fortify to Scan Android JNI C/C++ Code (CMake)
This is based on a hands on JNI example project JniExample.
HP Fortify does not directly support scanning Android JNI code base, we need some customise steps to tell HP Fortify source analyser to generate the FPR report.
Environment
- MacOS Catalina: 10.15.16
- HP Fortify Analyser Fortify_SCA_and_Apps_20.1.0
- Android NDK 21.0.6113669
- CMake 3.10.2.4988404
- Assume project name: JNI_EXAMPLE
Create directory fortify_tools
This directory is at the same level as JniExample/app/CMakeLists.txt
Create fortify_tools/fortify_cc
This is the gcc compiler wrapper which injects the sourceanalyzer
command.
#!/bin/sh
SOURCEANALYZER=$HPFORTIFY_HOME/sourceanalyzer
MEMORY="-Xmx7270M -Xms400M -Xss24M "
PROJECTID="JNI_EXAMPLE"
$SOURCEANALYZER $MEMORY -b $PROJECTID gcc $@
Create fortify_tools/fortify_cxx
This is the g++ compiler wrapper which injects the sourceanalyzer
command.
#!/bin/sh
SOURCEANALYZER=$HPFORTIFY_HOME/sourceanalyzer
MEMORY="-Xmx7270M -Xms400M -Xss24M "
PROJECTID="JNI_EXAMPLE"
$SOURCEANALYZER $MEMORY -b $PROJECTID g++ $@
Change directory to dir fortify_tools
cd JniExample/app/fortify_tools
Create hpfortify_build
directory
rm -rf hpfortify_build
mkdir hpfortify_build
Config cmake files using standalone CMake command
The standalone cmake build command can be found from file JniExample/app/.cxx/cmake/release/x86/build_command.txt
which is generated after running below command
➜ JniExample ✗ ./gradlew :app:externalNativeBuildRelease
build_command.txt
can be something similar as below
Executable : /Library/Android/sdk/cmake/3.10.2.4988404/bin/cmake
arguments :
-H<your-working-home-dir>/JniExample/app
-DCMAKE_CXX_FLAGS=-std=c++11 -frtti -fexceptions
-DCMAKE_FIND_ROOT_PATH=<your-working-home-dir>/JniExample/app/.cxx/cmake/release/prefab/x86/prefab
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_TOOLCHAIN_FILE=<your-working-home-dir>/Library/Android/sdk/ndk/21.0.6113669/build/cmake/android.toolchain.cmake
-DANDROID_ABI=x86
-DANDROID_NDK=<your-working-home-dir>/Library/Android/sdk/ndk/21.0.6113669
-DANDROID_PLATFORM=android-16
-DCMAKE_ANDROID_ARCH_ABI=x86
-DCMAKE_ANDROID_NDK=<your-working-home-dir>/Library/Android/sdk/ndk/21.0.6113669
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=<your-working-home-dir>/JniExample/app/build/intermediates/cmake/release/obj/x86
-DCMAKE_MAKE_PROGRAM=<your-working-home-dir>/Library/Android/sdk/cmake/3.10.2.4988404/bin/ninja
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_SYSTEM_VERSION=16
-B<your-working-home-dir>/JniExample/app/.cxx/cmake/release/x86
-GNinja
jvmArgs :
Build command args:
According to above to create the host specific CMake command for generating cmakefiles. E.g. for my case, it is as below:
➜ build-jni git:(master) ✗ /Users/myname/Library/Android/sdk/cmake/3.10.2.4988404/bin/cmake -H/Users/myname/JniExample/app \
-DCMAKE_CXX_FLAGS=-std=c++11 -frtti -fexceptions \
-DCMAKE_FIND_ROOT_PATH=/Users/myname/JniExample/app/build-jni/.cxx/cmake/release/prefab/x86/prefab \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=/Users/myname/Library/Android/sdk/ndk/21.0.6113669/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=x86 \
-DANDROID_NDK=/Users/myname/Library/Android/sdk/ndk/21.0.6113669 \
-DANDROID_PLATFORM=android-16 \
-DCMAKE_ANDROID_ARCH_ABI=x86 \
-DCMAKE_ANDROID_NDK=/Users/myname/Library/Android/sdk/ndk/21.0.6113669 \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/myname/JniExample/app/build-jni/intermediates/cmake/release/obj/x86 \
-DCMAKE_MAKE_PROGRAM=/Users/myname/Library/Android/sdk/cmake/3.10.2.4988404/bin/ninja \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_SYSTEM_VERSION=16 \
-B/Users/myname/JniExample/app/build-jni/.cxx/cmake/release/x86 \
-GNinja ..
Build project
cd JniExample/app/hpfortify_build
/Users/myname/Library/Android/sdk/cmake/3.10.2.4988404/bin/cmake --build .
Manually change compiler
-
Go to directory
/Users/myname/JniExample/app/hpfortify_build/CMakeFiles/3.10.2
-
Replace the C compiler inside
CMakeCCompiler.cmake
set(CMAKE_C_COMPILER "/Users/myname/JniExample/app/fortify_tools/fortify_cc")
-
Replace the CXX compiler inside
CMakeCXXCompiler.cmake
set(CMAKE_CXX_COMPILER "/Users/myname/JniExample/app/fortify_tools/fortify_cxx"))
Rebuild project
/Users/myname/Library/Android/sdk/cmake/3.10.2.4988404/bin/cmake --build .
Generate Fortify report
Applications/Fortify/Fortify_SCA_and_Apps_20.1.0/bin/sourceanalyzer -b JNI_EXAMPLE -scan -f JNI_EXAMPLE.fpr
Automation
All of above manual steps can be automatically done with script hpfortify_scan_native.sh.
Run below command:
➜ fortify_tools git:(master) ✗ sh hpfortify_scan_native.sh
You will see the FPR file is generated under directory app/fortify_tools/
.
Screenshot for the report
Some errors
But this error does not really matter with report generation, can simply ignore it.
-
ld: unknown option: –sysroot=/Users/myname/Library/Android/ndk/android-ndk-r20b/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
In short: The clang compiler provided by the NDK is using the linker from the linux host machine and not the linker provided by the NDK. The root cause is the --gcc-toolchain=
path is incorrect because of _ANDROID_TOOL_NAME
is not set via Determine-Compiler-NDK.cmake
because of the missing TOOLCHAIN_NAME
in ndk-bundle/build/core/toolchains/arm-linux-androideabi-clang/setup.mk
.