Merge pull request #3473 from YoungJules/feature/add_kgh_tools

Feature/add kgh tools
This commit is contained in:
Iceman
2026-09-06 21:01:40 +07:00
committed by GitHub
6 changed files with 885 additions and 37 deletions
+60 -30
View File
@@ -56,6 +56,13 @@ if (NOT SKIPPYTHON EQUAL 1)
pkg_search_module(PYTHON3EMBED QUIET ${PYTHON3_PKGCONFIG}-embed)
endif (NOT SKIPPYTHON EQUAL 1)
if (MINGW)
# Prefer static archives for single-exe deployments.
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(ZLIB_USE_STATIC_LIBS ON)
set(BZIP2_USE_STATIC_LIBS ON)
endif()
# If cross-compiled, we need to init source and build.
if (CMAKE_TOOLCHAIN_FILE)
if (ANDROID)
@@ -692,7 +699,26 @@ add_library(pm3rrg_rdv4 SHARED
${ADDITIONAL_SRC}
)
target_compile_definitions(pm3rrg_rdv4 PRIVATE LIBPM3)
add_library(pm3rrg_rdv4_static STATIC
${PM3_ROOT}/client/src/proxmark3.c
${PM3_ROOT}/client/src/guidummy.cpp
${TARGET_SOURCES}
${ADDITIONAL_SRC}
)
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.
@@ -700,17 +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)
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)
@@ -718,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}")
@@ -727,15 +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}
)
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)
@@ -759,22 +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})
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)
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
target_link_libraries(${target} PRIVATE pthread)
endforeach()
endif (NOT SKIPPTHREAD EQUAL 1)
if (NOT SKIPPYTHON EQUAL 1)
@@ -786,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()
+54 -7
View File
@@ -58,13 +58,13 @@ static bool legic_xor(uint8_t *data, uint16_t cardsize) {
return true;
}
static void legic_xor_with_crc(uint8_t *data, uint16_t cardsize, uint8_t crc) {
void legic_xor_with_crc(uint8_t *data, uint16_t cardsize, uint8_t crc) {
for (uint16_t i = 22; i < cardsize; i++) {
data[i] ^= crc;
}
}
static bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) {
bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) {
// Segment headers are parsed from offset 22 everywhere else in LEGIC decoding.
size_t start = 22;
bool found_segment = false;
@@ -96,7 +96,7 @@ static bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, co
return true;
}
static bool legic_clone_update_kgh_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) {
bool legic_clone_update_kgh_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) {
// Decoded segmented payload starts at byte 22.
size_t start = 22;
bool found_kgh = false;
@@ -142,7 +142,7 @@ static bool legic_clone_update_kgh_crcs(uint8_t *data, size_t bytes_read, const
return true;
}
static int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *data, size_t bytes_read, const char *verb) {
int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *data, size_t bytes_read, const char *verb) {
PrintAndLogEx(SUCCESS, "%s", verb);
// fast push mode
@@ -183,21 +183,68 @@ static int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *
PrintAndLogEx(NORMAL, "");
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Failed writing tag");
PrintAndLogEx(WARNING, "Failed writing tag at offset %zu len %zu. Status: %d", i, len, resp.status);
g_conn.block_after_ACK = false;
return PM3_ERFTRANS;
}
PrintAndLogEx(SUCCESS, "Wrote chunk [offset %zu | len %zu | total %zu", i, len, i + len);
PrintAndLogEx(SUCCESS, "Wrote chunk [offset %zu | len %zu | end %zu of %zu]", i, len, i + len, bytes_read);
}
g_conn.block_after_ACK = false;
return PM3_SUCCESS;
}
static int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read) {
int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read) {
return legic_write_bytes_to_tag(7, 0x55, dump, bytes_read, "Restoring to card");
}
int legic_migrate_dump(uint8_t *dump, size_t bytes_read, bool rewrite_kgh, const uint8_t dcf[2], bool allow_dcf) {
if (dump == NULL || bytes_read <= 22) {
return PM3_EINVARG;
}
if (dcf != NULL && !allow_dcf) {
return PM3_EINVARG;
}
legic_card_select_t card;
if (legic_get_type(&card) != PM3_SUCCESS) {
return PM3_ESOFT;
}
if (card.cardsize < bytes_read) {
return PM3_EFILE;
}
uint8_t target_uid[4] = {0};
memcpy(target_uid, card.uid, sizeof(target_uid));
uint8_t target_mcc = (uint8_t)CRC8Legic(target_uid, sizeof(target_uid));
if (!legic_clone_update_segment_crcs(dump, bytes_read, target_uid)) {
return PM3_EFAILED;
}
if (rewrite_kgh && !legic_clone_update_kgh_crcs(dump, bytes_read, target_uid)) {
return PM3_EFAILED;
}
memcpy(dump, target_uid, sizeof(target_uid));
dump[4] = target_mcc;
legic_xor_with_crc(dump, bytes_read, dump[4]);
int write_res = legic_write_dump_to_tag(dump, bytes_read);
if (write_res != PM3_SUCCESS) {
return write_res;
}
if (dcf != NULL) {
uint8_t dcf_dump[7] = {0};
memcpy(dcf_dump + 5, dcf, 2);
return legic_write_bytes_to_tag(5, 0x55, dcf_dump, sizeof(dcf_dump), "Applying explicit DCF update");
}
return PM3_SUCCESS;
}
static int CmdLegicMigrate(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf legic migrate",
+6
View File
@@ -31,5 +31,11 @@ int legic_get_type(legic_card_select_t *card);
void legic_chk_iv(uint32_t *iv);
void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes);
int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uint16_t *outlen);
int legic_migrate_dump(uint8_t *dump, size_t bytes_read, bool rewrite_kgh, const uint8_t dcf[2], bool allow_dcf);
void legic_xor_with_crc(uint8_t *data, uint16_t cardsize, uint8_t crc);
bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]);
bool legic_clone_update_kgh_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]);
int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *data, size_t bytes_read, const char *verb);
int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read);
#endif
+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()
+71
View File
@@ -0,0 +1,71 @@
# legic_kgh_tool
## Running
`legic_kgh_tool` reads KGH LEGIC card information, badge numbers, and stamps,
and writes the supported KGH card layout.
Examples:
```bash
./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:
- `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 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` 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.
## Building in ProxSpace (for Windows)
Do this in ProxSpace:
```bash
cd ~/proxmark3/tools/kgh
rm -rf build-lite
cmake -G "MSYS Makefiles" -S . -B build-lite \
-DSKIPPYTHON=1 \
-DSKIPREADLINE=1 \
-DSKIPJANSSONSYSTEM=1 \
-DSKIPWHEREAMISYSTEM=1 \
-DSKIPBT=1
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 tools/kgh
rm -rf build-linux
cmake -S . -B build-linux \
-DSKIPPYTHON=1 \
-DSKIPREADLINE=1 \
-DSKIPJANSSONSYSTEM=1 \
-DSKIPWHEREAMISYSTEM=1 \
-DSKIPBT=1
cmake --build build-linux --target legic_kgh_tool -j
ldd build-linux/legic_kgh_tool
```
## Static Link Notes
- `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.
File diff suppressed because it is too large Load Diff