|
42 | 42 | #include <string.h> |
43 | 43 | #include <errno.h> |
44 | 44 | #include <stdlib.h> |
| 45 | +#include <stdbool.h> |
45 | 46 | #include <termios.h> |
46 | 47 | #include <unistd.h> |
47 | 48 | #include <stdio.h> |
@@ -935,6 +936,114 @@ int k5_prepare(int fd) { |
935 | 936 | return(1); |
936 | 937 | } |
937 | 938 |
|
| 939 | +#define CRC_POLY16 0x1021 |
| 940 | +// slow bit-bang based |
| 941 | +// https://github.com/OneOfEleven/k5prog-win/blob/a8af838b91bf17027179881accc1701949d66a0b/Unit1.cpp#L1192C2-L1192C2 |
| 942 | +uint16_t crc16(const uint8_t *data, const int size) |
| 943 | +{ |
| 944 | + uint16_t crc = 0; |
| 945 | + if (data != NULL && size > 0) |
| 946 | + { |
| 947 | + for (int i = 0; i < size; i++) |
| 948 | + { |
| 949 | + crc ^= (uint16_t)data[i] << 8; |
| 950 | + for (unsigned int k = 8; k > 0; k--) |
| 951 | + crc = (crc & 0x8000) ? (crc << 1) ^ CRC_POLY16 : crc << 1; |
| 952 | + } |
| 953 | + } |
| 954 | + return crc; |
| 955 | +} |
| 956 | + |
| 957 | +// (de)obfuscate firmware data |
| 958 | +// https://github.com/OneOfEleven/k5prog-win/blob/a8af838b91bf17027179881accc1701949d66a0b/Unit1.cpp#L1456 |
| 959 | +void k5_xor_firmware(uint8_t *data, const int len) |
| 960 | +{ |
| 961 | + static const uint8_t xor_pattern[] = |
| 962 | + { |
| 963 | + 0x47, 0x22, 0xc0, 0x52, 0x5d, 0x57, 0x48, 0x94, 0xb1, 0x60, 0x60, 0xdb, 0x6f, 0xe3, 0x4c, 0x7c, |
| 964 | + 0xd8, 0x4a, 0xd6, 0x8b, 0x30, 0xec, 0x25, 0xe0, 0x4c, 0xd9, 0x00, 0x7f, 0xbf, 0xe3, 0x54, 0x05, |
| 965 | + 0xe9, 0x3a, 0x97, 0x6b, 0xb0, 0x6e, 0x0c, 0xfb, 0xb1, 0x1a, 0xe2, 0xc9, 0xc1, 0x56, 0x47, 0xe9, |
| 966 | + 0xba, 0xf1, 0x42, 0xb6, 0x67, 0x5f, 0x0f, 0x96, 0xf7, 0xc9, 0x3c, 0x84, 0x1b, 0x26, 0xe1, 0x4e, |
| 967 | + 0x3b, 0x6f, 0x66, 0xe6, 0xa0, 0x6a, 0xb0, 0xbf, 0xc6, 0xa5, 0x70, 0x3a, 0xba, 0x18, 0x9e, 0x27, |
| 968 | + 0x1a, 0x53, 0x5b, 0x71, 0xb1, 0x94, 0x1e, 0x18, 0xf2, 0xd6, 0x81, 0x02, 0x22, 0xfd, 0x5a, 0x28, |
| 969 | + 0x91, 0xdb, 0xba, 0x5d, 0x64, 0xc6, 0xfe, 0x86, 0x83, 0x9c, 0x50, 0x1c, 0x73, 0x03, 0x11, 0xd6, |
| 970 | + 0xaf, 0x30, 0xf4, 0x2c, 0x77, 0xb2, 0x7d, 0xbb, 0x3f, 0x29, 0x28, 0x57, 0x22, 0xd6, 0x92, 0x8b |
| 971 | + }; |
| 972 | + |
| 973 | + if (data == NULL || len <= 0) |
| 974 | + return; |
| 975 | + |
| 976 | + for (int i = 0; i < len; i++) |
| 977 | + data[i] ^= xor_pattern[i % sizeof(xor_pattern)]; |
| 978 | +} |
| 979 | + |
| 980 | +// https://github.com/OneOfEleven/k5prog-win/blob/a8af838b91bf17027179881accc1701949d66a0b/Unit1.cpp#L2663 |
| 981 | +bool decrypt(unsigned char *flash, int *flash_size) { |
| 982 | + |
| 983 | + bool encrypted = true; |
| 984 | + |
| 985 | + const uint16_t crc1 = crc16(&flash[0], *flash_size - 2); |
| 986 | + const uint16_t crc2 = ((uint16_t)flash[*flash_size - 1] << 8) | ((uint16_t)flash[*flash_size - 2] << 0); |
| 987 | + |
| 988 | + if (flash[ 2] == 0x00 && |
| 989 | + flash[ 3] == 0x20 && |
| 990 | + flash[ 6] == 0x00 && |
| 991 | + flash[10] == 0x00 && |
| 992 | + flash[14] == 0x00) { |
| 993 | + encrypted = false; |
| 994 | + } |
| 995 | + |
| 996 | + if (encrypted && crc1 == crc2) |
| 997 | + { // the file appears to be encrypted |
| 998 | + |
| 999 | + // drop the 16-bit CRC |
| 1000 | + *flash_size = *flash_size - 2; |
| 1001 | + |
| 1002 | + // decrypt it |
| 1003 | + k5_xor_firmware(&flash[0], *flash_size); |
| 1004 | + |
| 1005 | + if (flash[ 2] == 0x00 && |
| 1006 | + flash[ 3] == 0x20 && |
| 1007 | + flash[ 6] == 0x00 && |
| 1008 | + flash[10] == 0x00 && |
| 1009 | + flash[14] == 0x00) { |
| 1010 | + encrypted = false; |
| 1011 | + } |
| 1012 | + |
| 1013 | + if (!encrypted) |
| 1014 | + { |
| 1015 | + fprintf(stdout, "firmware file de-obfuscated\n"); |
| 1016 | + } |
| 1017 | + |
| 1018 | + if (!encrypted && *flash_size >= (0x2000 + 16)) |
| 1019 | + { // extract and remove the 16-byte version string |
| 1020 | + |
| 1021 | + char firmware_ver[17] = {0}; |
| 1022 | + memcpy(firmware_ver, &flash[0x2000], 16); |
| 1023 | + |
| 1024 | + if (*flash_size > (0x2000 + 16)) |
| 1025 | + memmove(&flash[0x2000], &flash[0x2000 + 16], *flash_size - 0x2000 - 16); |
| 1026 | + *flash_size = (*flash_size - 16); |
| 1027 | + |
| 1028 | + if (strlen(firmware_ver) > 8) { |
| 1029 | + fprintf(stderr, "Firmware version unexpectedly long (%ld): %s\n", strlen(firmware_ver), firmware_ver); |
| 1030 | + return false; |
| 1031 | + } |
| 1032 | + strncpy(flash_version_string, firmware_ver, 8); |
| 1033 | + |
| 1034 | + fprintf(stdout, "firmware file version '%s'\n", flash_version_string); |
| 1035 | + } |
| 1036 | + } |
| 1037 | + |
| 1038 | + if (encrypted) |
| 1039 | + { |
| 1040 | + fprintf(stderr,"File doesn't appear to be valid for uploading\n"); |
| 1041 | + return false; |
| 1042 | + } |
| 1043 | + |
| 1044 | + return true; |
| 1045 | +} |
| 1046 | + |
938 | 1047 | int main(int argc,char **argv) |
939 | 1048 | { |
940 | 1049 | int fd,ffd; |
@@ -996,6 +1105,11 @@ int main(int argc,char **argv) |
996 | 1105 | flash_length=read(ffd,(unsigned char *)&flash,UVK5_MAX_FLASH_SIZE); |
997 | 1106 | close(ffd); |
998 | 1107 |
|
| 1108 | + bool success = decrypt(flash, &flash_length); |
| 1109 | + if (!success) { |
| 1110 | + exit(1); |
| 1111 | + } |
| 1112 | + |
999 | 1113 | /* arbitrary limit do that someone doesn't flash some random short file */ |
1000 | 1114 | if ((i_know_what_im_doing<5)&&(flash_length<50000)) { |
1001 | 1115 | fprintf(stderr,"Failed to read whole eeprom from file %s (read %i), file too short or some other error\n",file,flash_length); |
|
0 commit comments