Moved the new tool to tools/kgh and renamed it legic_kgh_tool. Added some validation so we don't return random bytes from non-kgh cards. Applied some styling to the new code. Added GPL header to new source file. Cleaned up the static build.

This commit is contained in:
Julian Isaac
2026-09-04 23:14:22 +02:00
parent 1d5d09b073
commit 71ca59a8ad
4 changed files with 135 additions and 112 deletions
+46 -82
View File
@@ -523,26 +523,17 @@ endif(NOT SKIPREADLINE EQUAL 1)
if (BZIP2_FOUND)
set(ADDITIONAL_DIRS ${BZIP2_INCLUDE_DIRS} ${ADDITIONAL_DIRS})
if (MINGW)
set(BZIP2_LIBRARIES /mingw64/lib/libbz2.a)
endif()
set(ADDITIONAL_LNK ${BZIP2_LIBRARIES} ${ADDITIONAL_LNK})
endif (BZIP2_FOUND)
if (LZ4_FOUND)
set(ADDITIONAL_DIRS ${LZ4_INCLUDE_DIRS} ${ADDITIONAL_DIRS})
if (MINGW)
set(LZ4_LIBRARIES /mingw64/lib/liblz4.a)
endif()
set(ADDITIONAL_LNK ${LZ4_LIBRARIES} ${ADDITIONAL_LNK})
endif (LZ4_FOUND)
if (ZLIB_FOUND)
add_definitions("-DHAVE_ZLIB")
set(ADDITIONAL_DIRS ${ZLIB_INCLUDE_DIRS} ${ADDITIONAL_DIRS})
if (MINGW)
set(ZLIB_LIBRARIES /mingw64/lib/libz.a)
endif()
set(ADDITIONAL_LNK ${ZLIB_LIBRARIES} ${ADDITIONAL_LNK})
endif (ZLIB_FOUND)
@@ -715,25 +706,19 @@ add_library(pm3rrg_rdv4_static STATIC
${ADDITIONAL_SRC}
)
add_executable(kgh_tools
${PM3_ROOT}/client/experimental_lib/example_c/legic_migrate_tool.c
)
set_target_properties(kgh_tools PROPERTIES OUTPUT_NAME "kgh_tools")
if (MINGW)
target_link_options(kgh_tools PRIVATE -static -static-libgcc)
endif()
target_link_libraries(kgh_tools PRIVATE pm3rrg_rdv4_static)
target_include_directories(kgh_tools PRIVATE
${PM3_ROOT}/common
${PM3_ROOT}/client/include
${PM3_ROOT}/client/src
${PM3_ROOT}/include
)
target_compile_definitions(pm3rrg_rdv4 PRIVATE LIBPM3)
target_compile_definitions(pm3rrg_rdv4_static PRIVATE LIBPM3)
set(PM3_LIBRARY_TARGETS pm3rrg_rdv4 pm3rrg_rdv4_static)
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
target_compile_definitions(${target} PRIVATE LIBPM3)
target_compile_options(${target} PUBLIC -Wall -Werror -O3)
target_include_directories(${target} PRIVATE
${PM3_ROOT}/common
${PM3_ROOT}/common_fpga
${PM3_ROOT}/include
${PM3_ROOT}/client/src
${PM3_ROOT}/client/include
${ADDITIONAL_DIRS}
)
endforeach()
if (UNIX AND NOT APPLE)
# Fail the build if a client source file is missing from TARGET_SOURCES.
@@ -741,18 +726,22 @@ if (UNIX AND NOT APPLE)
set_property(TARGET pm3rrg_rdv4 APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--no-undefined")
endif()
target_compile_options(pm3rrg_rdv4 PUBLIC -Wall -Werror -O3)
target_compile_options(pm3rrg_rdv4_static PUBLIC -Wall -Werror -O3)
if (EMBED_READLINE)
if (NOT SKIPREADLINE EQUAL 1)
add_dependencies(pm3rrg_rdv4 ncurses readline)
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
add_dependencies(${target} ncurses readline)
endforeach()
endif (NOT SKIPREADLINE EQUAL 1)
endif (EMBED_READLINE)
if (EMBED_BZIP2)
add_dependencies(pm3rrg_rdv4 bzip2)
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
add_dependencies(${target} bzip2)
endforeach()
endif (EMBED_BZIP2)
if (EMBED_LZ4)
add_dependencies(pm3rrg_rdv4 lz4)
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
add_dependencies(${target} lz4)
endforeach()
endif (EMBED_LZ4)
if (MINGW)
@@ -760,7 +749,9 @@ if (MINGW)
# and setting _ISOC99_SOURCE sets internally __USE_MINGW_ANSI_STDIO=1
# FTR __USE_MINGW_ANSI_STDIO seems deprecated in Mingw32
# but not Mingw64 https://fr.osdn.net/projects/mingw/lists/archive/users/2019-January/000199.html
target_compile_definitions(pm3rrg_rdv4 PRIVATE _ISOC99_SOURCE)
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
target_compile_definitions(${target} PRIVATE _ISOC99_SOURCE)
endforeach()
set(CMAKE_C_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_CXX_FLAGS}")
@@ -769,24 +760,6 @@ if (MINGW)
set(ADDITIONAL_LNK ws2_32 ${ADDITIONAL_LNK})
endif (MINGW)
target_include_directories(pm3rrg_rdv4 PRIVATE
${PM3_ROOT}/common
${PM3_ROOT}/common_fpga
${PM3_ROOT}/include
${PM3_ROOT}/client/src
${PM3_ROOT}/client/include
${ADDITIONAL_DIRS}
)
target_include_directories(pm3rrg_rdv4_static PRIVATE
${PM3_ROOT}/common
${PM3_ROOT}/common_fpga
${PM3_ROOT}/include
${PM3_ROOT}/client/src
${PM3_ROOT}/client/include
${ADDITIONAL_DIRS}
)
if (NOT APPLE)
# required for Raspberry Pi, but breaks with clang (OSX). Need to be at the end of the linker line.
set(ADDITIONAL_LNK ${ADDITIONAL_LNK} -Wl,--as-needed -latomic -Wl,--no-as-needed)
@@ -810,37 +783,26 @@ if (NOT WHEREAMI_FOUND)
set(ADDITIONAL_LNK pm3rrg_rdv4_whereami ${ADDITIONAL_LNK})
endif (NOT WHEREAMI_FOUND)
target_link_libraries(pm3rrg_rdv4 PRIVATE
m
pm3rrg_rdv4_mbedtls
pm3rrg_rdv4_cliparser
pm3rrg_rdv4_lua
pm3rrg_rdv4_tinycbor
pm3rrg_rdv4_amiibo
pm3rrg_rdv4_reveng
pm3rrg_rdv4_hardnested
pm3rrg_rdv4_id48
pm3rrg_rdv4_mqtt
pm3rrg_rdv4_vec
${ADDITIONAL_LNK})
target_link_libraries(pm3rrg_rdv4_static PRIVATE
m
pm3rrg_rdv4_mbedtls
pm3rrg_rdv4_cliparser
pm3rrg_rdv4_lua
pm3rrg_rdv4_tinycbor
pm3rrg_rdv4_amiibo
pm3rrg_rdv4_reveng
pm3rrg_rdv4_hardnested
pm3rrg_rdv4_id48
pm3rrg_rdv4_mqtt
pm3rrg_rdv4_vec
${ADDITIONAL_LNK})
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
target_link_libraries(${target} PRIVATE
m
pm3rrg_rdv4_mbedtls
pm3rrg_rdv4_cliparser
pm3rrg_rdv4_lua
pm3rrg_rdv4_tinycbor
pm3rrg_rdv4_amiibo
pm3rrg_rdv4_reveng
pm3rrg_rdv4_hardnested
pm3rrg_rdv4_id48
pm3rrg_rdv4_mqtt
pm3rrg_rdv4_vec
${ADDITIONAL_LNK})
endforeach()
if (NOT SKIPPTHREAD EQUAL 1)
target_link_libraries(pm3rrg_rdv4 PRIVATE pthread)
target_link_libraries(pm3rrg_rdv4_static PRIVATE pthread)
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
target_link_libraries(${target} PRIVATE pthread)
endforeach()
endif (NOT SKIPPTHREAD EQUAL 1)
if (NOT SKIPPYTHON EQUAL 1)
@@ -852,4 +814,6 @@ if (NOT SKIPPYTHON EQUAL 1)
endif (PYTHON3EMBED_FOUND OR PYTHON3_FOUND)
endif (NOT SKIPPYTHON EQUAL 1)
target_link_directories(pm3rrg_rdv4 PRIVATE ${ADDITIONAL_LNKDIRS})
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
target_link_directories(${target} PRIVATE ${ADDITIONAL_LNKDIRS})
endforeach()
+35
View File
@@ -0,0 +1,35 @@
#-----------------------------------------------------------------------------
# Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# See LICENSE.txt for the text of the license.
#-----------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.10)
project(legic_kgh_tool)
set(PM3_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..)
add_subdirectory(${PM3_ROOT}/client/experimental_lib ${CMAKE_BINARY_DIR}/experimental_lib)
add_executable(legic_kgh_tool legic_kgh_tool.c)
target_link_libraries(legic_kgh_tool PRIVATE pm3rrg_rdv4_static)
target_include_directories(legic_kgh_tool PRIVATE
${PM3_ROOT}/common
${PM3_ROOT}/client/include
${PM3_ROOT}/client/src
${PM3_ROOT}/include
)
if (MINGW)
target_link_options(legic_kgh_tool PRIVATE -static -static-libgcc)
endif()
@@ -1,17 +1,18 @@
# kgh_tools
# legic_kgh_tool
## Running
`kgh_tools` is a small CLI wrapper around the LEGIC migration helpers.
`legic_kgh_tool` reads KGH LEGIC card information, badge numbers, and stamps,
and writes the supported KGH card layout.
Examples:
```bash
./kgh_tools COM13 read_badge_number
./kgh_tools COM13 read_info
./kgh_tools COM13 read_stamp
./kgh_tools COM13 read_stamp -o stamp.bin
./kgh_tools COM13 write_card 600001 --stamp-file stamp.bin
./legic_kgh_tool COM13 read_badge_number
./legic_kgh_tool COM13 read_info
./legic_kgh_tool COM13 read_stamp
./legic_kgh_tool COM13 read_stamp -o stamp.bin
./legic_kgh_tool COM13 write_card 600001 --stamp-file stamp.bin
```
Commands:
@@ -19,15 +20,14 @@ Commands:
- `read_badge_number` prints the badge number only.
- `read_info` prints one-line JSON.
- `read_stamp` prints the stamp as 8 hex chars, or writes raw 4 bytes with `-o/--output-file`.
- `write_card <badge> (--stamp-file <path> | --stamp-hex <8hex>)` writes the card.
- `write_card <badge> (--stamp-file <path> | --stamp-hex <8hex>)` writes the supported KGH layout.
Notes:
- Windows ProxSpace examples use `COM13`.
- The stamp is 4 bytes and specific to your site.
It can be specified as a .bin (use .e.g hexedit to create it)
Or as a command-line option (less secure) as --stamp-hex (e.g.) 003EF417
You can find the stamp on any card in use at your site by using read_info (see above)
It can be specified as a `.bin` file or as `--stamp-hex` (for example, `003EF417`).
- You can retrieve the stamp from a supported KGH card with `read_info` or `read_stamp`.
- Linux examples usually use `/dev/ttyACM0` or similar.
- `--verbose` can be useful for `read_badge_number` and `write_card`, if they fail for mysterious reasons.
@@ -36,42 +36,36 @@ Notes:
Do this in ProxSpace:
```bash
cd ~/proxmark3/client/experimental_lib
cd ~/proxmark3/tools/kgh
rm -rf build-lite
cmake -G "MSYS Makefiles" -S . -B build-lite \
-DSKIPPYTHON=1 \
-DSKIPREADLINE=1 \
-DSKIPJANSSYSTEM=1 \
-DSKIPJANSSONSYSTEM=1 \
-DSKIPWHEREAMISYSTEM=1 \
-DSKIPBT=1
cmake --build build-lite --target kgh_tools -j
objdump -p build-lite/kgh_tools.exe | grep -i "DLL Name"
cmake --build build-lite --target legic_kgh_tool -j
objdump -p build-lite/legic_kgh_tool.exe | grep -i "DLL Name"
```
## Building Linux
```bash
cd client/experimental_lib
cd tools/kgh
rm -rf build-linux
cmake -S . -B build-linux \
-DSKIPPYTHON=1 \
-DSKIPREADLINE=1 \
-DSKIPJANSSYSTEM=1 \
-DSKIPJANSSONSYSTEM=1 \
-DSKIPWHEREAMISYSTEM=1 \
-DSKIPBT=1
cmake --build build-linux --target kgh_tools -j
ldd build-linux/kgh_tools
cmake --build build-linux --target legic_kgh_tool -j
ldd build-linux/legic_kgh_tool
```
## Static Link Notes
- `kgh_tools` is linked against `pm3rrg_rdv4_static`.
- `legic_kgh_tool` is linked against `pm3rrg_rdv4_static`.
- On MinGW, CMake adds `-static -static-libgcc` for the target.
- Linux builds are not the same as the ProxSpace single-EXE deployment.
- If CMakeCache paths change between ProxSpace and a native shell, delete the build dir and reconfigure.
## Existing Helper Scripts
- `01make_lib.sh` creates `build/`, runs `cmake ..`, then `make -j`.
- `01make_lib_continue.sh` reruns `make -j` in `build/`.
- `00make_swig.sh` regenerates the SWIG wrappers before building the library.
@@ -1,3 +1,19 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
@@ -238,6 +254,14 @@ static void free_legic_read_result(legic_read_result_t *res) {
memset(&res->card, 0, sizeof(res->card));
}
static bool is_kgh_card(const uint8_t *dump, uint16_t len) {
return len >= sizeof(k_legic_template_head) &&
dump[5] == 0x60 && dump[6] == 0xEA &&
dump[7] == 0x9F && dump[8] == 0xFF &&
dump[22] == 0x0D && dump[23] == 0xC0 &&
dump[24] == 0x08 && dump[25] == 0x00;
}
static const char *classify_legic_card(const uint8_t *dump, uint16_t len) {
if (len < 9) {
return "unknown";
@@ -252,7 +276,7 @@ static const char *classify_legic_card(const uint8_t *dump, uint16_t len) {
return "IAM";
}
if ((dump[7] & 0x0F) == 0x0F && dump[8] == 0xFF) {
if (is_kgh_card(dump, len)) {
return "KGH";
}
@@ -519,7 +543,11 @@ static int read_badge_from_card(uint32_t *badge_out) {
dump[i] ^= crc;
}
bool ok = decode_badge_bcd(&dump[31], badge_out);
bool is_kgh = is_kgh_card(dump, datalen);
bool ok = is_kgh && decode_badge_bcd(&dump[31], badge_out);
if (!is_kgh) {
fprintf(stderr, "not a supported KGH card\n");
}
free(dump);
g_printAndLog = prev_print;
return ok ? 0 : 1;
@@ -563,7 +591,7 @@ int main(int argc, char *argv[]) {
legic_read_result_t res;
if (read_legic_dump(&res, verbose)) {
uint8_t stamp[4] = {0};
if (extract_stamp(res.dump, res.len, stamp)) {
if (is_kgh_card(res.dump, res.len) && extract_stamp(res.dump, res.len, stamp)) {
if (has_output) {
if (save_stamp_file(output_file, stamp)) {
rc = EXIT_SUCCESS;
@@ -574,6 +602,8 @@ int main(int argc, char *argv[]) {
printf("%02X%02X%02X%02X\n", stamp[0], stamp[1], stamp[2], stamp[3]);
rc = EXIT_SUCCESS;
}
} else {
fprintf(stderr, "not a supported KGH card\n");
}
free_legic_read_result(&res);
} else if (verbose) {
@@ -614,7 +644,7 @@ int main(int argc, char *argv[]) {
uint8_t dump[35] = {0};
build_migration_dump(badge, stamp, dump, sizeof(dump));
if (legic_migrate_dump(dump, sizeof(dump), true, (const uint8_t[2]){0x60, 0xEA}, true) == PM3_SUCCESS) {
if (legic_migrate_dump(dump, sizeof(dump), true, (const uint8_t[2]) {0x60, 0xEA}, true) == PM3_SUCCESS) {
rc = EXIT_SUCCESS;
} else {
fprintf(stderr, "failed to write card\n");