Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/deploy_libhc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Publish libhc debian package

on:
workflow_dispatch:
push:
branches:
- main

jobs:
create_release:
runs-on: ubuntu-latest
outputs:
v-version: ${{ steps.version.outputs.v-version }}

steps:
- name: Get next version
uses: reecetech/version-increment@2024.10.1
id: version
with:
release_branch: main
use_api: true
increment: patch

build_and_publish:
needs: create_release
runs-on: ubuntu-latest

steps:
- name: Install dependencies
run: |
sudo apt-get -qq update -y
sudo apt-get -qq install cmake

- name: Check out
uses: actions/checkout@v5

- name: Build package
run: |
cmake -B"./build" \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_BUILD_TYPE=Release \
-DLIBHC_VERSION=${{ needs.create_release.outputs.v-version }}
cd build
cpack -G DEB

- name: Upload debian package to release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ needs.create_release.outputs.v-version }}
draft: false
generate_release_notes: true
prerelease: false
files: |
build/libhc-dev*.deb
57 changes: 57 additions & 0 deletions .github/workflows/test_libhc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Test Hypercalls
on:
workflow_dispatch:
pull_request:
branches:
- main

jobs:
test_hypercalls:
runs-on: ubuntu-latest
strategy:
matrix:
arch:
- i386
- x86_64
- arm
- aarch64

steps:
- name: Install dependencies
run: |
sudo apt-get -qq update -y
sudo apt-get -qq install cmake

- name: Check out
uses: actions/checkout@v5

- name: Download for cross-compiling
run: |
# i386 compile
sudo apt-get -y install gcc-multilib
# arm compile
sudo apt-get -y install gcc-arm-linux-gnueabihf
# aarch64 compile
sudo apt-get -y install gcc-aarch64-linux-gnu

- name: Install PANDA
run: |
ubuntu_version=$(lsb_release -rs)
curl -LJ -o /tmp/pandare_${ubuntu_version}.deb https://github.com/panda-re/panda/releases/latest/download/pandare_${ubuntu_version}.deb
sudo apt-get -y install /tmp/pandare_${ubuntu_version}.deb
pip install pandare

# Adding the rm -rf to make sure the build directory is clean before building on each architecture
- name: Build binaries for ${{ matrix.arch }}
run: |
rm -rf build
cmake -B"./build" \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_BUILD_TYPE=Release \
-DARCH=${{ matrix.arch }}
cmake --build "./build" --parallel "$(nproc)" --config Release

- name: Run tests
run: |
cd tests
python3 test_libhc.py --arch ${{ matrix.arch }} --ci
34 changes: 34 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# No panda recordings
recording-rr-*
*.plog
*.json

# No ISO
*.iso

# Binaries
test_hc_i386
test_hc_x86_64
test_hc_arm
test_hc_aarch64

# No IDE
.idea

# ignore build folder and any debian packages
build
*.deb

# Got this from here, https://github.com/github/gitignore/blob/main/CMake.gitignore
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json
80 changes: 80 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
cmake_minimum_required(VERSION 3.15)

set (CMAKE_CONFIGURATION_TYPES "Release" CACHE STRING "Configs" FORCE)
set(CMAKE_VERBOSE_MAKEFILE OFF)

# Read version number from command line argument
if(DEFINED LIBHC_VERSION)
string(REGEX MATCH "v?([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ ${LIBHC_VERSION})
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1})
set(PROJECT_VERSION_MINOR ${CMAKE_MATCH_2})
set(PROJECT_VERSION_PATCH ${CMAKE_MATCH_3})
else()
set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "0")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(PROJECT_VERSION_MAJOR "0")
set(PROJECT_VERSION_MINOR "0")
set(PROJECT_VERSION_PATCH "0")
endif()

# --- Options ---
# Architecture can be: x86_64 (default), i386, arm, aarch64
set(ARCH "x86_64" CACHE STRING "Target architecture for compilation")

# --- Architecture Specific Flags ---
# We use a list for flags to ensure CMake passes them as separate arguments to the compiler
set(ARCH_FLAGS "")

if(ARCH STREQUAL "aarch64")
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
list(APPEND ARCH_FLAGS "-static" "-O0" "-g" "-gdwarf-2" "-fno-stack-protector")
message(STATUS "Configuring for AArch64 (Cross-compile)")
elseif(ARCH STREQUAL "arm")
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
list(APPEND ARCH_FLAGS "-marm" "-static" "-O0" "-g" "-gdwarf-2" "-fno-stack-protector")
message(STATUS "Configuring for ARM 32-bit (Cross-compile)")
elseif(ARCH STREQUAL "i386")
# Separate flags are required: -m32 for 32-bit, -static for PANDA portability
list(APPEND ARCH_FLAGS "-m32" "-static" "-O0" "-g" "-gdwarf-2" "-fno-stack-protector")
message(STATUS "Configuring for i386 32-bit (Requires gcc-multilib)")
else()
list(APPEND ARCH_FLAGS "-static" "-O0" "-g" "-gdwarf-2" "-fno-stack-protector")
message(STATUS "Configuring for Native x86_64")
endif()

project(libhc VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} LANGUAGES C CXX)

# --- Build Target ---
add_executable(test_hc tests/test_hc.c)

# Include the headers from the local directory
target_include_directories(test_hc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)

# Apply static flags, you need this for PANDA
target_compile_options(test_hc PRIVATE ${ARCH_FLAGS} -Wall -Wextra)
if(ARCH_FLAGS MATCHES "-static")
target_link_options(test_hc PRIVATE ${ARCH_FLAGS})
endif()

# Set output name based on architecture and place the binary in the tests/ directory
set_target_properties(test_hc PROPERTIES
OUTPUT_NAME "test_hc_${ARCH}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/tests"
)

# --- Installation ---
include(GNUInstallDirs)

# Install the header file, we want this /usr/include/panda/hypercall.h
install(FILES include/hypercall.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/panda)

install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE"
DESTINATION "${CMAKE_INSTALL_DOCDIR}"
RENAME "copyright")

include(CPackConfig.txt)
30 changes: 30 additions & 0 deletions CPackConfig.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
set(CPACK_PACKAGE_NAME "libhc-dev")

set(CPACK_PACKAGE_VENDOR "pandare")

set(CPACK_PACKAGE_DESCRIPTION "Cross-platform assembly hypercall wrappers for PANDA")

set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Cross-platform assembly hypercall wrappers for PANDA")

set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})

set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all")

set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Luke Craig <luke.craig@mit.edu>")

set(CPACK_DEBIAN_PACKAGE_SECTION "devel")

set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")

set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${PROJECT_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")

# Homepage
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/panda-re/libhc")

# Source ignore files
set(CPACK_SOURCE_IGNORE_FILES "^${CMAKE_BINARY_DIR};/\\\\.gitignore;/\\\\.svn;\\\\.swp$;\\\\.#;/#;.*~")

# Licensing information
set(CPACK_DEBIAN_PACKAGE_LICENSE "MIT")

include(CPack)
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Libhc
Use this header inconjuction with [PIRATE](https://github.com/panda-re/panda/blob/dev/panda/plugins/taint2/PIRATE.md) for making your hypercalls. For an example, see how [LAVA](https://github.com/panda-re/lava/blob/master/tools/include/pirate_mark_lava.h) and [PANDA](https://github.com/panda-re/panda/blob/dev/panda/plugins/pri_taint/pri_taint.cpp) use this library for an architecture neutral hypercall system to taint bytes.

This has the advantage of once you get this working in one platform, it is much easier to make your plug-in work in other archtictures.

Please see [this](https://github.com/panda-re/libhc/issues/5) for a better understanding how the mapping between arguments and registers is decided.

## Creating the package locally
To create the debian package use the following steps, the package would be under the `build` folder.

```bash
cmake -B"./build" \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_BUILD_TYPE=Release
pushd build
cpack -G DEB
popd
```
11 changes: 7 additions & 4 deletions hypercall.h → include/hypercall.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#ifndef __HYPERCALL_H__
#define __HYPERCALL_H__

#define DECLARE_REGISTER(x,y,z) register unsigned long reg##x asm(#y) = z;
#define COMMA ,
Expand Down Expand Up @@ -115,23 +117,23 @@
#else
#error "not supported"
#endif
static inline unsigned long igloo_hypercall(unsigned long num, unsigned long arg1){
static inline unsigned long igloo_hypercall(unsigned long num, unsigned long arg1) {
REGISTER2
ASM()
RETURN
}
static inline unsigned long igloo_hypercall2(unsigned long num, unsigned long arg1, unsigned long arg2){
static inline unsigned long igloo_hypercall2(unsigned long num, unsigned long arg1, unsigned long arg2) {
REGISTER3
ASM(COMMA"r"(reg2))
RETURN
}

static inline unsigned long igloo_hypercall3(unsigned long num, unsigned long arg1, unsigned long arg2, unsigned long arg3){
static inline unsigned long igloo_hypercall3(unsigned long num, unsigned long arg1, unsigned long arg2, unsigned long arg3) {
REGISTER4
ASM(COMMA "r"(reg2)COMMA "r"(reg3))
RETURN
}
static inline unsigned long igloo_hypercall4(unsigned long num, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4){
static inline unsigned long igloo_hypercall4(unsigned long num, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) {
REGISTER5
ASM(COMMA "r"(reg2)COMMA "r"(reg3)COMMA "r"(reg4))
RETURN
Expand All @@ -155,3 +157,4 @@ static inline int hc(int hc_type, void **s,int len) {
return ret;
}
#endif
#endif // __HYPERCALL_H__
22 changes: 22 additions & 0 deletions tests/test_hc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <stdio.h>
#include "hypercall.h"


int main() {
int magic = 6767;
printf("[*] Attempting hypercall %d...\n", magic);
printf("[!] NOTE: This will FAIL if run outside of PANDA/QEMU!\n");

// In PANDA, the handler will set the return value to 6767 if successful
int result = (int) igloo_hypercall4(magic, 42, 43, 44, 45);

printf("[+] Hypercall finished. Result: %d\n", result);

if (result == magic) {
printf("[+] Success!\n");
return 0;
} else {
printf("[-] Failure: Expected %d, got %d\n", magic, result);
return 1;
}
}
Loading