В этом посте я хочу поделиться с вами информацией о том как расшифровать коды ключей Proximity карт в базе Орион Про Болид. Может возникнуть вопрос: «Зачем это нужно, ведь есть же генератор отчетов который предоставляет данную информацию?». Возможно я странный человек, но я считаю что пользоваться разного рода надстройками над СУБД если имеешь опыт работы с нею это несколько странно. Работать с базой напрямую гораздо интереснее и веселее чем изучать чужие костыли. Коды карточек PROXIMITY в базе хранятся в базе в таблице pMark с столбце CodeP. Зачем-то разработчики ПО Орион Про их зашифровали… Но на наше счастье не очень серьезно. О связях между таблицами в БД Орион Про я уже делал короткую заметку в посте Связи между таблицами в базе данных MS SQL Orion Pro Болид, поэтому сейчас на этом останавливаться не будем. Вообще если кого-то интересует аналитика — пишите в комментах, присылайте дампы базы — будем посмотреть.
У меня есть файл в формате excel — экспорт открытых ключей из базы Орион Про. На его примере мы посмотрим как легко расшифровываются ключи карточек. Вот скрин:
Возьмем в работу 3х человек, да простят меня эти люди. Участниками эксперимента назначим номера 10239 Меднов, 10297 Дербенева и 10308 Лапынин.
Теперь напишем простенький запрос к БД:
SELECT
TabNumber
,Name
,FirstName
,MidName
,CodeP
FROM pList INNER JOIN pMark
ON pList.ID = pMark.Owner
WHERE pList.TabNumber IN ('10239', '10297', '10308')
На скрине виден результат. Ключи, как мы видим спрятаны.
Давайте скопируем крокозябры из столбца codeP и вставим в какой-нибудь двоичный редактор. Что получилось — видно на скринах ниже:
Таким образом мы выяснили что ключ представляет 12 байтное слово. На самом деле это кодировка ASCII. Теперь давайте для наглядности впишем полученные данные в нашу таблицу. Вот что получается:
Прелестно… Теперь откинем первый байт и заменим все пары байт FE01 на 00, вот что получилось:
Ну и напоследок: осталось переписать байты задом наперед и ключик наш! Делаем.
Вот собственно и все шифрование, далее осталось только написать функцию которая будет делать это на лету и можно писать любые запросы.
Если есть вопросы — задавайте в комментариях. Пожалуйста оценивайте статью.
Эта схема преобразования старая как мир, но работает не во всех версиях Орион ПРО.
Сейчас пытаюсь реверсировать для версии 1.20.2 и пока без результата.
Вот пара примеров карт и базы:
Карта: 62 00 04 00 AF CE DE 01
в Базе: 08 01 D0 AE D0 9E D0 87 D1 8E 01 04 D1 8E 01 62
Карта: 8A 00 00 00 34 7C FA 01
в Базе: 08 01 D1 8A 7C 34 D1 8E 01 D1 8E 01 D1 8E 01 D0 89
Выложите еще пару примеров. По двум картам сложно что-то анализировать.
81d18a7c34d18e1d18e1d18e1d089
15003900347CFA01
81d18e45d48d18e1d18e1d18e1d0bb
DA004600485D5C01
817b5b59d18e1d18e1d18e1d183
8F004A00595B7B01
81d18fd09ed087d18e14d18e1d0b3
E3000400AFCEFF01
8131d59d18e14ad18e1d093
C3004A00591D0301
81d18a7c34d18e1d18e1d18e1d089
15003900347CFA01
81d18e45d48d18e1d18e1d18e1d0bb
DA004600485D5C01
817b5b59d18e1d18e1d18e1d183
8F004A00595B7B01
81d18fd09ed087d18e14d18e1d0b3
E3000400AFCEFF01
8131d59d18e14ad18e1d093
C3004A00591D0301
SQl Server 2014
ALTER FUNCTION [dbo].[fсGetCodeStr] (@pCode varchar(50), @pCodeAdd varchar(50))
RETURNS varchar(50) AS
BEGIN
DECLARE @Result varchar(50) ;
IF @pCode IS NOT NULL
BEGIN
SET @Result = UPPER(REPLACE(sys.fn_varbintohexsubstring(0, CONVERT(varbinary(max), REVERSE(@pCode)), 1, 0)
, sys.fn_varbintohexsubstring(0, CONVERT(varbinary(max), REVERSE(@pCodeAdd)), 1, 0)
,’00’));
IF RIGHT(@Result, 2) = ’08’
SET @Result =LEFT(@Result, LEN(@Result)-2) ;
END
Return(@Result)
END
Спасибо тебе добрый человек.
Используя данное знание перевел из SQL в PHP функцию:
—- INFO —-
@info_1 как показали тесты, значение из поля `CodePAdd` не участвует в образовании хранимого в `CodeP` кода, поэтому `01fe` является постоянным значением заменяемым на `00`.
@info_2 `mb_convert_encoding` потребовалось в нашем случае, т.к. результат выборки из базы данных приходил в `UTF-8` кодировке.
—- SAMPLE —
<?php
// php 7.2
function decode_bolid_card ( $code_from_pMark_CodeP )
{
$code = mb_convert_encoding( $code_from_pMark_CodeP, 'cp1251' );
$code = strrev( $code );
$code = unpack( 'H*', $code );
$code = str_replace( '01fe', '00', array_pop( $code ) );
$code = substr( $code, 0, 16 );
return $code;
}
Тоже сильно раздражали костыли классического интерфейса. Делаю альтернативный веб интерфейс для добавления пользователей и карт, с картами возник затык c расшифровкой.
Для примера что получилось при использовании функции(версия орион про 1.12)
4D00370086FE6F01
4D00370086023F6F01
3F0009002246B101
BD0009002246B101
В конечном итоге получилось расшифровать карты, использовав их же штатную библиотеку, осталась проблема с шифрованием, для добавления ключей
// Если кому понадобится на javascript
function convertBolid(str) {
var arr = [];
for (var i = 0, l = str.length; i < l; i ++) {
var hex = Number(str.charCodeAt(i)).toString(16);
if(hex.length == 1 ) {
hex = '0'+hex
}
arr.push(hex);
}
return arr.slice(1).reverse().join('').replace(/01fe/g,'00');
}
Эм. Я тут свои пять копеек оставлю.
В дистребутиве АРМ 1.12.2 есть файлик, выше его уже упоминали, «C:\BOLID\ARM_ORION_PRO1_12_2\Data\LibraryDBEditor\OrionDBEditor.dll». А в нем функция DecodePassword которая расшифровывает все пароли в CodeP.
В 1.20.3 тоже работает.
Спасибо за статью. Подскажите как выгрузить pMark в csv файл.
Можно, например, с помощью Microsoft SQL Server Management Studio.
Спасибо, сделал с помощью орионовского експлорера. Но из-за проблем с кодировкой не получается адекватно расшифровать коды. Ввожу в HEX редактор значения сначала адекватно конвертирует, но после 3-4 ввода Совсем другой результат. Целый день потратил и так не разобрался в чём проблема. Может вы мне поможете, я вам скину csv а вы расшифруете коды в нём? Разумеется не бесплатно. Времени очень мало на то чтобы разбираться с этим.
Написал вам на почту.
Данные карт в базе никак не шифруются. Они хранятся в двоичном виде.
При запросе к базе используйте CAST в varbinary для поля CodeP.
Получится как то так «select id, cast(codep as varbinary), owner, ownername from pMark»
К примеру, возьмем одну из карт из базы 0x0801B97A73FE0108FE012A из запроса выше.
0х08 префикс. Меняем все FE01 на 00 (почему так еще не выяснил, скорее всего что то связанное с хранением во флэш памяти контроллеров или еще что то).
Получится 01B97A730008002A — как раз те данные карты которые отображаются в АБД (нужно только перевернуть в правильную сторону — 2A000800737AB901).
Для формирования кода из карт используется алгоритм CRC-8/maxim dallas.
Получить код карты тоже просто. Убираем младший байт 01 (он всегда имеет такое значение), и старший байт 2A (это контрольная сумма).
Получаем код карты B97A730008
Нет никакого шифрования в поле с кодом карт. Они хранятся в двоичном виде.
формат 0х0801хххххххххххх
Для формирования кода из карт используется алгоритм CRC-8/Dallas
Тогда уж в 16-тиричном)
В базе в двоичном.
Для читаемости перевел в шестнадцатеричный.
Единички и нолики писать не камильфо )
Небольшое дополнение.
Стало понятно почему в базе хранится ключ с FE 01 вместо 00.
Очевидно же почему так реализовано. Но не догнал сразу.
Формат поля в БД — varchar, а сохраняются данные в это поле в двоичном виде. Но в строку нельзя записать символ с кодом 00.
Поэтому и используется такое кодирование — вместо 00, пишется FE 01.
Я не понял, как можно произвести поиска карты в базе, точнее как преобразовать код карты для поиска по базе?
Описанное в статье на SQL можно реализовать так:
select
…
replace(convert(varchar(max), cast(reverse(substring(pm.CodeP, 2, len(pm.CodeP) — 1)) as varbinary(max)), 2), ’01FE’, ’00’)
…
from
pMark pm
У нас не сработал алгоритм с заменой FE01, FE02 и т.д.
Вернее сказать на большинство карт сработал, около 7 карт остались анонимными. Таких карт я не нашёл в БД.
Здравствуйте. Есть CSV файл с номерами карт в формате болида. 8B004100412D2F01, 8D004100635FEF01, 30003A003DA7E301, CB00270060822101, F900270060A96B01 и т.п. Нам нужно их конвертировать в десятеричную систему (или в любую другую, которую можно потом конвертировать в десятеричную) чтобы не собирать карты у всех сотрудников для модернизации системы. Можете помочь? разумеется не бесплатно!
Иван, не буду спрашивать зачем вам это, но если напрямую конвертировать 8-байтное слово в 10-чную ССч — числа получатся огромные. Данную операцию может проделать даже калькулятор винды. Просто переключите его в режим «программист» во вкладке Вид. Если вам не хочется этим заниматься — сообщите мне, пришлёте файл, я вам сконвертирую каким-нибудь автоматическим способом.
Александр, как с вами связаться возможно?
Public Function mark(str As Variant, gtype As Byte) As String
Dim ab() As Byte
If IsNull(str) Then
mark = «»
Exit Function
End If
ab = StrConv(str, vbFromUnicode) ‘ &H75)
Select Case gtype
Case 1
mark = Password(ab()) ‘password1(str) ‘
Case 3, 4
mark = prox(ab)
Case Else
mark = «??»
End Select
End Function
Private Function prox(ab As Variant) As String
Dim l As Byte, i As Byte, b As Byte
l = UBound(ab)
For i = 1 To l
b = ab(i)
If b = &HFE Then
Select Case ab(i + 1)
Case 1
b = 0
Case 2
b = &HFE
End Select
i = i + 1
End If
If b < 16 Then
prox = "0" & Hex(b) & prox
Else
prox = Hex(b) & prox
End If
Next i
End Function
Обновленная PHP функция декодирования карт из значения «CodeP», для Болида v1.20.2.
Изменения не значительные, теперь кроме «01fe» нужно так же заменять еще несколько значений.
— PHP Code —
function decode_bolid_card ( $code )
{
$code = mb_convert_encoding( $code, ‘CP1251’ );
$code = strrev( $code );
$code = unpack( ‘H*’, $code );
$code = array_pop( $code );
$code = str_replace( ’01fe’, ’00’, $code );
$code = str_replace( ’01fe’, ‘fe’, $code );
$code = str_replace( ’02fe’, ‘fe’, $code );
$code = str_replace( ’03fe’, ’20’, $code );
$code = str_replace( ’04fe’, ‘cc’, $code );
$code = str_replace( ’05fe’, ‘0a’, $code );
$code = str_replace( ’00fe’, ‘0001FE’, $code );
$code = substr( $code, 0, 16 );
return $code;
}
— — — — — —
Кому надо выкладываю код ЛаоСана но в Python вариации.
Я переводил-переводил… и что-то осознал что зря делала :))) с usb proxy считывателя совсем другой код приходит. ха)
— Python Code —
import binascii
def decodingCard(self, code) -> str:
__result = code.encode(«CP1251»)
__result = binascii.hexlify(__result[::-1])
__result = __result[::-1].decode()
__result = __result[::-1].replace(’01fe’, ’00’)
__result = __result[::-1].replace(’01fe’, ‘fe’)
__result = __result[::-1].replace(’02fe’, ‘fe’)
__result = __result[::-1].replace(’03fe’, ’20’)
__result = __result[::-1].replace(’04fe’, ‘cc’)
__result = __result[::-1].replace(’05fe’, ‘0a’)
__result = __result[::-1].replace(’00fe’, ‘0001FE’)
return __result[0:16]
————
Некрокоммент с вопросами к ЛаоСан и MeGaX5.
1. На кой нужна строка
$code = str_replace( ’01fe’, ‘fe’, $code );
, когда предыдущей строкой мы уже поменяли все ’01fe’?
2. Как мы можем получить в выхлопе («расшифрованной» строке) последовательность ’00fe’? (ну или зачем нужна последняя замена)