Начало

Передо мной встала задача провести реверс-инжиниринг программы мониторинга артериального давления для устройства Spacelabs OnTrak 90227 ABP Monitor.

Вот такого:

Spacelabs OnTrak 90227 ABP Monitor
Spacelabs OnTrak 90227 ABP Monitor

Устройство подключается через USB и определяется системой как виртуальный COM-порт.

Программа, с которой мне предстояло работать, 32 битная, написана на C++ с использованием MFC и была выпущена в 2010 году.

Несмотря на поддержку юникода, приложение имеет проблемы с отображением кириллицы — но это не важно для задачи.

 Приложение мониторинга артериального давления компании Spacelabs
Приложение мониторинга артериального давления компании Spacelabs

Основная цель — найти код, связанный с кнопками «Выгрузить монитор» и «Инициализировать монитор», чтобы осуществить обмен данными с устройством.

На основе найденного кода нужно разработать консольное приложение, которое будет инициализировать устройство для последующих измерений или считывать данные с устройства и записывать результаты в файл.

Экзешник программы называется abpwin.exe, и в одной папке с ним находятся несколько библиотек DLL и вспомогательных программ для работы с базой данных. Кстати, приложение использует MS SQL Server 2005, хотя, для таких задач вполне подошел бы SQLite.

При нажатии на кнопку «Выгрузить монитор» открывается диалог в виде мастера, где и происходит выгрузка данных.

Выгрузка данных с устройства
Выгрузка данных с устройства

Я попытался найти этот диалог в ресурсах приложения с помощью Resource Hacker, но не нашел его. Я предположил, что он может находиться в одной из библиотек DLL.

Внимание привлекла библиотека ABPDeviceCommunicator.dll, которая, судя по названию, отвечает за обмен данными с устройством.

В ней я обнаружил часть диалога — панель с надписью «Select Communication Mode:»

OLE Компонент
OLE Компонент

Кроме того, в ресурсах присутствуют REGISTRY и TYPELIB, что указывает на наличие OLE компонента! Зачем было использовать OLE в этом приложении? Для чего?

Ведь достаточно просто экспортировать функции из библиотеки.

На эти вопросы вряд ли кто-то сможет ответить, кроме разработчиков приложения, но с ними понятное дело связи нет.

Это явно оверинжиниринг. Наверно, разработчики хотели продемонстрировать свои навыки, используя OLE и работу с SQL-сервером, чтобы показать, какие они крутые программисты.

Стоит отметить, что библиотека экспортирует лишь четыре функции: DllCanUnloadNow, DllGetClassObject, DllRegisterServer и DllUnregisterServer. В рамках OLE нет необходимости экспортировать функции из библиотек, и это создает определенные сложности при исследовании и поиске нужного кода.

Для получения списка классов и методов можно воспользоваться инструментом OLE/COM Object Viewer из Windows SDK. Вот что он выдал:

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: ABPDeviceCommunicator.dll

[
  uuid(3A9C5D9C-4434-4420-A475-6BB9F4CD720A),
  version(1.0),
  helpstring("ABPDeviceCommunicator 1.0 Type Library"),
  custom(DE77BA64-517C-11D1-A2DA-0000F8773CE9, 83951780),
  custom(DE77BA63-517C-11D1-A2DA-0000F8773CE9, 1363618625)

]
library ABPDEVICECOMMUNICATORLib
{
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface IABPCommunicate;

    [
      uuid(91E73282-9D64-4932-B680-18BF6A2DD293),
      helpstring("ABPDeviceComn Class")
    ]
    coclass ABPDeviceComn {
        [default] interface IABPCommunicate;
    };

    [
      odl,
      uuid(A12CA95D-5EFC-459A-9296-740E44CA79A6),
      helpstring("IABPCommunicate Interface"),
      dual,
      oleautomation
    ]
    interface IABPCommunicate : IDispatch {
        [id(0x00000001), helpstring("method UploadMonitor")]
        HRESULT UploadMonitor([out, retval] VARIANT* pVarCompleteXMLTree);
        [id(0x00000002), helpstring("method StartInitializingMonitor")]
        HRESULT StartInitializingMonitor([out, retval] long* pbInitSucceeded);
        [id(0x00000003), helpstring("method AddMonitorPeriod")]
        HRESULT AddMonitorPeriod(
                        [in] VARIANT* pVarStartTime, 
                        [in] VARIANT* pVarEndTime, 
                        [in] long nCycleTime, 
                        [in] long bToneHeard, 
                        [in] long bFirstPeriod);
        [id(0x00000004), helpstring("method AddMonitorSettings")]
        HRESULT AddMonitorSettings(
                        [in] long bShowReadingsresults, 
                        [in] long bClinicalVerifSetup, 
                        [in] long bShowCuffPressure, 
                        [in] long bClockFormat, 
                        [in] BSTR strPatientGuidId, 
                        [in] BSTR strReasonForTest, 
                        [in] BSTR strPrimaryPassword, 
                        [in] BSTR strSecondaryPassword);
        [id(0x00000005), helpstring("method UseThisLanguage")]
        HRESULT UseThisLanguage([in, optional, defaultvalue("0")] BSTR bstrLanguage);
        [id(0x00000006), helpstring("method UploadMonitorVC")]
        HRESULT UploadMonitorVC(
                        [out] VARIANT* pVarMonitorReading, 
                        [out] VARIANT* pVarInitDate, 
                        [out] VARIANT* pVarPatientGuid, 
                        [out] VARIANT* pVarReasonsForTest, 
                        [out] VARIANT* pVarNumberOfReadings, 
                        [out, retval] long* pbUploadSucceeded);
        [id(0x00000007), helpstring("method SetLastUsedComPort")]
        HRESULT SetLastUsedComPort([in] BSTR bstrComPort);
        [id(0x00000008), helpstring("method GetLastUsedComPort")]
        HRESULT GetLastUsedComPort([out] VARIANT* pVarComPort);
        [id(0x00000009), helpstring("method GetFirmwareVersion")]
        HRESULT GetFirmwareVersion(
                        [out] VARIANT* pVarModel90207Version, 
                        [out] VARIANT* pVarModel90217Version);
        [id(0x0000000a), helpstring("method GetLastUsedComPortAsp")]
        HRESULT GetLastUsedComPortAsp([out, retval] VARIANT* pVarComPort);
        [id(0x0000000b), helpstring("method StopInitMonitor")]
        HRESULT StopInitMonitor();
        [id(0x0000000c), helpstring("method StopReadMonitor")]
        HRESULT StopReadMonitor();
        [id(0x0000000d), helpstring("method DumpMonitorDataToFile")]
        HRESULT DumpMonitorDataToFile(
                        [in] BSTR bstrFilePath, 
                        [out, retval] long* bResult);
        [id(0x0000000e), helpstring("method CheckForNoCOMPorts")]
        HRESULT CheckForNoCOMPorts();
    };
};

От этого, конечно, мало толку, но имеем то, что имеем.

Судя по всему, за процесс выгрузки данных отвечают методы UploadMonitor и UploadMonitorVC.

Эмулятор на Arduino.

Статическим анализом эти методы не найти, поэтому необходима отладка. Однако возникла загвоздка: устройство находится в офисе в другой стране, и доступ к машине осуществляется через VNC. Отлаживать в таких условиях не очень удобно, в добавок ко всему устройство может "заснуть" и пропасть из системы в любой момент, несмотря на подключение через USB.

Мне нужно было придумать альтернативное решение, и я нашел его. Я перехватил обмен данными по COM-порту с помощью программы «Device Monitoring Studio». Хотя версия программы была урезанной, ее функциональности оказалось достаточно для выполнения задачи.

К тому же, для работы с устройством подходит только опция «Direct», в то время как опция «Modem» не функционирует, что значительно упрощает процесс. Протокол обмена данными интересный, с простым 16-битным шифрованием.

На основе перехваченных команд и данных я разработал скетч для Arduino, который эмулировает устройство. Программа успешно приняла этот эмулятор и прочитала все данные, не вызвав никаких ошибок. Это позволило мне проводить отладку локально, не опасаясь, что устройство "заснет".

Сам скетч:

#include <Arduino.h>

#define SERIAL_BAUD_RATE 9600
#define RX_BUFF_SIZE 81

using namespace std;

// Commands
const uint8_t I66F9[] =
{
    0x01, 0x49, 0x0D, 0x36, 0x36, 0x46, 0x39
};

const uint8_t V75B4[] = 
{
    0x01, 0x56, 0x0D, 0x37, 0x35, 0x42, 0x34
};

const uint8_t C406[] =
{
    0x01, 0x3F, 0x0D, 0x43, 0x34, 0x30, 0x36 
};

const uint8_t UGENERIC[] =
{
    0x01, 0x55, 0x47, 0x45, 0x4E, 0x45,
    0x52, 0x49, 0x43, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x0D, 0x41,
    0x46, 0x30, 0x32 
};

const uint8_t MAA3D[] = { 0x01, 0x4D, 0x0D, 0x41, 0x41, 0x33, 0x44 };

const uint8_t D816022ADAC[] = { 0x01, 0x44, 0x38, 0x31, 0x36, 0x30, 0x32, 0x32, 0x0D, 0x41, 0x44, 0x41, 0x43 };

const uint8_t D81820F2591[] = { 0x01, 0x44, 0x38, 0x31, 0x38, 0x32, 0x30, 0x46, 0x0D, 0x32, 0x35, 0x39, 0x31 };

const uint8_t D819241[] = { 0x01, 0x44, 0x38, 0x31, 0x39, 0x32, 0x34, 0x31, 0x0D, 0x43, 0x32, 0x43, 0x45 };

const uint8_t D81D306[] = { 0x01, 0x44, 0x38, 0x31, 0x44, 0x33, 0x30, 0x36, 0x0D, 0x43, 0x44, 0x44, 0x31 };

const uint8_t D810B01[] = { 0x01, 0x44, 0x38, 0x31, 0x30, 0x42, 0x30, 0x31, 0x0D, 0x46, 0x34, 0x30, 0x37 };

const uint8_t D820078[] = { 0x01, 0x44, 0x38, 0x32, 0x30, 0x30, 0x37, 0x38, 0x0D, 0x41, 0x41, 0x46, 0x32 };

const uint8_t D830078[] = { 0x01, 0x44, 0x38, 0x33, 0x30, 0x30, 0x37, 0x38, 0x0D, 0x45, 0x46, 0x35, 0x32 };

const uint8_t D840078[] = { 0x01, 0x44, 0x38, 0x34, 0x30, 0x30, 0x37, 0x38, 0x0D, 0x32, 0x37, 0x31, 0x33 };

const uint8_t D850078[] = { 0x01, 0x44, 0x38, 0x35, 0x30, 0x30, 0x37, 0x38, 0x0D, 0x36, 0x32, 0x42, 0x33 };

const uint8_t D860078[] = { 0x01, 0x44, 0x38, 0x36, 0x30, 0x30, 0x37, 0x38, 0x0D, 0x41, 0x43, 0x35, 0x33 };

const uint8_t D870078[] = { 0x01, 0x44, 0x38, 0x37, 0x30, 0x30, 0x37, 0x38, 0x0D, 0x45, 0x39, 0x46, 0x33 };

const uint8_t DCA0078[] = { 0x01, 0x44, 0x43, 0x41, 0x30, 0x30, 0x37, 0x38, 0x0D, 0x45, 0x45, 0x43, 0x30 };

const uint8_t DCB0078[] = { 0x01, 0x44, 0x43, 0x42, 0x30, 0x30, 0x37, 0x38, 0x0D, 0x32, 0x30, 0x32, 0x30 };

const uint8_t W81DA011[] = { 0x01, 0x57, 0x38, 0x31, 0x44, 0x41, 0x30, 0x31, 0x31, 0x41, 0x0D, 0x35, 0x45, 0x36, 0x38 };

const uint8_t F21D57[] = { 0x01, 0x46, 0x32, 0x0D, 0x31, 0x44, 0x35, 0x37 };

const uint8_t f7021[] = { 0x01, 0x66, 0x0D, 0x37, 0x30, 0x32, 0x31 };

const uint8_t BBA03[] = { 0x01, 0x42, 0x0D, 0x42, 0x41, 0x30, 0x33 };

const uint8_t D81DA01[] = { 0x01, 0x44, 0x38, 0x31, 0x44, 0x41, 0x30, 0x31, 0x0D, 0x46, 0x42, 0x35, 0x42 };

const uint8_t RB970[] = { 0x01, 0x52, 0x0D, 0x42, 0x39, 0x37, 0x30 };

const uint8_t W8117020401[] = { 0x01, 0x57, 0x38, 0x31, 0x31, 0x37, 0x30, 0x32, 0x30, 0x34, 0x30, 0x31, 0x0D, 0x44, 0x36, 0x42, 0x32 };

const uint8_t W814018[] =
{ 
    0x01, 0x57, 0x38, 0x31, 0x34, 0x30, 0x31, 0x38
};

const uint8_t W81D306[] =
{
    0x01, 0x57, 0x38, 0x31, 0x44, 0x33, 0x30
};

const uint8_t T[] =
{
    0x01, 0x54
};

const uint8_t W8160[] =
{
    0x01, 0x57, 0x38, 0x31, 0x36, 0x30
};

const uint8_t W8182[] =
{
    0x01, 0x57, 0x38, 0x31, 0x38, 0x32
};

const uint8_t W8192[] =
{
    0x01, 0x57, 0x38, 0x31, 0x39, 0x32
};

const uint8_t W81DA0100[] =
{
    0x01, 0x57, 0x38, 0x31, 0x44, 0x41, 0x30, 0x31, 0x30, 0x30, 0x0D, 0x35, 0x32, 0x33, 0x30
};

const uint8_t W811702[] =
{
    0x01, 0x57, 0x38, 0x31, 0x31, 0x37, 0x30, 0x32
};

// Responses
const uint8_t Response_I66F9[] PROGMEM =
{
    0x01, 0x49, 0x39, 0x30, 0x32, 0x31, 0x37,
    0x20, 0x56, 0x20, 0x30, 0x33, 0x2E, 0x30,
    0x32, 0x2E, 0x30, 0x35, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x0D, 0x37, 0x43, 0x44,
    0x39 
};

const uint8_t Response_V75B4[] PROGMEM =
{
    0x01, 0x56, 0x06, 0x0D, 0x39, 0x37, 0x36, 0x35 
};

const uint8_t Response_C406[] PROGMEM =
{
    0x01, 0x3F, 0x39, 0x30, 0x32, 0x31, 0x37, 0x20, 0x56, 0x20, 0x30, 0x33, 0x2E, 0x30, 0x32, 0x2E,
    0x31, 0x36, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0D, 0x33, 0x43, 0x31, 0x44 
};

const uint8_t Response_UGENERIC[] PROGMEM = { 0x01, 0x55, 0x06, 0x0D, 0x43, 0x45, 0x33, 0x35 };

const uint8_t Response_MAA3D[] PROGMEM = { 0x01, 0x4D, 0x30, 0x30, 0x30, 0x30, 0x0D, 0x31, 0x35, 0x43, 0x39 };

const uint8_t Response_D816022ADAC[] PROGMEM = 
{
    0x01, 0x44, 0x36, 0x42, 0x44, 0x37, 0x43, 0x41, 0x38, 0x35, 0x41, 0x44,
    0x38, 0x43, 0x43, 0x30, 0x39, 0x37, 0x36, 0x31, 0x31, 0x45, 0x35, 0x46,
    0x39, 0x45, 0x38, 0x43, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37,
    0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43,
    0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37,
    0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x0D, 0x44,
    0x31, 0x41, 0x44 
};

const uint8_t Response_D81820F2591[] PROGMEM = 
{
    0x01, 0x44, 0x32, 0x46, 0x31, 0x32, 0x31, 0x46, 0x38, 0x39, 0x37, 0x43, 0x43, 0x36, 0x35, 0x31,
    0x38, 0x45, 0x45, 0x37, 0x45, 0x33, 0x43, 0x42, 0x35, 0x37, 0x32, 0x35, 0x44, 0x34, 0x45, 0x39,
    0x0D, 0x44, 0x31, 0x41, 0x44 
};

const uint8_t Response_D819241[] PROGMEM = 
{
    0x01, 0x44, 0x33, 0x34, 0x38, 0x32, 0x33, 0x43, 0x36, 0x32, 0x43, 0x43,
    0x45, 0x30, 0x44, 0x31, 0x45, 0x39, 0x30, 0x37, 0x36, 0x38, 0x41, 0x33,
    0x33, 0x45, 0x43, 0x35, 0x44, 0x33, 0x36, 0x39, 0x46, 0x37, 0x35, 0x33,
    0x32, 0x45, 0x42, 0x43, 0x39, 0x44, 0x44, 0x33, 0x42, 0x36, 0x46, 0x38,
    0x45, 0x36, 0x44, 0x33, 0x41, 0x32, 0x39, 0x44, 0x33, 0x44, 0x39, 0x34,
    0x37, 0x46, 0x39, 0x38, 0x34, 0x31, 0x44, 0x33, 0x42, 0x31, 0x42, 0x44,
    0x34, 0x42, 0x42, 0x41, 0x33, 0x44, 0x46, 0x45, 0x37, 0x33, 0x41, 0x32,
    0x36, 0x44, 0x34, 0x38, 0x39, 0x45, 0x33, 0x44, 0x44, 0x44, 0x33, 0x44,
    0x39, 0x41, 0x37, 0x43, 0x36, 0x42, 0x39, 0x34, 0x37, 0x38, 0x33, 0x30,
    0x32, 0x33, 0x33, 0x44, 0x33, 0x37, 0x33, 0x45, 0x43, 0x42, 0x36, 0x37,
    0x32, 0x37, 0x41, 0x43, 0x43, 0x41, 0x34, 0x32, 0x31, 0x43, 0x30, 0x31,
    0x0D, 0x44, 0x31, 0x41, 0x44 
};

const uint8_t Response_D81D306[] PROGMEM = 
{
    0x01, 0x44, 0x43, 0x41, 0x44, 0x46, 0x39, 0x35, 0x45, 0x42, 0x39, 0x45,
    0x31, 0x34, 0x0D, 0x34, 0x38, 0x30, 0x37 
};

const uint8_t Response_D810B01[] PROGMEM = 
{
    0x01, 0x44, 0x30, 0x34, 0x0D, 0x33, 0x44, 0x31, 0x35
};

const uint8_t Response_D820078[] PROGMEM = 
{
    0x01, 0x44, 0x35, 0x34, 0x32, 0x44, 0x41, 0x33, 0x31, 0x35, 0x35, 0x33,
    0x38, 0x41, 0x30, 0x34, 0x31, 0x33, 0x41, 0x43, 0x30, 0x35, 0x31, 0x37,
    0x32, 0x41, 0x36, 0x39, 0x45, 0x34, 0x43, 0x43, 0x33, 0x30, 0x42, 0x41,
    0x33, 0x43, 0x46, 0x45, 0x45, 0x34, 0x46, 0x31, 0x39, 0x31, 0x31, 0x38,
    0x37, 0x30, 0x43, 0x38, 0x32, 0x45, 0x32, 0x39, 0x45, 0x38, 0x41, 0x37,
    0x30, 0x41, 0x41, 0x35, 0x33, 0x44, 0x42, 0x35, 0x46, 0x35, 0x32, 0x36,
    0x34, 0x34, 0x37, 0x39, 0x36, 0x36, 0x30, 0x36, 0x44, 0x39, 0x33, 0x38,
    0x35, 0x39, 0x38, 0x45, 0x38, 0x32, 0x41, 0x39, 0x45, 0x39, 0x39, 0x35,
    0x42, 0x45, 0x41, 0x39, 0x34, 0x32, 0x38, 0x41, 0x45, 0x37, 0x44, 0x38,
    0x39, 0x37, 0x34, 0x44, 0x45, 0x36, 0x43, 0x33, 0x46, 0x43, 0x43, 0x41,
    0x46, 0x31, 0x42, 0x42, 0x36, 0x39, 0x39, 0x34, 0x45, 0x36, 0x31, 0x31,
    0x32, 0x46, 0x43, 0x33, 0x42, 0x39, 0x39, 0x46, 0x42, 0x38, 0x46, 0x46,
    0x32, 0x44, 0x31, 0x45, 0x38, 0x34, 0x42, 0x32, 0x42, 0x32, 0x36, 0x42,
    0x36, 0x41, 0x41, 0x37, 0x38, 0x37, 0x31, 0x46, 0x36, 0x36, 0x35, 0x42,
    0x38, 0x33, 0x35, 0x42, 0x30, 0x35, 0x45, 0x44, 0x44, 0x46, 0x42, 0x43,
    0x36, 0x32, 0x45, 0x33, 0x42, 0x43, 0x44, 0x38, 0x38, 0x41, 0x31, 0x46,
    0x46, 0x34, 0x42, 0x33, 0x36, 0x38, 0x41, 0x33, 0x33, 0x45, 0x43, 0x35,
    0x44, 0x33, 0x36, 0x39, 0x46, 0x37, 0x35, 0x33, 0x32, 0x45, 0x42, 0x43,
    0x39, 0x44, 0x44, 0x33, 0x42, 0x36, 0x46, 0x38, 0x45, 0x36, 0x44, 0x33,
    0x41, 0x32, 0x39, 0x44, 0x33, 0x44, 0x39, 0x34, 0x37, 0x46, 0x39, 0x38,
    0x34, 0x31, 0x0D, 0x34, 0x44, 0x37, 0x33 
};

const uint8_t Response_D830078[] PROGMEM = 
{
    0x01, 0x44, 0x33, 0x31, 0x31, 0x41, 0x32, 0x39, 0x37, 0x34, 0x42, 0x45,
    0x34, 0x31, 0x39, 0x36, 0x34, 0x31, 0x35, 0x38, 0x37, 0x35, 0x44, 0x41,
    0x37, 0x39, 0x31, 0x33, 0x35, 0x46, 0x36, 0x33, 0x33, 0x44, 0x37, 0x41,
    0x31, 0x33, 0x35, 0x35, 0x39, 0x34, 0x39, 0x44, 0x42, 0x43, 0x38, 0x34,
    0x38, 0x36, 0x33, 0x43, 0x42, 0x35, 0x45, 0x35, 0x45, 0x36, 0x43, 0x33,
    0x46, 0x43, 0x43, 0x41, 0x46, 0x31, 0x42, 0x42, 0x36, 0x39, 0x39, 0x34,
    0x45, 0x36, 0x31, 0x31, 0x32, 0x46, 0x43, 0x33, 0x42, 0x39, 0x39, 0x46,
    0x42, 0x38, 0x46, 0x46, 0x32, 0x44, 0x31, 0x45, 0x38, 0x34, 0x42, 0x32,
    0x42, 0x32, 0x36, 0x42, 0x36, 0x41, 0x41, 0x37, 0x38, 0x37, 0x31, 0x46,
    0x36, 0x36, 0x35, 0x42, 0x38, 0x33, 0x35, 0x42, 0x30, 0x35, 0x45, 0x44,
    0x44, 0x46, 0x42, 0x43, 0x36, 0x32, 0x45, 0x33, 0x42, 0x43, 0x44, 0x38,
    0x38, 0x41, 0x31, 0x46, 0x46, 0x34, 0x42, 0x33, 0x36, 0x38, 0x41, 0x33,
    0x33, 0x45, 0x43, 0x35, 0x44, 0x33, 0x36, 0x39, 0x46, 0x37, 0x35, 0x33,
    0x32, 0x45, 0x42, 0x43, 0x39, 0x44, 0x44, 0x33, 0x42, 0x36, 0x46, 0x38,
    0x45, 0x36, 0x44, 0x33, 0x41, 0x32, 0x39, 0x44, 0x33, 0x44, 0x39, 0x34,
    0x37, 0x46, 0x39, 0x38, 0x34, 0x31, 0x44, 0x33, 0x42, 0x31, 0x42, 0x44,
    0x34, 0x42, 0x42, 0x41, 0x33, 0x44, 0x46, 0x45, 0x37, 0x33, 0x41, 0x32,
    0x36, 0x44, 0x34, 0x38, 0x39, 0x45, 0x33, 0x44, 0x44, 0x44, 0x33, 0x44,
    0x39, 0x41, 0x37, 0x43, 0x36, 0x42, 0x39, 0x34, 0x37, 0x38, 0x33, 0x30,
    0x32, 0x33, 0x33, 0x44, 0x33, 0x37, 0x33, 0x45, 0x43, 0x42, 0x36, 0x37,
    0x32, 0x37, 0x0D, 0x44, 0x39, 0x38, 0x46 
};

const uint8_t Response_D840078[] PROGMEM = 
{
    0x01, 0x44, 0x36, 0x33, 0x30, 0x41, 0x38, 0x37, 0x36, 0x30, 0x39, 0x42,
    0x32, 0x32, 0x45, 0x31, 0x39, 0x46, 0x30, 0x32, 0x44, 0x32, 0x35, 0x35,
    0x33, 0x38, 0x31, 0x38, 0x34, 0x46, 0x31, 0x42, 0x42, 0x30, 0x36, 0x41,
    0x42, 0x35, 0x45, 0x35, 0x35, 0x35, 0x39, 0x37, 0x43, 0x34, 0x33, 0x30,
    0x45, 0x43, 0x35, 0x44, 0x32, 0x45, 0x35, 0x42, 0x46, 0x39, 0x34, 0x46,
    0x41, 0x43, 0x30, 0x33, 0x42, 0x30, 0x44, 0x33, 0x43, 0x41, 0x44, 0x33,
    0x42, 0x46, 0x35, 0x46, 0x42, 0x43, 0x36, 0x45, 0x46, 0x34, 0x32, 0x39,
    0x30, 0x41, 0x38, 0x41, 0x43, 0x41, 0x35, 0x44, 0x46, 0x42, 0x42, 0x30,
    0x33, 0x37, 0x33, 0x45, 0x43, 0x42, 0x36, 0x37, 0x32, 0x37, 0x41, 0x43,
    0x43, 0x41, 0x34, 0x32, 0x31, 0x43, 0x30, 0x31, 0x38, 0x43, 0x43, 0x39,
    0x31, 0x45, 0x34, 0x46, 0x43, 0x41, 0x46, 0x30, 0x36, 0x33, 0x33, 0x34,
    0x43, 0x46, 0x36, 0x35, 0x35, 0x41, 0x45, 0x39, 0x30, 0x37, 0x36, 0x38,
    0x41, 0x33, 0x33, 0x45, 0x43, 0x35, 0x44, 0x33, 0x36, 0x39, 0x46, 0x37,
    0x35, 0x33, 0x32, 0x45, 0x42, 0x43, 0x39, 0x44, 0x44, 0x33, 0x42, 0x36,
    0x46, 0x38, 0x45, 0x36, 0x44, 0x33, 0x41, 0x32, 0x39, 0x44, 0x33, 0x44,
    0x39, 0x34, 0x37, 0x46, 0x39, 0x38, 0x34, 0x31, 0x44, 0x33, 0x42, 0x31,
    0x42, 0x44, 0x34, 0x42, 0x42, 0x41, 0x33, 0x44, 0x46, 0x45, 0x37, 0x33,
    0x41, 0x32, 0x36, 0x44, 0x34, 0x38, 0x39, 0x45, 0x33, 0x44, 0x44,
    0x44, 0x33, 0x44, 0x39, 0x41, 0x37, 0x43, 0x36, 0x42, 0x39, 0x34, 0x37,
    0x38, 0x33, 0x30, 0x32, 0x33, 0x33, 0x44, 0x33, 0x37, 0x33, 0x45, 0x43,
    0x42, 0x36, 0x37, 0x0D, 0x45, 0x32, 0x43, 0x38
};

const uint8_t Response_D850078[] PROGMEM = 
{
    0x01, 0x44, 0x35, 0x38, 0x33, 0x44, 0x35, 0x39, 0x36, 0x33, 0x31, 0x39, 0x42, 0x46, 0x38, 0x34,
    0x31, 0x30, 0x42, 0x30, 0x36, 0x30, 0x42, 0x42, 0x31, 0x36, 0x46, 0x34, 0x43, 0x44, 0x35, 0x38,
    0x30, 0x35, 0x43, 0x31, 0x45, 0x45, 0x38, 0x32, 0x30, 0x35, 0x42, 0x31, 0x35, 0x33, 0x38, 0x34,
    0x35, 0x45, 0x41, 0x39, 0x39, 0x42, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30, 0x42, 0x37, 0x41, 0x43, 0x41, 0x43, 0x39, 0x30,
    0x42, 0x37, 0x0D, 0x44, 0x44, 0x46, 0x37
};

const uint8_t Response_D860078[] PROGMEM = 
{
    0x01, 0x44, 0x30, 0x35, 0x43, 0x36, 0x45, 0x32, 0x42, 0x46, 0x42, 0x45,
    0x32, 0x32, 0x32, 0x39, 0x44, 0x42, 0x35, 0x30, 0x36, 0x34, 0x44, 0x33,
    0x31, 0x42, 0x39, 0x41, 0x38, 0x34, 0x43, 0x42, 0x31, 0x31, 0x38, 0x45,
    0x36, 0x44, 0x30, 0x36, 0x37, 0x42, 0x44, 0x37, 0x46, 0x39, 0x46, 0x44,
    0x42, 0x44, 0x39, 0x36, 0x33, 0x46, 0x43, 0x43, 0x37, 0x41, 0x41, 0x32,
    0x45, 0x35, 0x45, 0x35, 0x38, 0x43, 0x39, 0x34, 0x35, 0x34, 0x44, 0x33,
    0x44, 0x43, 0x41, 0x31, 0x45, 0x31, 0x43, 0x32, 0x33, 0x45, 0x38, 0x43,
    0x35, 0x39, 0x46, 0x30, 0x42, 0x35, 0x30, 0x33, 0x42, 0x37, 0x43, 0x44,
    0x33, 0x45, 0x41, 0x39, 0x42, 0x30, 0x34, 0x35, 0x43, 0x38, 0x43, 0x42,
    0x36, 0x32, 0x45, 0x33, 0x42, 0x43, 0x44, 0x38, 0x38, 0x41, 0x31, 0x46,
    0x46, 0x34, 0x42, 0x33, 0x36, 0x38, 0x41, 0x33, 0x33, 0x45, 0x43, 0x35,
    0x44, 0x33, 0x36, 0x39, 0x46, 0x37, 0x35, 0x33, 0x32, 0x45, 0x42, 0x43,
    0x39, 0x44, 0x44, 0x33, 0x42, 0x36, 0x46, 0x38, 0x45, 0x36, 0x44, 0x33,
    0x41, 0x32, 0x39, 0x44, 0x33, 0x44, 0x39, 0x34, 0x37, 0x46, 0x39, 0x38,
    0x34, 0x31, 0x44, 0x33, 0x42, 0x31, 0x42, 0x44, 0x34, 0x42, 0x42, 0x41,
    0x33, 0x44, 0x46, 0x45, 0x37, 0x33, 0x41, 0x32, 0x36, 0x44, 0x34, 0x38,
    0x39, 0x45, 0x33, 0x44, 0x44, 0x44, 0x33, 0x44, 0x39, 0x41, 0x37, 0x43,
    0x36, 0x42, 0x39, 0x34, 0x37, 0x38, 0x33, 0x30, 0x32, 0x33, 0x33, 0x44,
    0x33, 0x37, 0x33, 0x45, 0x43, 0x42, 0x36, 0x37, 0x32, 0x37, 0x41, 0x43,
    0x43, 0x41, 0x34, 0x32, 0x31, 0x43, 0x30, 0x31, 0x38, 0x43, 0x43, 0x39,
    0x31, 0x45, 0x0D, 0x32, 0x45, 0x38, 0x44
};

const uint8_t Response_D870078[] PROGMEM = 
{
    0x01, 0x44, 0x41, 0x37, 0x45, 0x34, 0x34, 0x42, 0x31, 0x34, 0x44, 0x35,
    0x46, 0x39, 0x33, 0x46, 0x36, 0x45, 0x46, 0x39, 0x38, 0x35, 0x36, 0x30,
    0x32, 0x38, 0x42, 0x34, 0x38, 0x38, 0x42, 0x32, 0x44, 0x30, 0x44, 0x44,
    0x37, 0x36, 0x35, 0x41, 0x36, 0x42, 0x42, 0x39, 0x44, 0x43, 0x34, 0x44,
    0x43, 0x31, 0x42, 0x43, 0x31, 0x42, 0x45, 0x30, 0x45, 0x33, 0x31, 0x34,
    0x41, 0x31, 0x44, 0x42, 0x35, 0x46, 0x42, 0x43, 0x36, 0x45, 0x46, 0x34,
    0x32, 0x39, 0x30, 0x41, 0x38, 0x41, 0x43, 0x41, 0x35, 0x44, 0x46, 0x42,
    0x42, 0x30, 0x33, 0x37, 0x33, 0x45, 0x43, 0x42, 0x36, 0x37, 0x32, 0x37,
    0x41, 0x43, 0x43, 0x41, 0x34, 0x32, 0x31, 0x43, 0x30, 0x31, 0x38,
    0x43, 0x43, 0x39, 0x31, 0x45, 0x34, 0x46, 0x43, 0x41, 0x46, 0x30,
    0x36, 0x33, 0x33, 0x34, 0x43, 0x46, 0x36, 0x35, 0x35, 0x41, 0x45, 0x39,
    0x30, 0x37, 0x36, 0x38, 0x41, 0x33, 0x33, 0x45, 0x43, 0x35, 0x44, 0x33,
    0x36, 0x39, 0x46, 0x37, 0x35, 0x33, 0x32, 0x45, 0x42, 0x43, 0x39, 0x44,
    0x44, 0x33, 0x42, 0x36, 0x46, 0x38, 0x45, 0x36, 0x44, 0x33, 0x41, 0x32,
    0x39, 0x44, 0x33, 0x44, 0x39, 0x34, 0x37, 0x46, 0x39, 0x38, 0x34, 0x31,
    0x44, 0x33, 0x42, 0x31, 0x42, 0x44, 0x34, 0x42, 0x42, 0x41, 0x33, 0x44,
    0x46, 0x45, 0x37, 0x33, 0x41, 0x32, 0x36, 0x44, 0x34, 0x38, 0x39, 0x45,
    0x33, 0x44, 0x44, 0x44, 0x33, 0x44, 0x39, 0x41, 0x37, 0x43, 0x36, 0x42,
    0x39, 0x34, 0x37, 0x38, 0x33, 0x30, 0x32, 0x33, 0x33, 0x44, 0x33, 0x37,
    0x33, 0x45, 0x43, 0x42, 0x36, 0x37, 0x32, 0x37, 0x41, 0x43, 0x43, 0x41,
    0x34, 0x32, 0x31, 0x43, 0x0D, 0x39, 0x36, 0x43, 0x39
};

const uint8_t Response_DCA0078[] PROGMEM = 
{
    0x01, 0x44, 0x44, 0x43, 0x44, 0x33, 0x32, 0x46, 0x43, 0x45, 0x42, 0x45, 0x37, 0x37, 0x30, 0x41,
    0x37, 0x36, 0x41, 0x42, 0x30, 0x39, 0x36, 0x39, 0x30, 0x33, 0x30, 0x37, 0x34, 0x38, 0x42, 0x32,
    0x35, 0x36, 0x46, 0x43, 0x45, 0x46, 0x45, 0x32, 0x31, 0x35, 0x35, 0x38, 0x44, 0x38, 0x46, 0x45,
    0x41, 0x46, 0x37, 0x44, 0x32, 0x35, 0x46, 0x35, 0x45, 0x37, 0x36, 0x45, 0x35, 0x36, 0x35, 0x44,
    0x41, 0x34, 0x34, 0x39, 0x46, 0x42, 0x46, 0x36, 0x45, 0x38, 0x31, 0x46, 0x33, 0x45, 0x31, 0x46,
    0x46, 0x38, 0x35, 0x39, 0x43, 0x43, 0x32, 0x35, 0x35, 0x46, 0x38, 0x44, 0x46, 0x41, 0x41, 0x45,
    0x37, 0x41, 0x31, 0x46, 0x33, 0x44, 0x35, 0x38, 0x32, 0x37, 0x41, 0x42, 0x42, 0x35, 0x41, 0x46,
    0x42, 0x37, 0x31, 0x45, 0x42, 0x32, 0x44, 0x37, 0x42, 0x44, 0x45, 0x39, 0x46, 0x38, 0x36, 0x45,
    0x42, 0x45, 0x37, 0x33, 0x30, 0x37, 0x32, 0x36, 0x36, 0x45, 0x31, 0x46, 0x30, 0x41, 0x39, 0x35,
    0x44, 0x34, 0x42, 0x35, 0x46, 0x31, 0x34, 0x31, 0x44, 0x37, 0x30, 0x42, 0x42, 0x34, 0x46, 0x36,
    0x33, 0x35, 0x36, 0x38, 0x45, 0x34, 0x46, 0x33, 0x43, 0x35, 0x42, 0x41, 0x35, 0x45, 0x42, 0x35,
    0x44, 0x41, 0x46, 0x41, 0x34, 0x36, 0x38, 0x46, 0x41, 0x36, 0x38, 0x46, 0x39, 0x30, 0x39, 0x37,
    0x45, 0x31, 0x33, 0x30, 0x41, 0x44, 0x43, 0x34, 0x46, 0x31, 0x37, 0x35, 0x37, 0x42, 0x37, 0x45,
    0x36, 0x30, 0x33, 0x42, 0x41, 0x46, 0x35, 0x44, 0x35, 0x38, 0x45, 0x37, 0x45, 0x34, 0x45, 0x33,
    0x31, 0x32, 0x31, 0x46, 0x34, 0x36, 0x44, 0x31, 0x46, 0x41, 0x41, 0x45, 0x37, 0x41, 0x31, 0x46,
    0x33, 0x44, 0x0D, 0x43, 0x46, 0x32, 0x46
};

const uint8_t Response_DCB0078[] PROGMEM = 
{
    0x01, 0x44, 0x44, 0x43, 0x41, 0x41, 0x44, 0x43, 0x36, 0x46, 0x45, 0x45, 0x35, 0x30, 0x32, 0x34,
    0x34, 0x37, 0x34, 0x39, 0x41, 0x36, 0x34, 0x46, 0x45, 0x38, 0x38, 0x32, 0x30, 0x35, 0x37, 0x46,
    0x35, 0x38, 0x43, 0x45, 0x44, 0x45, 0x35, 0x30, 0x38, 0x32, 0x35, 0x35, 0x37, 0x31, 0x43, 0x41,
    0x32, 0x43, 0x33, 0x37, 0x43, 0x31, 0x45, 0x35, 0x30, 0x33, 0x36, 0x34, 0x46, 0x31, 0x31, 0x37,
    0x43, 0x36, 0x38, 0x39, 0x34, 0x30, 0x46, 0x42, 0x35, 0x42, 0x37, 0x44, 0x36, 0x44, 0x41, 0x37,
    0x32, 0x45, 0x30, 0x39, 0x43, 0x37, 0x35, 0x42, 0x44, 0x36, 0x45, 0x35, 0x38, 0x39, 0x36, 0x38,
    0x43, 0x34, 0x42, 0x44, 0x37, 0x41, 0x31, 0x31, 0x34, 0x35, 0x41, 0x44, 0x35, 0x45, 0x46, 0x31,
    0x37, 0x42, 0x31, 0x36, 0x41, 0x46, 0x38, 0x46, 0x46, 0x41, 0x45, 0x30, 0x37, 0x42, 0x45, 0x44,
    0x45, 0x36, 0x36, 0x35, 0x44, 0x41, 0x46, 0x41, 0x34, 0x36, 0x38, 0x46, 0x41, 0x36, 0x38, 0x46,
    0x39, 0x30, 0x39, 0x37, 0x45, 0x31, 0x33, 0x30, 0x41, 0x44, 0x43, 0x34, 0x46, 0x31, 0x37, 0x35,
    0x37, 0x42, 0x37, 0x45, 0x36, 0x30, 0x33, 0x42, 0x41, 0x46, 0x35, 0x44, 0x35, 0x38, 0x45, 0x37,
    0x45, 0x34, 0x45, 0x33, 0x31, 0x32, 0x31, 0x46, 0x34, 0x36, 0x44, 0x31, 0x46, 0x41, 0x41, 0x45,
    0x37, 0x41, 0x31, 0x46, 0x33, 0x44, 0x35, 0x38, 0x32, 0x37, 0x41, 0x42, 0x42, 0x35, 0x41, 0x46,
    0x42, 0x37, 0x31, 0x45, 0x42, 0x32, 0x44, 0x37, 0x42, 0x44, 0x45, 0x39, 0x46, 0x38, 0x36, 0x45,
    0x42, 0x45, 0x37, 0x33, 0x30, 0x37, 0x32, 0x36, 0x36, 0x45, 0x31, 0x46, 0x30, 0x41, 0x39, 0x35,
    0x44, 0x34, 0x0D, 0x33, 0x45, 0x33, 0x35
};

const uint8_t Response_F21D57[] PROGMEM = { 0x01, 0x46, 0x06, 0x0D, 0x44, 0x34, 0x30, 0x36 };

const uint8_t Response_f7021[] PROGMEM = { 0x01, 0x66, 0x06, 0x0D, 0x35, 0x32, 0x43, 0x30 };

const uint8_t Response_BBA03[] PROGMEM = { 0x01, 0x42, 0x30, 0x33, 0x42, 0x41, 0x0D, 0x43, 0x36, 0x31, 0x36 };

const uint8_t Response_D81DA01[] PROGMEM = { 0x01, 0x44, 0x37, 0x30, 0x0D, 0x44, 0x31, 0x41, 0x44 };

const uint8_t Response_RB970[] PROGMEM = { 0x01, 0x52, 0x06, 0x0D, 0x34, 0x42, 0x41, 0x35 };

const uint8_t Response_T[] PROGMEM = { 0x01, 0x54, 0x06, 0x0D, 0x46, 0x39, 0x30, 0x35 };

const uint8_t Response_W8160[] PROGMEM = { 0x01, 0x57, 0x06, 0x0D, 0x41, 0x30, 0x35, 0x35 };

const uint8_t Response_W811702[] PROGMEM = { 0x01, 0x52, 0x06, 0x0D, 0x34, 0x42, 0x41, 0x35 };

struct CMD 
{
    uint16_t cmd;
    size_t cmd_size;
    uint8_t *responce;
    size_t responce_size;
};

uint16_t crc16(const uint8_t* pcBlock, size_t len)
{
    uint16_t crc = 0x0000;
    unsigned char i;

    while (len--)
    {
        crc ^= *pcBlock++ << 8;

        for (i = 0; i < 8; i++)
            crc = crc & 0x8000 ? (crc << 1) ^ 0x1021 : crc << 1;
    }
    return crc;
}

CMD makeCmd(const uint8_t* cmd, size_t cmd_size, const uint8_t* responce, const size_t responce_size) 
{
    return CMD{crc16(cmd, cmd_size), cmd_size, const_cast<uint8_t*>(responce), (size_t)responce_size};
}

CMD cmds[33] = 
{
    makeCmd(I66F9, sizeof(I66F9), Response_I66F9, sizeof(Response_I66F9)),
    makeCmd(V75B4, sizeof(V75B4), Response_V75B4, sizeof(Response_V75B4)),
    makeCmd(C406, sizeof(C406), Response_C406, sizeof(Response_C406)),
    makeCmd(UGENERIC, sizeof(UGENERIC), Response_UGENERIC, sizeof(Response_UGENERIC)),
    makeCmd(MAA3D, sizeof(MAA3D), Response_MAA3D, sizeof(Response_MAA3D)),
    makeCmd(D816022ADAC, sizeof(D816022ADAC), Response_D816022ADAC, sizeof(Response_D816022ADAC)),
    makeCmd(D81820F2591, sizeof(D81820F2591), Response_D81820F2591, sizeof(Response_D81820F2591)),
    makeCmd(D819241, sizeof(D819241), Response_D819241, sizeof(Response_D819241)),
    makeCmd(D81D306, sizeof(D81D306), Response_D81D306, sizeof(Response_D81D306)),
    makeCmd(D810B01, sizeof(D810B01), Response_D810B01, sizeof(Response_D810B01)),
    makeCmd(D820078, sizeof(D820078), Response_D820078, sizeof(Response_D820078)),
    makeCmd(D830078, sizeof(D830078), Response_D830078, sizeof(Response_D830078)),
    makeCmd(D840078, sizeof(D840078), Response_D840078, sizeof(Response_D840078)),
    makeCmd(D850078, sizeof(D850078), Response_D850078, sizeof(Response_D850078)),
    makeCmd(D860078, sizeof(D860078), Response_D860078, sizeof(Response_D860078)),
    makeCmd(D870078, sizeof(D870078), Response_D870078, sizeof(Response_D870078)),
    makeCmd(DCA0078, sizeof(DCA0078), Response_DCA0078, sizeof(Response_DCA0078)),
    makeCmd(DCB0078, sizeof(DCB0078), Response_DCB0078, sizeof(Response_DCB0078)),
    makeCmd(W81DA011, sizeof(W81DA011), Response_W8160, sizeof(Response_W8160)),
    makeCmd(F21D57, sizeof(F21D57), Response_F21D57, sizeof(Response_F21D57)),
    makeCmd(f7021, sizeof(f7021), Response_f7021, sizeof(Response_f7021)),
    makeCmd(BBA03, sizeof(BBA03), Response_BBA03, sizeof(Response_BBA03)),
    makeCmd(D81DA01, sizeof(D81DA01), Response_D81DA01, sizeof(Response_D81DA01)),
    makeCmd(RB970, sizeof(RB970), Response_RB970, sizeof(Response_RB970)),
    makeCmd(W8117020401, sizeof(W8117020401), Response_W8160, sizeof(Response_W8160)),
    makeCmd(W814018, sizeof(W814018), Response_T, sizeof(Response_T)),
    makeCmd(W81D306, sizeof(W81D306), Response_T, sizeof(Response_T)),
    makeCmd(T, sizeof(T), Response_T, sizeof(Response_T)),
    makeCmd(W8160, sizeof(W8160), Response_W8160, sizeof(Response_W8160)),
    makeCmd(W8182, sizeof(W8182), Response_W8160, sizeof(Response_W8160)),
    makeCmd(W8192, sizeof(W8192), Response_W8160, sizeof(Response_W8160)),
    makeCmd(W81DA0100, sizeof(W81DA0100), Response_W8160, sizeof(Response_W8160)),
    makeCmd(W811702, sizeof(W811702), Response_W811702, sizeof(Response_W811702)),
};

const CMD find(uint8_t* cmd, size_t bytesRead) 
{
    size_t count = sizeof(cmds);
    size_t old_size = 0;
    uint16_t crc = 0;

    for(size_t i = 0; i <= count; i++) 
    {
        CMD result = cmds[i];
        if(bytesRead >= result.cmd_size) 
        {
            if(old_size != result.cmd_size) 
            {
                old_size = result.cmd_size;
                crc = crc16(cmd, result.cmd_size);
            }

            if(result.cmd == crc)
            {
                return result;
            }
        }
    }
    return CMD{0};
}

void setup()
{
    Serial.begin(SERIAL_BAUD_RATE);
}

void sendResponce(const uint8_t *response, size_t responseLen)
{
    uint8_t *buff = (uint8_t *)malloc(responseLen);
    memcpy_P(buff, response, responseLen);

    Serial.write(buff, responseLen);
    free(buff);
}

void loop()
{
    int len = Serial.available();
    if (len > 0)
    {
        uint8_t rxBuff[RX_BUFF_SIZE] = {0};
        size_t bytesRead = Serial.readBytes(rxBuff, RX_BUFF_SIZE);

        if(bytesRead) 
        {
            const CMD cmd = find(rxBuff, bytesRead);
            if(cmd.responce) 
            {
                sendResponce(cmd.responce, cmd.responce_size);
            }
        }
    }
}

Ответы на команды пришлось записать в ПЗУ Arduino с помощью PROGMEM, так как все данные не помещались в оперативную память.

Чтение данных с устройства

Во время выгрузки данных с устройства отображаются сообщения, такие как «Checking monitor availability...», расположенные над прогресс-баром.

Сообщение: «Checking monitor availability...»
Сообщение: «Checking monitor availability...»

Чтобы выяснить, где происходит вызов функции UploadMonitor или UploadMonitorVC, я загрузил библиотеку в Ghidra и нашел соответствующую строку.

Строка: «Checking monitor availability...»
Строка: «Checking monitor availability...»

Вернее, было найдено три таких строки. Две из них находятся в юникоде в ресурсах, а одна — в данных в ANSI; на последнюю ссылаются две функции.

Я загрузил библиотеку в отладчик и поставил точки останова на эти строки. Сработала точка останова в первой функции.

В стеке меня привлекло внимание, что вызов библиотеки происходит из core.dll, одной из библиотек приложения. Я выбрал в стеке соответствующую строку и обнаружил, что это вызов UploadMonitorVC. Это указывает на то, что библиотека core.dll использует ABPDeviceCommunicator.dll и вызывает методы через COM. Однако функция FUN_100016ef не является UploadMonitorVC, так как она также вызывается при инициализации. Вместо этого я переименовал FUN_10006c8d в UploadMonitorVC, так как именно она является ею.

undefined4 UploadMonitorVC(void)
{
  int iVar1;
  int iVar2;
  int iVar3;
  int extraout_ECX;
  int unaff_EBP;
  undefined4 uVar4;
  
  FUN_1000e9d0();
  CByteArray::SetSize((CByteArray *)(extraout_ECX + 0x548),0,-1);
  if (*(int *)(extraout_ECX + 0x518) == 1) {
    (**(code **)(*(int *)(extraout_ECX + 0x504) + 0x34))();
  }
  CString::CString((CString *)(unaff_EBP + -0x18));
  *(undefined4 *)(unaff_EBP + -4) = 0;
  FUN_1000957f();
  *(undefined **)(unaff_EBP + -0x10) = &stack0xffffffdc;
  CString::CString((CString *)&stack0xffffffdc,"Checking monitor availability .....");
  FUN_1000e0e9();
  *(undefined4 *)(extraout_ECX + 0x500) = 0;
  *(undefined4 *)(extraout_ECX + 0x4f8) = 0;
  CString::CString((CString *)(unaff_EBP + -0x14));
  *(undefined *)(unaff_EBP + -4) = 1;
  CString::LoadStringW((CString *)(unaff_EBP + -0x14),0xc35c);
  CString::GetBuffer((CString *)(extraout_ECX + 0xc),0x14);
  iVar1 = OpenComPort();
  if (iVar1 == 0) {
    *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,"Failed to open COM port");
    FUN_1000e0e9();
    FUN_1000957f();
    goto LAB_10007386;
  }
  *(undefined **)(unaff_EBP + -0x10) = &stack0xffffffdc;
  CString::CString((CString *)&stack0xffffffdc,"COM port opened");
  FUN_1000e0e9();
  *(undefined4 *)(extraout_ECX + 0x4f8) = 1;
  if (*(int *)(extraout_ECX + 1280) == 1) goto LAB_100072fb;
  if (*(int *)(extraout_ECX + 1228) == 2) {
    FUN_1000957f();
    iVar1 = FUN_1000d79e();
    if (iVar1 == 0) {
      *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,"Initialize modem failed");
      FUN_1000e0e9();
      CString::CString((CString *)(unaff_EBP + -0x10));
      *(undefined *)(unaff_EBP + -4) = 2;
      CString::CString((CString *)(unaff_EBP + 8));
      *(undefined *)(unaff_EBP + -4) = 3;
      *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      MessageBoxW(*(HWND *)(extraout_ECX + 0x14),*(LPCWSTR *)(unaff_EBP + -0x10),
                  *(LPCWSTR *)(unaff_EBP + 8),0);
      iVar1 = 0x3a9a;
      *(undefined *)(unaff_EBP + -4) = 2;
      CString::~CString((CString *)(unaff_EBP + 8));
      *(undefined *)(unaff_EBP + -4) = 1;
LAB_10007033:
      CString::~CString((CString *)(unaff_EBP + -0x10));
LAB_10007253:
      if (*(int *)(extraout_ECX + 0x4cc) == 2) {
        iVar2 = *(int *)(extraout_ECX + 0x4f8);
        if (((iVar2 == 0) || (iVar2 == 2)) || (iVar2 == 1)) {
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"No modem connection has been made..");
          FUN_1000e0e9();
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Have to close the port");
          FUN_1000e0e9();
        }
        else {
          FUN_1000957f();
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Breaking the modems\' connection....");
          FUN_1000e0e9();
          *(undefined4 *)(extraout_ECX + 0x4f8) = 4;
          if (*(int *)(extraout_ECX + 0x500) == 1) goto LAB_100072fb;
          FUN_1000dca5();
        }
      }
      *(undefined4 *)(extraout_ECX + 0x4f8) = 9;
      if (*(int *)(extraout_ECX + 0x500) != 1) {
        FUN_1000a781(extraout_ECX);
        *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
        CString::CString((CString *)&stack0xffffffdc,"Closing COM port");
        FUN_1000e0e9();
        if (iVar1 == 0) {
          CString::operator=((CString *)(extraout_ECX + 0x18),(CString *)(extraout_ECX + 0xc));
          FUN_1000957f();
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Monitor successfully read");
          FUN_1000e0e9();
          uVar4 = 1;
          goto LAB_10007388;
        }
        FUN_1000957f();
        *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
        CString::CString((CString *)&stack0xffffffdc,"Monitor read failed");
        FUN_1000e0e9();
        goto LAB_10007386;
      }
    }
    else {
      *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,"Initialize modem succeeded");
      FUN_1000e0e9();
      *(undefined4 *)(extraout_ECX + 0x4f8) = 2;
      if (*(int *)(extraout_ECX + 0x500) != 1) {
        FUN_1000957f();
        iVar1 = FUN_1000dbb4();
        if (iVar1 == 0) {
          *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Connecting the modems failed");
          FUN_1000e0e9();
          CString::CString((CString *)(unaff_EBP + -0x10));
          *(undefined *)(unaff_EBP + -4) = 4;
          CString::CString((CString *)(unaff_EBP + 8));
          *(undefined *)(unaff_EBP + -4) = 5;
          *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffd4;
          CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
          FUN_1000871b();
          *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffd4;
          CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
          FUN_1000871b();
          MessageBoxW(*(HWND *)(extraout_ECX + 0x14),*(LPCWSTR *)(unaff_EBP + -0x10),
                      *(LPCWSTR *)(unaff_EBP + 8),0);
          iVar1 = 0x3a9a;
          *(undefined *)(unaff_EBP + -4) = 4;
          CString::~CString((CString *)(unaff_EBP + 8));
          *(undefined *)(unaff_EBP + -4) = 1;
          goto LAB_10007033;
        }
        *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffdc;
        CString::CString((CString *)&stack0xffffffdc,"Connecting the modems succeeded");
        FUN_1000e0e9();
        *(undefined4 *)(extraout_ECX + 0x4f8) = 3;
        if (*(int *)(extraout_ECX + 0x500) != 1) goto LAB_10006f5e;
      }
    }
LAB_100072fb:
    FUN_1000dd49();
  }
  else {
LAB_10006f5e:
    iVar1 = DetectMonitorVersion();
    if (iVar1 == -1) goto LAB_10007386;
    if ((iVar1 != 0) && (iVar1 != 0x3b86)) {
      *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,"Connecting with monitor failed");
      FUN_1000e0e9();
      CString::CString((CString *)(unaff_EBP + -0x10));
      *(undefined *)(unaff_EBP + -4) = 6;
      CString::CString((CString *)(unaff_EBP + 8));
      *(undefined *)(unaff_EBP + -4) = 7;
      *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffd8;
      CString::CString((CString *)&stack0xffffffd8,(CString *)(unaff_EBP + -0x10));
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
      *(undefined *)(unaff_EBP + -4) = 8;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(unaff_EBP + 8));
      *(undefined *)(unaff_EBP + -4) = 7;
      FUN_1000a511();
      *(undefined *)(unaff_EBP + -4) = 6;
      CString::~CString((CString *)(unaff_EBP + 8));
      *(undefined *)(unaff_EBP + -4) = 1;
      goto LAB_10007033;
    }
    *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,"Connecting with monitor succeeded");
    FUN_1000e0e9();
    *(undefined4 *)(extraout_ECX + 0x4f8) = 5;
    if (*(int *)(extraout_ECX + 0x500) == 1) goto LAB_100072fb;
    if ((iVar1 == 0) || (iVar1 == 15238)) {
      CString::LoadStringW((CString *)(unaff_EBP + -0x14),50012);
      iVar1 = EstablishLink();
      *(undefined4 *)(extraout_ECX + 0x4f8) = 8;
      if ((*(int *)(extraout_ECX + 0x500) == 1) && (iVar2 = FUN_1000dd49(), iVar2 != 0))
      goto LAB_10007386;
    }
    iVar2 = *(int *)(unaff_EBP + 8);
    if (iVar1 == 0) {
      *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,"Establish Link for read to Monitor succeeded");
      FUN_1000e0e9();
      iVar1 = FUN_100074d9();
    }
    else {
      *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,"Establish Link for read to Monitor failed");
      FUN_1000e0e9();
    }
    if (iVar1 != -1) {
      *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,"Read raw readings from monitor");
      FUN_1000e0e9();
      if ((*(int *)(extraout_ECX + 0x500) != 1) || (iVar3 = FUN_1000dd49(), iVar3 == 0)) {
        if (iVar1 == 0) {
          *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Read raw readings from monitor succeeded");
          FUN_1000e0e9();
          iVar1 = FUN_1000866c(*(undefined4 *)(iVar2 + 0xc));
        }
        else if (iVar1 != 0x3a9a) {
          *(undefined **)(unaff_EBP + 8) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Read raw readings from monitor failed");
          FUN_1000e0e9();
          CString::CString((CString *)(unaff_EBP + 8));
          *(undefined *)(unaff_EBP + -4) = 9;
          CString::CString((CString *)(unaff_EBP + -0x10));
          *(undefined *)(unaff_EBP + -4) = 10;
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
          CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
          FUN_1000871b();
          if (iVar1 == 0x3b94) {
            *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
          }
          else if (iVar1 == 0x3b98) {
            *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
          }
          else {
            *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
          }
          CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
          FUN_1000871b();
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd8;
          CString::CString((CString *)&stack0xffffffd8,(CString *)(unaff_EBP + -0x10));
          *(undefined **)(unaff_EBP + -0x1c) = &stack0xffffffd4;
          *(undefined *)(unaff_EBP + -4) = 0xb;
          CString::CString((CString *)&stack0xffffffd4,(CString *)(unaff_EBP + 8));
          *(undefined *)(unaff_EBP + -4) = 10;
          FUN_1000a511();
          *(undefined *)(unaff_EBP + -4) = 9;
          CString::~CString((CString *)(unaff_EBP + -0x10));
          *(undefined *)(unaff_EBP + -4) = 1;
          CString::~CString((CString *)(unaff_EBP + 8));
        }
        if ((*(int *)(extraout_ECX + 0x500) != 1) || (iVar2 = FUN_1000dd49(), iVar2 == 0))
        goto LAB_10007253;
      }
    }
  }
LAB_10007386:
  uVar4 = 0;
LAB_10007388:
  *(undefined *)(unaff_EBP + -4) = 0;
  CString::~CString((CString *)(unaff_EBP + -0x14));
  *(undefined4 *)(unaff_EBP + -4) = 0xffffffff;
  CString::~CString((CString *)(unaff_EBP + -0x18));
  ExceptionList = *(void **)(unaff_EBP + -0xc);
  return uVar4;
}

Функция OpenComPort

undefined4 OpenComPort(void)
{
  LPCWSTR _Source;
  undefined4 uVar1;
  int iVar2;
  HANDLE pvVar3;
  BOOL BVar4;
  int extraout_ECX;
  undefined4 uVar5;
  int unaff_EBP;
  
  FUN_1000e9d0();
  _Source = *(LPCWSTR *)(unaff_EBP + 8);
  CString::CString((CString *)(unaff_EBP + 8),(ushort *)_Source);
  uVar5 = 0;
  *(undefined4 *)(unaff_EBP + -4) = 0;
  iVar2 = CString::Find((CString *)(unaff_EBP + 8),(ushort *)&this_10015028);
  if (iVar2 + 4 < *(int *)(*(int *)(unaff_EBP + 8) + -8)) {
    swprintf((wchar_t *)(unaff_EBP + -0x23c),0x10015018,_Source);
  }
  else {
    wcscpy((wchar_t *)(unaff_EBP + -0x23c),_Source);
  }
  if (*(int *)(extraout_ECX + 0x4cc) == 1) {
    pvVar3 = CreateFileW((LPCWSTR)(unaff_EBP + -0x23c),0xc0000000,0,(LPSECURITY_ATTRIBUTES)0x0,3,0,
                         (HANDLE)0x0);
    *(HANDLE *)(extraout_ECX + 0x2c) = pvVar3;
    if (pvVar3 == (HANDLE)0xffffffff) goto LAB_1000a762;
    *(undefined4 *)(unaff_EBP + -0x3c) = 0x1c;
    GetCommState(pvVar3,(LPDCB)(unaff_EBP + -0x3c));
    uVar1 = *(undefined4 *)(extraout_ECX + 0x43c);
    *(undefined *)(unaff_EBP + -0x2a) = 8;
    *(undefined4 *)(unaff_EBP + -0x38) = uVar1;
    *(undefined *)(unaff_EBP + -0x29) = 0;
    *(undefined *)(unaff_EBP + -0x28) = 0;
    *(uint *)(unaff_EBP + -0x34) =
         CONCAT31((uint3)((uint)*(undefined4 *)(unaff_EBP + -0x34) >> 8) & 0xffff80,1);
    BVar4 = SetCommState(*(HANDLE *)(extraout_ECX + 0x2c),(LPDCB)(unaff_EBP + -0x3c));
    if (BVar4 == 0) goto LAB_1000a762;
    GetCommTimeouts(*(HANDLE *)(extraout_ECX + 0x2c),(LPCOMMTIMEOUTS)(unaff_EBP + -0x20));
    *(undefined4 *)(unaff_EBP + -0x1c) = 0;
    *(undefined4 *)(unaff_EBP + -0x20) = 1000;
    *(undefined4 *)(unaff_EBP + -0x18) = 1000;
    *(undefined4 *)(unaff_EBP + -0x14) = 10;
    *(undefined4 *)(unaff_EBP + -0x10) = 20000;
    iVar2 = SetCommTimeouts(*(HANDLE *)(extraout_ECX + 0x2c),(LPCOMMTIMEOUTS)(unaff_EBP + -0x20));
LAB_1000a72e:
    if (iVar2 != 0) {
      uVar5 = 1;
    }
  }
  else {
    pvVar3 = CreateFileW(_Source,0xc0000000,0,(LPSECURITY_ATTRIBUTES)0x0,3,0x40000080,(HANDLE)0x0);
    *(HANDLE *)(extraout_ECX + 0x2c) = pvVar3;
    if (pvVar3 == (HANDLE)0xffffffff) goto LAB_1000a762;
    *(undefined4 *)(unaff_EBP + -0x20) = 0xffffffff;
    *(undefined4 *)(unaff_EBP + -0x1c) = 0;
    *(undefined4 *)(unaff_EBP + -0x18) = 0;
    *(undefined4 *)(unaff_EBP + -0x14) = 0;
    *(undefined4 *)(unaff_EBP + -0x10) = 5000;
    SetCommTimeouts(pvVar3,(LPCOMMTIMEOUTS)(unaff_EBP + -0x20));
    pvVar3 = CreateEventW((LPSECURITY_ATTRIBUTES)0x0,1,0,(LPCWSTR)0x0);
    *(HANDLE *)(extraout_ECX + 0x4e0) = pvVar3;
    pvVar3 = CreateEventW((LPSECURITY_ATTRIBUTES)0x0,1,0,(LPCWSTR)0x0);
    *(HANDLE *)(extraout_ECX + 0x4f4) = pvVar3;
    *(undefined4 *)(unaff_EBP + -0x3c) = 0x1c;
    GetCommState(*(HANDLE *)(extraout_ECX + 0x2c),(LPDCB)(unaff_EBP + -0x3c));
    *(undefined4 *)(unaff_EBP + -0x38) = 0x4b0;
    *(undefined *)(unaff_EBP + -0x2a) = 8;
    BVar4 = SetCommState(*(HANDLE *)(extraout_ECX + 0x2c),(LPDCB)(unaff_EBP + -0x3c));
    if (BVar4 != 0) {
      BVar4 = SetupComm(*(HANDLE *)(extraout_ECX + 0x2c),10000,10000);
      if (((BVar4 != 0) && (*(int *)(extraout_ECX + 0x4e0) != 0)) &&
         (*(int *)(extraout_ECX + 0x4f4) != 0)) {
        iVar2 = EscapeCommFunction(*(HANDLE *)(extraout_ECX + 0x2c),5);
        goto LAB_1000a72e;
      }
    }
    GetLastError();
    if (*(HANDLE *)(extraout_ECX + 0x4e0) != (HANDLE)0x0) {
      CloseHandle(*(HANDLE *)(extraout_ECX + 0x4e0));
    }
    if (*(HANDLE *)(extraout_ECX + 0x4f4) != (HANDLE)0x0) {
      CloseHandle(*(HANDLE *)(extraout_ECX + 0x4f4));
    }
    CloseHandle(*(HANDLE *)(extraout_ECX + 0x2c));
  }
LAB_1000a762:
  *(undefined4 *)(unaff_EBP + -4) = 0xffffffff;
  CString::~CString((CString *)(unaff_EBP + 8));
  ExceptionList = *(void **)(unaff_EBP + -0xc);
  return uVar5;
}

Функция DetectMonitorVersion реализует интересную логику: она последовательно перебирает скорости соединения — 4800, 1200, 2400 и 9600 бод — до тех пор, пока не получит ответ на команду I, которая запрашивает версию устройства.

Такая логика предназначена для работы с устройствами, поддерживающими разные скорости соединения. Однако имеется только одно устройство, функционирующее на скорости 9600 бод.

Это делает текущую логику неактуальной, и её можно просто не брать во внимание.

BOOL DetectMonitorVersion(void)
{
  BOOL BVar1;
  CString *pCVar2;
  void *in_ECX;
  int iVar3;
  wchar_t *pwStack_20;
  wchar_t *pwStack_1c;
  wchar_t *pwStack_18;
  int iStack_14;
  void *pvStack_10;
  undefined *puStack_c;
  undefined4 uStack_8;
  
  uStack_8 = 0xffffffff;
  puStack_c = &LAB_1000f8ec;
  pvStack_10 = ExceptionList;
  iVar3 = 0;
  ExceptionList = &pvStack_10;
  memset((void *)((int)in_ECX + 0x131),0,0x101);
  memset((void *)((int)in_ECX + 0x232),0,0x101);
  if (*(int *)((int)in_ECX + 0x4cc) == 2) { // Под отладкой условие не выполняется
    BVar1 = EscapeCommFunction(*(HANDLE *)((int)in_ECX + 0x2c),5);
    if (BVar1 == 0) {
      ExceptionList = pvStack_10;
      return 0;
    }
    do {
      iStack_14 = SendCommand("I",0x1f);
      CString::CString((CString *)&pwStack_1c,(char *)&this_100157d4);
      uStack_8 = 0;
      if (iStack_14 == 0) {
        uStack_8 = 0xffffffff;
        CString::~CString((CString *)&pwStack_1c);
        goto LAB_1000aaa8;
      }
      if (*(int *)((int)in_ECX + 0x500) == 1) {
        FUN_1000dd49();
        uStack_8 = 0xffffffff;
        CString::~CString((CString *)&pwStack_1c);
        goto LAB_1000abd1;
      }
      uStack_8 = 0xffffffff;
      CString::~CString((CString *)&pwStack_1c);
      iVar3 = iVar3 + 1;
    } while (iVar3 < 0x1e);
  }
  else {
    pwStack_1c = (wchar_t *)0x0;
    do {
	                /* В цикле оправляем команду I и если не
                       смогли прочитать данные меняем скорость
                       соединения и пытаемся еще раз оправить
                       команду. Повторяем до тех пор, пока число
                       попыток не достигнет 2 */

      iStack_14 = SendCommand("I",0x1f);
      if (iStack_14 == 0) goto LAB_1000aaa8;
      if (*(int *)((int)in_ECX + 0x500) == 1) goto LAB_1000abcc;
      Sleep(200);
      FUN_1000a863(in_ECX,0x12c0);
      iStack_14 = SendCommand("I",0x1f);
      if (iStack_14 == 0) {
        *(undefined4 *)((int)in_ECX + 0x43c) = 0x12c0;
        goto LAB_1000aaa8;
      }
      if (*(int *)((int)in_ECX + 0x500) == 1) goto LAB_1000abcc;
      Sleep(200);
      FUN_1000a863(in_ECX,0x4b0);
      iStack_14 = SendCommand("I",0x1f);
      if (iStack_14 == 0) {
        *(undefined4 *)((int)in_ECX + 0x43c) = 0x4b0;
        goto LAB_1000aaa8;
      }
      if (*(int *)((int)in_ECX + 0x500) == 1) goto LAB_1000abcc;
      Sleep(200);
      FUN_1000a863(in_ECX,0x2580);
      iStack_14 = SendCommand("I",0x1f);
      if (iStack_14 == 0) {
        *(undefined4 *)((int)in_ECX + 0x43c) = 0x2580;
        goto LAB_1000aaa8;
      }
      if (*(int *)((int)in_ECX + 0x500) == 1) goto LAB_1000abcc;
      Sleep(200);
      FUN_1000a863(in_ECX,0x960);
      iStack_14 = SendCommand("I",0x1f);
      if (iStack_14 == 0) {
        *(undefined4 *)((int)in_ECX + 0x43c) = 0x960;
        break;
      }
      pwStack_1c = (wchar_t *)((int)pwStack_1c + 1);
    } while ((int)pwStack_1c < 2);
  }
  if (iStack_14 == 0) {
LAB_1000aaa8:
    CString::CString((CString *)&pwStack_18);
    uStack_8 = 1;
    CString::operator=((CString *)((int)in_ECX + 0x444),(char *)((int)in_ECX + 0x232));
    pCVar2 = (CString *)CString::Mid((CString *)((int)in_ECX + 0x444),&pwStack_20,4);
    uStack_8._0_1_ = 2;
    CString::operator=((CString *)&pwStack_18,pCVar2);
    uStack_8 = CONCAT31(uStack_8._1_3_,1);
    CString::~CString((CString *)&pwStack_20);
    CString::CString((CString *)&pwStack_1c,"202");
    iVar3 = wcscmp(pwStack_18,pwStack_1c);
    CString::~CString((CString *)&pwStack_1c);
    if (iVar3 == 0) {
      *(undefined4 *)((int)in_ECX + 0x440) = 0xca;
    }
    else {
      CString::CString((CString *)&pwStack_1c,"207");
      iVar3 = wcscmp(pwStack_18,pwStack_1c);
      CString::~CString((CString *)&pwStack_1c);
      if (iVar3 == 0) {
        *(undefined4 *)((int)in_ECX + 0x440) = 0xcf;
      }
      else {
        CString::CString((CString *)&pwStack_20,"217");
        iVar3 = wcscmp(pwStack_18,pwStack_20);
        CString::~CString((CString *)&pwStack_20);
        if (iVar3 == 0) {
          *(undefined4 *)((int)in_ECX + 0x440) = 0xd9;
        }
        else {
          *(undefined4 *)((int)in_ECX + 0x440) = 0;
          iStack_14 = 0x3b7f;
        }
      }
    }
    uStack_8 = 0xffffffff;
    CString::~CString((CString *)&pwStack_18);
    if ((iStack_14 == 0) && (*(int *)((int)in_ECX + 0x440) != 0xca)) {
      if (*(int *)((int)in_ECX + 0x500) == 1) {
LAB_1000abcc:
        FUN_1000dd49();
LAB_1000abd1:
        iStack_14 = -1;
      }
      else {
        iStack_14 = FUN_1000c2be((int)in_ECX);
      }
    }
  }
  ExceptionList = pvStack_10;
  return iStack_14;
}

Функция SendCommand:

int SendCommand(undefined4 param_1,undefined4 param_2)
{
  bool bVar1;
  undefined3 extraout_var;
  int iVar2;
  
  bVar1 = Command2Com(); // Формирует пакет из команды и оправляет его
  if (CONCAT31(extraout_var,bVar1) == 0) {
    iVar2 = 0x20;
  }
  else {
    iVar2 = GetResponseData(); // Читает ответ и расшифровывает его
  }
  return iVar2;
}

Функция Command2Com формирует пакет, имеющий следующую структуру: 0x01 + команда + возврат каретки + CRC16(0x01 + команда + возврат каретки). После этого пакет отправляется.

bool Command2Com(void)
{
  char *_Dest;
  char cVar1;
  char *_Source;
  size_t sVar2;
  uint uVar3;
  int iVar4;
  void *this;
  CString *this_00;
  CString *this_01;
  int unaff_EBP;
  
  FUN_1000e9d0();
  CString::CString((CString *)(unaff_EBP + -0x10));
  _Source = *(char **)(unaff_EBP + 8);
  *(undefined4 *)(unaff_EBP + -4) = 0;
  CString::Format(this_00,(ushort *)(unaff_EBP + -0x10));
  *(undefined **)(unaff_EBP + 8) = &stack0xffffffe8;
  CString::CString((CString *)&stack0xffffffe8,(CString *)(unaff_EBP + -0x10));
  FUN_1000e0e9();
  _Dest = (char *)((int)this + 0x30);
  strcpy(_Dest, 0x01); // Начало пакета
  if (*_Source == 'c') {
    cVar1 = _Source[3];
    *(undefined4 *)(unaff_EBP + -0x14) = 0;
    *(uint *)(unaff_EBP + 8) = (uint)(byte)(cVar1 + 9);
    iVar4 = (byte)(cVar1 + 9) - 1;
    if (0 < iVar4) {
      uVar3 = 0;
      do {
        cVar1 = _Source[uVar3];
        *(int *)(unaff_EBP + -0x14) = *(int *)(unaff_EBP + -0x14) + 1;
        *(char *)(uVar3 + 0x31 + (int)this) = cVar1;
        uVar3 = (uint)*(ushort *)(unaff_EBP + -0x14);
      } while ((int)uVar3 < iVar4);
    }
  }
  else {
    strcat(_Dest,_Source); // Команда
    strcat(_Dest, 0x0D); // Возврат каретки
    sVar2 = strlen(_Dest);
    FUN_1000ad6b((byte *)((int)this + 0x31),(sVar2 & 0xffff) - 1); // crc16(0x01 + команда + возврат каретки)
    sprintf((char *)((sVar2 & 0xffff) + 0x30 + (int)this),(char *)&_Format_100150b8);
    sVar2 = strlen(_Dest);
    *(size_t *)(unaff_EBP + 8) = sVar2;
  }
  memset((void *)((int)this + 0x131),0,0x101);
  memset((void *)((int)this + 0x232),0,0x101);
  CString::Format(this_01,(ushort *)(unaff_EBP + -0x10));
  *(undefined **)(unaff_EBP + -0x14) = &stack0xffffffec;
  CString::CString((CString *)&stack0xffffffec,(CString *)(unaff_EBP + -0x10));
  FUN_1000e0e9();
  if (*(int *)((int)this + 0x4cc) == 1) {
    iVar4 = WriteFile(*(HANDLE *)((int)this + 0x2c),_Dest,(uint)*(ushort *)(unaff_EBP + 8),
                      (LPDWORD)(unaff_EBP + -0x18),(LPOVERLAPPED)0x0);
  }
  else {
    iVar4 = FUN_1000d43e(this,_Dest,(uint)*(ushort *)(unaff_EBP + 8));
  }
  *(undefined4 *)(unaff_EBP + -4) = 0xffffffff;
  CString::~CString((CString *)(unaff_EBP + -0x10));
  ExceptionList = *(void **)(unaff_EBP + -0xc);
  return iVar4 != 0;
}

Функция GetResponseData получает данные, парсит их и расшифровывает, если команда начинается с символа D. В этом случае контрольная сумма представляет собой 16-битный ключ для расшифровки данных.

Структура ответа на команды, начинающиеся с D, выглядит следующим образом: 0x01 + D + зашифрованные данные + возврат каретки + CRC16(расшифрованные данные + возврат каретки).

int GetResponseData(void)
{
  LPCSTR lpString;
  void *_Dst;
  uchar uVar1;
  ushort uVar2;
  HANDLE hHandle;
  size_t sVar3;
  int iVar4;
  char *_Str;
  ulong uVar5;
  void *in_ECX;
  CHAR *pCVar6;
  char *in_stack_00000004;
  char **_EndPtr;
  DWORD dwMilliseconds;
  CByteArray aCStack_58 [4];
  int iStack_54;
  int iStack_50;
  uchar auStack_44 [4];
  char acStack_40 [4];
  uchar uStack_3c;
  undefined3 uStack_3b;
  char acStack_38 [4];
  int iStack_34;
  int iStack_30;
  uint uStack_2c;
  int iStack_28;
  uint uStack_24;
  LPCSTR pCStack_20;
  int iStack_1c;
  int iStack_18;
  int iStack_14;
  void *pvStack_10;
  undefined *puStack_c;
  undefined4 uStack_8;
  
  uStack_8 = 0xffffffff;
  puStack_c = &LAB_1000f914;
  pvStack_10 = ExceptionList;
  iStack_18 = 1;
  ExceptionList = &pvStack_10;
  *(undefined *)((int)in_ECX + 0x232) = 0;
  lpString = (LPCSTR)((int)in_ECX + 0x232);
  iStack_28 = 0;
  iStack_30 = 0;
  uStack_24 = 0;
  pCStack_20 = (LPCSTR)0x0;
  uStack_2c = 0;
  iStack_34 = 3;
  do {
    if (*(int *)((int)in_ECX + 0x4cc) == 1) {
      memset((void *)((int)in_ECX + 0x131),0,0x101);
      iStack_1c = FUN_1000b0db();
      iStack_14 = 0;
      if (0 < iStack_1c) {
        do {
          CByteArray::SetAtGrow
                    ((CByteArray *)((int)in_ECX + 0x548),*(int *)((int)in_ECX + 0x550),
                     *(uchar *)((int)in_ECX + 0x131 + iStack_14));
          iStack_14 = iStack_14 + 1;
        } while (iStack_14 < iStack_1c);
        goto LAB_1000af3f;
      }
    }
    else {
      CByteArray::CByteArray(aCStack_58);
      uStack_8 = 0;
      _Dst = (void *)((int)in_ECX + 0x131);
      while( true ) {
        memset(_Dst,0,0x101);
        dwMilliseconds = *(DWORD *)((int)in_ECX + 0x544);
        hHandle = GetCurrentThread();
        WaitForSingleObject(hHandle,dwMilliseconds);
        iStack_1c = FUN_1000b0db();
        iStack_14 = 0;
        if (iStack_1c < 1) break;
        do {
          _uStack_3c = CONCAT31(uStack_3b,*(uchar *)((int)_Dst + iStack_14));
          CByteArray::SetAtGrow(aCStack_58,iStack_50,*(uchar *)((int)_Dst + iStack_14));
          iStack_14 = iStack_14 + 1;
        } while (iStack_14 < iStack_1c);
        if ((iStack_1c < 1) || ((int)in_stack_00000004 <= iStack_50)) break;
      }
      iStack_14 = 0;
      if (0 < iStack_50) {
        do {
          uVar1 = *(uchar *)(iStack_54 + iStack_14);
          auStack_44[0] = uVar1;
          *(uchar *)((int)_Dst + iStack_14) = uVar1;
          CByteArray::SetAtGrow
                    ((CByteArray *)((int)in_ECX + 0x548),*(int *)((int)in_ECX + 0x550),uVar1);
          iStack_14 = iStack_14 + 1;
        } while (iStack_14 < iStack_50);
      }
      iStack_1c = iStack_50;
      CByteArray::SetSize(aCStack_58,0,-1);
      uStack_8 = 0xffffffff;
      CByteArray::~CByteArray(aCStack_58);
LAB_1000af3f:
      pCVar6 = (CHAR *)((int)in_ECX + 0x131);
      if (0 < iStack_1c) {
        iStack_18 = 0;
        do {
          if (*pCVar6 == '\x01') {
            *lpString = '\0';
            uStack_24 = 0;
            iStack_30 = 0;
            pCStack_20 = (LPCSTR)0x0;
            iStack_28 = 0;
            uStack_2c = 0;
          }
          else if (*pCVar6 == '\r') {
            iStack_30 = 1;
            uStack_2c = uStack_24;
            pCStack_20 = lpString + uStack_24;
          }
          lpString[uStack_24] = *pCVar6;
          *(undefined *)((int)in_ECX + uStack_24 + 0x233) = 0;
          uStack_24 = uStack_24 + 1;
          if (0x101 < (int)uStack_24) {
            iStack_18 = 0x3b8b;
            break;
          }
          pCVar6 = pCVar6 + 1;
        } while ((int)(pCVar6 + (-0x131 - (int)in_ECX)) < iStack_1c);
      }
    }
    if ((iStack_30 != 0) && (sVar3 = strlen(pCStack_20), 4 < sVar3)) {
      iStack_28 = 1;
    }
    iStack_34 = iStack_34 + -1;
    if (iStack_34 < 1) {
      iStack_18 = 0x3b80;
      if (iStack_28 == 0) {
        ExceptionList = pvStack_10;
        return 0x3b80;
      }
LAB_1000aff8:
      if (*(char *)((int)in_ECX + 0x233) == '\x15') {
        ExceptionList = pvStack_10;
        return 0x3b88;
      }
      if (*(char *)((int)in_ECX + 0x234) == '\x15') {
        ExceptionList = pvStack_10;
        return 0x3b89;
      }
      if (*(char *)((int)in_ECX + 0x233) == 'D') {
        FUN_1000b1f8(in_ECX,(char *)((int)in_ECX + 0x32));
        iVar4 = lstrlenA(lpString);
        FUN_1000b273((LPCSTR)((int)in_ECX + 0x234),(int)in_ECX + 0x333,iVar4);
        *(undefined *)((uStack_2c >> 1) + 0x332 + (int)in_ECX) = 0xd;
        uVar2 = FUN_1000ad6b((byte *)((int)in_ECX + 0x333),uStack_2c >> 1);
        strcpy(acStack_40,pCStack_20 + 1);
        _EndPtr = &stack0x00000004;
        _Str = acStack_40;
      }
      else {
        uVar2 = FUN_1000ad6b((byte *)((int)in_ECX + 0x233),uStack_2c);
        strcpy(acStack_38,pCStack_20 + 1);
        _EndPtr = (char **)auStack_44;
        _Str = acStack_38;
      }
      uVar5 = strtoul(_Str,_EndPtr,0x10);
      ExceptionList = pvStack_10;
      return -(uint)(uVar2 != uVar5) & 0x3b8a;
    }
    if (iStack_28 != 0) goto LAB_1000aff8;
    if (iStack_18 != 0) {
      ExceptionList = pvStack_10;
      return iStack_18;
    }
  } while( true );
} 

Функция EstablishLink

int __thiscall EstablishLink(void *this)

{
  int iVar1;
  undefined4 *unaff_FS_OFFSET;
  CString aCStack_18 [4];
  CString aCStack_14 [4];
  undefined4 uStack_10;
  undefined *puStack_c;
  int iStack_8;
  
  iStack_8 = 0xffffffff;
  puStack_c = &LAB_1000f448;
  uStack_10 = *unaff_FS_OFFSET;
  *unaff_FS_OFFSET = &uStack_10;
  FUN_1000957f();
  iVar1 = FUN_1000bb19();
  if (iVar1 == 0) {
    iVar1 = FUN_1000c4b7();
    if (iVar1 == 0) {
      iVar1 = SendCommand("M",0xb);
      if (iVar1 == 0) {
        FUN_1000c118(this,(char *)((int)this + 0x234));
      }
    }
  }
  Sleep(100);
  if ((iVar1 != 0) && (iVar1 != 0x3a9a)) {
    CString::CString(aCStack_18);
    iStack_8 = 0;
    CString::CString(aCStack_14);
    iStack_8._0_1_ = 1;
    CString::CString((CString *)&stack0xffffffbc,(CString *)((int)this + 0x10));
    FUN_1000871b();
    CString::CString((CString *)&stack0xffffffbc,(CString *)((int)this + 0x10));
    FUN_1000871b();
    CString::CString((CString *)&stack0xffffffc0,aCStack_14);
    iStack_8._0_1_ = 2;
    CString::CString((CString *)&stack0xffffffbc,aCStack_18);
    iStack_8._0_1_ = 1;
    FUN_1000a511();
    iStack_8 = (uint)iStack_8._1_3_ << 8;
    CString::~CString(aCStack_14);
    iStack_8 = 0xffffffff;
    CString::~CString(aCStack_18);
  }
  *unaff_FS_OFFSET = uStack_10;
  return iVar1;
}

Функция FUN_1000bb19 отвечает за отправку пароля с использованием определённого формата.

Формат команды включает в себя букву U, за которой следует сам пароль и затем добавляются 7 пробелов.

undefined4 FUN_1000bb19(void)
{
  CString *pCVar1;
  wchar_t *pwVar2;
  int iVar3;
  int iVar4;
  ushort *puVar5;
  int extraout_ECX;
  int unaff_EBP;
  size_t sVar6;
  
  FUN_1000e9d0();
  *(int *)(unaff_EBP + -0x14) = extraout_ECX;
  CString::CString((CString *)(unaff_EBP + -0x20));
  *(undefined4 *)(unaff_EBP + -4) = 0;
  CString::CString((CString *)(unaff_EBP + -0x1c));
  *(undefined *)(unaff_EBP + -4) = 1;
  CString::CString((CString *)(unaff_EBP + -0x10),(CString *)(extraout_ECX + 0x44c));
  *(undefined *)(unaff_EBP + -4) = 2;
  if (*(int *)(*(int *)(unaff_EBP + -0x10) + -8) == 0) {
    CString::LoadStringW((CString *)(unaff_EBP + -0x10), 50006); // GENERIC
  }
  CString::CString((CString *)(unaff_EBP + -0x24), "       ");
  *(undefined *)(unaff_EBP + -4) = 3;
  CString::CString((CString *)(unaff_EBP + -0x18), "U");
  *(undefined *)(unaff_EBP + -4) = 4;
  operator+();
  *(undefined *)(unaff_EBP + -4) = 5;
  pCVar1 = (CString *)operator+();
  *(undefined *)(unaff_EBP + -4) = 6;
  CString::operator=((CString *)(unaff_EBP + -0x10),pCVar1);
  *(undefined *)(unaff_EBP + -4) = 5;
  CString::~CString((CString *)(unaff_EBP + -0x28));
  *(undefined *)(unaff_EBP + -4) = 4;
  CString::~CString((CString *)(unaff_EBP + -0x2c));
  *(undefined *)(unaff_EBP + -4) = 3;
  CString::~CString((CString *)(unaff_EBP + -0x18));
  *(undefined *)(unaff_EBP + -4) = 2;
  CString::~CString((CString *)(unaff_EBP + -0x24));
  sVar6 = 0x14;
  pwVar2 = (wchar_t *)CString::GetBuffer((CString *)(unaff_EBP + -0x10),0x14);
  wcstombs((char *)(unaff_EBP + -0x40),pwVar2,sVar6);
  Sleep(200);
  iVar3 = SendCommand((char *)(unaff_EBP + -0x40),8);
  *(int *)(unaff_EBP + -0x18) = iVar3;
  if (iVar3 == 0) goto LAB_1000c0e2;
  CString::operator=((CString *)(unaff_EBP + -0x10),(CString *)(*(int *)(unaff_EBP + -0x14) + 0x450)
                    );
  if (*(int *)(*(int *)(unaff_EBP + -0x10) + -8) == 0) {
    CString::LoadStringW((CString *)(unaff_EBP + -0x10), 50006); // GENERIC                 
  }
  CString::CString((CString *)(unaff_EBP + -0x18), "       ");
  *(undefined *)(unaff_EBP + -4) = 7;
  CString::CString((CString *)(unaff_EBP + -0x24), "U");
  *(undefined *)(unaff_EBP + -4) = 8;
  operator+();
  *(undefined *)(unaff_EBP + -4) = 9;
  pCVar1 = (CString *)operator+();
  *(undefined *)(unaff_EBP + -4) = 10;
  CString::operator=((CString *)(unaff_EBP + -0x10),pCVar1);
  *(undefined *)(unaff_EBP + -4) = 9;
  CString::~CString((CString *)(unaff_EBP + -0x2c));
  *(undefined *)(unaff_EBP + -4) = 8;
  CString::~CString((CString *)(unaff_EBP + -0x28));
  *(undefined *)(unaff_EBP + -4) = 7;
  CString::~CString((CString *)(unaff_EBP + -0x24));
  *(undefined *)(unaff_EBP + -4) = 2;
  CString::~CString((CString *)(unaff_EBP + -0x18));
  sVar6 = 0x14;
  pwVar2 = (wchar_t *)CString::GetBuffer((CString *)(unaff_EBP + -0x10),0x14);
  wcstombs((char *)(unaff_EBP + -0x40),pwVar2,sVar6);
  Sleep(500);
  iVar3 = SendCommand((char *)(unaff_EBP + -0x40),8);
  *(int *)(unaff_EBP + -0x18) = iVar3;
  if (iVar3 == 0) {
    iVar3 = *(int *)(unaff_EBP + -0x14);
    *(undefined **)(unaff_EBP + -0x2c) = &stack0xffffffa4;
    CString::CString((CString *)&stack0xffffffa4,(CString *)(iVar3 + 0x10));
    FUN_1000871b();
    *(undefined **)(unaff_EBP + -0x2c) = &stack0xffffffa4;
    CString::CString((CString *)&stack0xffffffa4,(CString *)(iVar3 + 0x10));
    FUN_1000871b();
    *(undefined **)(unaff_EBP + -0x2c) = &stack0xffffffa8;
    CString::CString((CString *)&stack0xffffffa8,(CString *)(unaff_EBP + -0x20));
    *(undefined **)(unaff_EBP + -0x28) = &stack0xffffffa4;
    *(undefined *)(unaff_EBP + -4) = 0xb;
    CString::CString((CString *)&stack0xffffffa4,(CString *)(unaff_EBP + -0x1c));
    *(undefined *)(unaff_EBP + -4) = 2;
    iVar4 = FUN_1000a511();
    if (iVar4 != 1) goto LAB_1000c0e2;
    CString::operator=((CString *)(unaff_EBP + -0x10),(CString *)(iVar3 + 0x44c));
    if (*(int *)(*(int *)(unaff_EBP + -0x10) + -8) == 0) {
      CString::LoadStringW((CString *)(unaff_EBP + -0x10), 50006); // GENERIC
    }
    CString::CString((CString *)(unaff_EBP + -0x18), "       ");
    *(undefined *)(unaff_EBP + -4) = 0xc;
    CString::CString((CString *)(unaff_EBP + -0x24), 0x4e);
    *(undefined *)(unaff_EBP + -4) = 0xd;
    operator+();
    *(undefined *)(unaff_EBP + -4) = 0xe;
    pCVar1 = (CString *)operator+();
    *(undefined *)(unaff_EBP + -4) = 0xf;
    CString::operator=((CString *)(unaff_EBP + -0x10),pCVar1);
    *(undefined *)(unaff_EBP + -4) = 0xe;
    CString::~CString((CString *)(unaff_EBP + -0x2c));
    *(undefined *)(unaff_EBP + -4) = 0xd;
    CString::~CString((CString *)(unaff_EBP + -0x28));
    *(undefined *)(unaff_EBP + -4) = 0xc;
    CString::~CString((CString *)(unaff_EBP + -0x24));
    *(undefined *)(unaff_EBP + -4) = 2;
    CString::~CString((CString *)(unaff_EBP + -0x18));
    sVar6 = 0x14;
    pwVar2 = (wchar_t *)CString::GetBuffer((CString *)(unaff_EBP + -0x10),0x14);
    wcstombs((char *)(unaff_EBP + -0x40),pwVar2,sVar6);
    Sleep(500);
    iVar4 = SendCommand((char *)(unaff_EBP + -0x40),8);
    *(int *)(unaff_EBP + -0x18) = iVar4;
LAB_1000c057:
    if (*(int *)(unaff_EBP + -0x18) == 0) goto LAB_1000c0e2;
  }
  else {
    CString::LoadStringW((CString *)(unaff_EBP + -0x10), 50006); // GENERIC
    CString::CString((CString *)(unaff_EBP + -0x18), "       ");
    *(undefined *)(unaff_EBP + -4) = 0x10;
    CString::CString((CString *)(unaff_EBP + -0x24),s_U_10015250);
    *(undefined *)(unaff_EBP + -4) = 0x11;
    operator+();
    *(undefined *)(unaff_EBP + -4) = 0x12;
    pCVar1 = (CString *)operator+();
    *(undefined *)(unaff_EBP + -4) = 0x13;
    CString::operator=((CString *)(unaff_EBP + -0x10),pCVar1);
    *(undefined *)(unaff_EBP + -4) = 0x12;
    CString::~CString((CString *)(unaff_EBP + -0x2c));
    *(undefined *)(unaff_EBP + -4) = 0x11;
    CString::~CString((CString *)(unaff_EBP + -0x28));
    *(undefined *)(unaff_EBP + -4) = 0x10;
    CString::~CString((CString *)(unaff_EBP + -0x24));
    *(undefined *)(unaff_EBP + -4) = 2;
    CString::~CString((CString *)(unaff_EBP + -0x18));
    Sleep(5000);
    pCVar1 = (CString *)CString::Left((CString *)(unaff_EBP + -0x10));
    iVar3 = 8;
    *(undefined *)(unaff_EBP + -4) = 0x14;
    puVar5 = CString::GetBuffer(pCVar1,8);
    iVar3 = SendCommand((char *)puVar5,iVar3);
    *(int *)(unaff_EBP + -0x18) = iVar3;
    *(undefined *)(unaff_EBP + -4) = 2;
    CString::~CString((CString *)(unaff_EBP + -0x2c));
    if (*(int *)(unaff_EBP + -0x18) == 0) {
      *(undefined **)(unaff_EBP + -0x2c) = &stack0xffffff98;
      CString::CString((CString *)&stack0xffffff98,(CString *)(*(int *)(unaff_EBP + -0x14) + 0x10));
      FUN_1000871b();
      *(undefined **)(unaff_EBP + -0x2c) = &stack0xffffff98;
      CString::CString((CString *)&stack0xffffff98,(CString *)(*(int *)(unaff_EBP + -0x14) + 0x10));
      FUN_1000871b();
      CString::operator=((CString *)(unaff_EBP + -0x10),
                         (CString *)(*(int *)(unaff_EBP + -0x14) + 0x44c));
      if (*(int *)(*(int *)(unaff_EBP + -0x10) + -8) == 0) {
        CString::LoadStringW((CString *)(unaff_EBP + -0x10), 50006); // GENERIC
      }
      CString::CString((CString *)(unaff_EBP + -0x18), "       ");
      *(undefined *)(unaff_EBP + -4) = 0x15;
      CString::CString((CString *)(unaff_EBP + -0x24),0x4e);
      *(undefined *)(unaff_EBP + -4) = 0x16;
      operator+();
      *(undefined *)(unaff_EBP + -4) = 0x17;
      pCVar1 = (CString *)operator+();
      *(undefined *)(unaff_EBP + -4) = 0x18;
      CString::operator=((CString *)(unaff_EBP + -0x10),pCVar1);
      *(undefined *)(unaff_EBP + -4) = 0x17;
      CString::~CString((CString *)(unaff_EBP + -0x2c));
      *(undefined *)(unaff_EBP + -4) = 0x16;
      CString::~CString((CString *)(unaff_EBP + -0x28));
      *(undefined *)(unaff_EBP + -4) = 0x15;
      CString::~CString((CString *)(unaff_EBP + -0x24));
      *(undefined *)(unaff_EBP + -4) = 2;
      CString::~CString((CString *)(unaff_EBP + -0x18));
      Sleep(500);
      pCVar1 = (CString *)CString::Left((CString *)(unaff_EBP + -0x10));
      iVar3 = 8;
      *(undefined *)(unaff_EBP + -4) = 0x19;
      puVar5 = CString::GetBuffer(pCVar1,8);
      iVar3 = SendCommand((char *)puVar5,iVar3);
      *(int *)(unaff_EBP + -0x18) = iVar3;
      *(undefined *)(unaff_EBP + -4) = 2;
      CString::~CString((CString *)(unaff_EBP + -0x2c));
      iVar3 = *(int *)(unaff_EBP + -0x14);
      goto LAB_1000c057;
    }
    iVar3 = *(int *)(unaff_EBP + -0x14);
  }
  if (*(int *)(unaff_EBP + -0x18) != 0x3a9a) {
    *(undefined **)(unaff_EBP + -0x28) = &stack0xffffff8c;
    CString::CString((CString *)&stack0xffffff8c,(CString *)(iVar3 + 0x10));
    FUN_1000871b();
    *(undefined **)(unaff_EBP + -0x28) = &stack0xffffff8c;
    CString::CString((CString *)&stack0xffffff8c,(CString *)(iVar3 + 0x10));
    FUN_1000871b();
    *(undefined **)(unaff_EBP + -0x28) = &stack0xffffff90;
    CString::CString((CString *)&stack0xffffff90,(CString *)(unaff_EBP + -0x20));
    *(undefined **)(unaff_EBP + -0x24) = &stack0xffffff8c;
    *(undefined *)(unaff_EBP + -4) = 0x1a;
    CString::CString((CString *)&stack0xffffff8c,(CString *)(unaff_EBP + -0x1c));
    *(undefined *)(unaff_EBP + -4) = 2;
    FUN_1000a511();
    *(undefined4 *)(unaff_EBP + -0x18) = 0x3b85;
  }
LAB_1000c0e2:
  *(undefined *)(unaff_EBP + -4) = 1;
  CString::~CString((CString *)(unaff_EBP + -0x10));
  *(undefined *)(unaff_EBP + -4) = 0;
  CString::~CString((CString *)(unaff_EBP + -0x1c));
  *(undefined4 *)(unaff_EBP + -4) = 0xffffffff;
  CString::~CString((CString *)(unaff_EBP + -0x20));
  ExceptionList = *(void **)(unaff_EBP + -0xc);
  return *(undefined4 *)(unaff_EBP + -0x18);
}

Чтобы начать отправлять команды для чтения данных, достаточно отправить только пароль.

Команды, которые идут перед паролем, такие как команда V, можно не отправлять, и устройство воспринимает это нормально.

Список команд с префиксом D, которые используются для чтения данных с устройства:

  • D816022 - Уникальный идентификатор пациента

  • D81820F - То же самое

  • D819241 - Причины для исследования

  • D81D306 - Дата и время инициализации

  • D810B01 - Количество измерений

  • D820078 - Систолические значения

  • D830078 - Диастолические значения

  • D840078 - Значения среднего артериального давления (MAP)

  • D850078 - Показатели частоты сердечных сокращений

  • D860078 - Время считывания в часах

  • D870078 - Время считывания в минутах

  • DCA0078 - Информация о дне месяца считывания

  • DCB0078 - Информация о месяце считывания

Инициализация устройства

Аналогично процессу выгрузки данных с монитора, я нахожу функцию StartInitializingMonitor, которая используется для инициализации устройства.

undefined4 StartInitializingMonitor(void)
{
  AFX_MODULE_STATE *pAVar1;
  LPWSTR lpReturnedString;
  ushort *puVar2;
  int iVar3;
  undefined4 uVar4;
  int unaff_EBX;
  LPWSTR unaff_ESI;
  UINT unaff_EDI;
  int *in_stack_00000004;
  undefined4 *in_stack_00000008;
  undefined4 uStack_2c;
  int iStack_28;
  undefined *puStack_24;
  LPCWSTR pWStack_20;
  CString aCStack_1c [4];
  LPCWSTR pWStack_18;
  undefined *puStack_14;
  void *pvStack_10;
  undefined *puStack_c;
  undefined4 uStack_8;
  
  puStack_c = &LAB_1000f198;
  pvStack_10 = ExceptionList;
  uStack_8 = 0;
  puStack_14 = &stack0xffffffc8;
  ExceptionList = &pvStack_10;
  pAVar1 = (AFX_MODULE_STATE *)FUN_1000e7f9();
  AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2((AFX_MAINTAIN_STATE2 *)&uStack_2c,pAVar1);
  uStack_8._0_1_ = 1;
  if (in_stack_00000004[0x180] != 0) {
    FUN_10006204(in_stack_00000004);
    uVar4 = InitMonitor();
    *in_stack_00000008 = uVar4;
    goto LAB_100055a3;
  }
  CString::CString((CString *)&pWStack_20);
  uStack_8._0_1_ = 2;
  CString::CString((CString *)&pWStack_18);
  uStack_8._0_1_ = 3;
  CString::CString((CString *)&stack0x00000004,(char *)&this_100157d4);
  uStack_8._0_1_ = 4;
  lpReturnedString = (LPWSTR)CString::GetBuffer((CString *)&stack0x00000004,0x104);
  GetPrivateProfileStringW
            (L"DevTest",L"Language",(LPCWSTR)&param_3_100157d0,lpReturnedString,0x104,L"abpwin.ini")
  ;
  CString::ReleaseBuffer((CString *)&stack0x00000004,-1);
  CString::CString(aCStack_1c,(char *)&this_100157d4);
  uStack_8 = CONCAT31(uStack_8._1_3_,5);
  puVar2 = CString::GetBuffer((CString *)&stack0x00000004,0x104);
  iVar3 = _strcmpi(&_Str1_100143d0,(char *)puVar2);
  if (iVar3 == 0) {
LAB_10005520:
    CString::operator=(aCStack_1c,"ABD Report Management System");
    CString::operator=((CString *)&pWStack_18,aCStack_1c);
  }
  else {
    puVar2 = CString::GetBuffer((CString *)&stack0x00000004,0x104);
    iVar3 = _strcmpi(&_Str1_100143cc,(char *)puVar2);
    if (iVar3 == 0) goto LAB_10005520;
    CString::LoadStringW((HINSTANCE)&DAT_00000065,unaff_EDI,unaff_ESI,unaff_EBX);
  }
  puStack_24 = &stack0xffffffbc;
  CString::CString((CString *)&stack0xffffffbc,(CString *)(in_stack_00000004 + 0x2c));
  FUN_1000871b();
  MessageBoxW((HWND)in_stack_00000004[0x14],pWStack_20,pWStack_18,0x40);
  uStack_8._0_1_ = 4;
  *in_stack_00000008 = 0;
  CString::~CString(aCStack_1c);
  uStack_8._0_1_ = 3;
  CString::~CString((CString *)&stack0x00000004);
  uStack_8._0_1_ = 2;
  CString::~CString((CString *)&pWStack_18);
  uStack_8 = CONCAT31(uStack_8._1_3_,1);
  CString::~CString((CString *)&pWStack_20);
LAB_100055a3:
  *(undefined4 *)(iStack_28 + 4) = uStack_2c;
  ExceptionList = pvStack_10;
  return 0;
}

Функция InitMonitor. Оправляет команды инициализации

undefined4 InitMonitor(void)
{
  undefined8 uVar1;
  int iVar2;
  undefined4 uVar3;
  int extraout_ECX;
  CString *this;
  int unaff_EBP;
  uint uVar4;
  char *pcVar5;
  
  FUN_1000e9d0();
  if (*(int *)(extraout_ECX + 0x518) == 1) {
    (**(code **)(*(int *)(extraout_ECX + 0x504) + 0x34))();
  }
  FUN_1000957f();
  *(undefined **)(unaff_EBP + -0x14) = &stack0xffffffdc;
  CString::CString((CString *)&stack0xffffffdc,"Checking monitor availability .....");
  FUN_1000e0e9();
  *(undefined4 *)(extraout_ECX + 0x500) = 0;
  *(undefined4 *)(extraout_ECX + 0x4f8) = 0;
  CString::GetBuffer((CString *)(extraout_ECX + 0xc),10);
  iVar2 = OpenComPort();
  if (iVar2 == 0) {
    *(undefined **)(unaff_EBP + -0x14) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,"Failed to open COM port");
    FUN_1000e0e9();
    FUN_1000957f();
    goto LAB_10009e80;
  }
  *(undefined **)(unaff_EBP + -0x14) = &stack0xffffffdc;
  CString::CString((CString *)&stack0xffffffdc,"COM port opened");
  FUN_1000e0e9();
  *(undefined4 *)(extraout_ECX + 0x4f8) = 1;
  if (*(int *)(extraout_ECX + 0x500) == 1) goto LAB_100096cd;
  if (*(int *)(extraout_ECX + 0x4cc) == 2) {
    FUN_1000957f();
    iVar2 = FUN_1000d79e();
    if (iVar2 == 0) {
      *(undefined **)(unaff_EBP + -0x14) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,"Initialize modem failed");
      FUN_1000e0e9();
      CString::CString((CString *)(unaff_EBP + -0x14));
      *(undefined4 *)(unaff_EBP + -4) = 0;
      CString::CString((CString *)(unaff_EBP + -0x10));
      *(undefined *)(unaff_EBP + -4) = 1;
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      MessageBoxW(*(HWND *)(extraout_ECX + 0x14),*(LPCWSTR *)(unaff_EBP + -0x14),
                  *(LPCWSTR *)(unaff_EBP + -0x10),0);
      *(undefined *)(unaff_EBP + -4) = 0;
      uVar4 = 0x3a9a;
      CString::~CString((CString *)(unaff_EBP + -0x10));
      *(undefined4 *)(unaff_EBP + -4) = 0xffffffff;
      this = (CString *)(unaff_EBP + -0x14);
LAB_10009a68:
      CString::~CString(this);
LAB_10009d88:
      if (*(int *)(extraout_ECX + 0x4cc) == 2) {
        iVar2 = *(int *)(extraout_ECX + 0x4f8);
        if (((iVar2 == 0) || (iVar2 == 2)) || (iVar2 == 1)) {
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"No modem connection has been made..");
          FUN_1000e0e9();
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Have to close the port");
          FUN_1000e0e9();
        }
        else {
          FUN_1000957f();
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Breaking the modems\' connection....");
          FUN_1000e0e9();
          *(undefined4 *)(extraout_ECX + 0x4f8) = 4;
          if (*(int *)(extraout_ECX + 0x500) == 1) goto LAB_100096cd;
          FUN_1000dca5();
        }
      }
      *(undefined4 *)(extraout_ECX + 0x4f8) = 9;
      if (*(int *)(extraout_ECX + 0x500) != 1) {
        FUN_1000a781(extraout_ECX);
        *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
        CString::CString((CString *)&stack0xffffffdc,"Closing COM port");
        FUN_1000e0e9();
        if (uVar4 == 0) {
          CString::operator=((CString *)(extraout_ECX + 0x18),(CString *)(extraout_ECX + 0xc));
          FUN_1000957f();
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Monitor successfully initialized");
          FUN_1000e0e9();
          uVar3 = 1;
          goto LAB_10009ebb;
        }
        FUN_1000957f();
        *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
        CString::CString((CString *)&stack0xffffffdc,"Initialization failed");
        FUN_1000e0e9();
        goto LAB_10009e80;
      }
    }
    else {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,"Initialize modem succeeded");
      FUN_1000e0e9();
      *(undefined4 *)(extraout_ECX + 0x4f8) = 2;
      if (*(int *)(extraout_ECX + 0x500) != 1) {
        FUN_1000957f();
        iVar2 = FUN_1000dbb4();
        if (iVar2 == 0) {
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Connecting the modems failed");
          FUN_1000e0e9();
          CString::CString((CString *)(unaff_EBP + -0x10));
          *(undefined4 *)(unaff_EBP + -4) = 2;
          CString::CString((CString *)(unaff_EBP + -0x14));
          *(undefined *)(unaff_EBP + -4) = 3;
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
          CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
          FUN_1000871b();
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
          CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
          FUN_1000871b();
          MessageBoxW(*(HWND *)(extraout_ECX + 0x14),*(LPCWSTR *)(unaff_EBP + -0x10),
                      *(LPCWSTR *)(unaff_EBP + -0x14),0);
          uVar4 = 0x3a9a;
          *(undefined *)(unaff_EBP + -4) = 2;
          CString::~CString((CString *)(unaff_EBP + -0x14));
          *(undefined4 *)(unaff_EBP + -4) = 0xffffffff;
          this = (CString *)(unaff_EBP + -0x10);
          goto LAB_10009a68;
        }
        *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
        CString::CString((CString *)&stack0xffffffdc,"Connecting the modems succeeded");
        FUN_1000e0e9();
        *(undefined4 *)(extraout_ECX + 0x4f8) = 3;
        if (*(int *)(extraout_ECX + 0x500) != 1) goto LAB_100098a7;
      }
    }
LAB_100096cd:
    FUN_1000dd49();
  }
  else {
LAB_100098a7:
    uVar4 = DetectMonitorVersion();
    if (uVar4 == 0xffffffff) goto LAB_10009e80;
    if (((uVar4 != 0) && (uVar4 != 0x3b86)) && (uVar4 != 0x3b89)) {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,(ushort *)L"Connecting with monitor failed");
      FUN_1000e0e9();
      CString::CString((CString *)(unaff_EBP + -0x10));
      *(undefined4 *)(unaff_EBP + -4) = 4;
      CString::CString((CString *)(unaff_EBP + -0x14));
      *(undefined *)(unaff_EBP + -4) = 5;
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      MessageBoxW(*(HWND *)(extraout_ECX + 0x14),*(LPCWSTR *)(unaff_EBP + -0x10),
                  *(LPCWSTR *)(unaff_EBP + -0x14),0);
      *(undefined *)(unaff_EBP + -4) = 4;
      CString::~CString((CString *)(unaff_EBP + -0x14));
      *(undefined4 *)(unaff_EBP + -4) = 0xffffffff;
      this = (CString *)(unaff_EBP + -0x10);
      goto LAB_10009a68;
    }
    *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,(ushort *)L"Connecting with monitor succeeded");
    FUN_1000e0e9();
    *(undefined4 *)(extraout_ECX + 0x4f8) = 5;
    if (*(int *)(extraout_ECX + 0x500) == 1) goto LAB_100096cd;
    uVar4 = FUN_1000a0a6();
    if (uVar4 == 0xffffffff) goto LAB_10009e80;
    if (uVar4 == 0x3a9a) {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,(ushort *)L"Failed to Establish Link to Monitor")
      ;
LAB_10009d81:
      FUN_1000e0e9();
      goto LAB_10009d88;
    }
    if (uVar4 != 0) {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,(ushort *)L"Failed to Establish Link to Monitor")
      ;
      FUN_1000e0e9();
      CString::CString((CString *)(unaff_EBP + -0x1c));
      *(undefined4 *)(unaff_EBP + -4) = 6;
      CString::CString((CString *)(unaff_EBP + -0x18));
      *(undefined *)(unaff_EBP + -4) = 7;
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
      CString::CString((CString *)&stack0xffffffd4,(CString *)(extraout_ECX + 0x10));
      FUN_1000871b();
      MessageBoxW(*(HWND *)(extraout_ECX + 0x14),*(LPCWSTR *)(unaff_EBP + -0x1c),
                  *(LPCWSTR *)(unaff_EBP + -0x18),0);
      *(undefined *)(unaff_EBP + -4) = 6;
      CString::~CString((CString *)(unaff_EBP + -0x18));
      *(undefined4 *)(unaff_EBP + -4) = 0xffffffff;
      this = (CString *)(unaff_EBP + -0x1c);
      goto LAB_10009a68;
    }
    *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,"Established Link to the Monitor");
    FUN_1000e0e9();
    *(undefined4 *)(extraout_ECX + 0x4f8) = 6;
    if (*(int *)(extraout_ECX + 0x500) == 1) goto LAB_100096cd;
    FUN_1000957f();
    iVar2 = SendCommand("R",8); // Сброс
    if ((iVar2 != 0) && (uVar4 = SendCommand("R",8), uVar4 != 0)) {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      pcVar5 = "Failed to reset the monitor";
      goto LAB_10009d7c;
    }
    *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,"Successfully reset the monitor");
    FUN_1000e0e9();
    *(undefined4 *)(extraout_ECX + 0x4f8) = 7;
    if (*(int *)(extraout_ECX + 0x500) == 1) goto LAB_100096cd;
    FUN_1000957f();
    uVar4 = FUN_1000b50a(extraout_ECX);
    if (uVar4 != 0) {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      pcVar5 = "Failed to send control flags to the monitor";
      goto LAB_10009d7c;
    }
    *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,
                     "Successfully sent the control flags to the monitor");
    FUN_1000e0e9();
    *(undefined4 *)(extraout_ECX + 0x4f8) = 8;
    if ((*(int *)(extraout_ECX + 0x500) == 1) && (iVar2 = FUN_1000dd49(), iVar2 != 0))
    goto LAB_10009e80;
    FUN_1000957f();
    if (*(int *)(extraout_ECX + 0x46c) == 0) {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      pcVar5 = "Failed to build monitor BP cycle";
LAB_10009d7c:
      CString::CString((CString *)&stack0xffffffdc,pcVar5);
      goto LAB_10009d81;
    }
    *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,"Succeeded in building monitor BP cycle");
    FUN_1000e0e9();
    FUN_1000957f();
    uVar4 = FUN_1000b578();
    if (uVar4 != 0) {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      pcVar5 = "Failed to build cycle time information and send to monitor";
      goto LAB_10009d7c;
    }
    *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
    CString::CString((CString *)&stack0xffffffdc,
                     "Failed to build cycle time information and send to monitor");
    FUN_1000e0e9();
    if ((*(int *)(extraout_ECX + 0x500) != 1) || (iVar2 = FUN_1000dd49(), iVar2 == 0)) {
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
      CString::CString((CString *)&stack0xffffffdc,
                       "Succeeded in building cycle time information and sending to monitor");
      FUN_1000e0e9();
      FUN_1000957f();
      uVar1 = *(undefined8 *)(extraout_ECX + 0x48c);
      *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
      uVar4 = FUN_1000b758(SUB81(uVar1,0));
      if (uVar4 == 0) {
        *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
        CString::CString((CString *)&stack0xffffffdc,
                         "Succeeded in sending initialization time to monitor");
        FUN_1000e0e9();
        if ((*(int *)(extraout_ECX + 0x500) == 1) && (iVar2 = FUN_1000dd49(), iVar2 != 0))
        goto LAB_10009e80;
        uVar1 = *(undefined8 *)(extraout_ECX + 0x48c);
        *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffd4;
        uVar4 = FUN_1000b7c8(SUB81(uVar1,0));
        if (uVar4 == 0) {
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          CString::CString((CString *)&stack0xffffffdc,"Succeeded in sending monitor clock");
          FUN_1000e0e9();
          if ((*(int *)(extraout_ECX + 0x500) == 1) && (iVar2 = FUN_1000dd49(), iVar2 != 0))
          goto LAB_10009e80;
          FUN_1000957f();
          uVar4 = FUN_1000b827();
          if (uVar4 == 0) {
            *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
            CString::CString((CString *)&stack0xffffffdc,"Succeeded in sending biographical data");
            FUN_1000e0e9();
            if ((*(int *)(extraout_ECX + 0x500) == 1) && (iVar2 = FUN_1000dd49(), iVar2 != 0))
            goto LAB_10009e80;
            uVar4 = FUN_1000bb0c();
            *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
            pcVar5 = "set processed readings flag to zero";
          }
          else {
            *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
            pcVar5 = "Failed to send biographical data";
          }
        }
        else {
          *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
          pcVar5 = "Failed to send monitor clock";
        }
      }
      else {
        *(undefined **)(unaff_EBP + -0x20) = &stack0xffffffdc;
        pcVar5 = "Failed to send initialization time to monitor";
      }
      goto LAB_10009d7c;
    }
  }
LAB_10009e80:
  uVar3 = 0;
LAB_10009ebb:
  ExceptionList = *(void **)(unaff_EBP + -0xc);
  return uVar3;
}

Функция FUN_1000b50a. Оправляет флаги управления монитором.

undefined4 __fastcall FUN_1000b50a(int param_1)
{
  char *_Dest;
  undefined4 uVar1;
  byte bVar2;
  
  bVar2 = *(int *)(param_1 + 1148) == 0; // Показать результаты измерения
  if (*(int *)(param_1 + 1160) == 0) { // Формат отображения времени (false: 12-часовой формат, true: 24-часовой формат)
    bVar2 = bVar2 | 8; 
  }
  if (*(int *)(param_1 + 1156) != 0) { // Отображение давления в манжете
    bVar2 = bVar2 | 16; 
  }
  if (*(int *)(param_1 + 1152) != 0) { // Установка клинической верификации
    bVar2 = bVar2 | 64; 
  }
  _Dest = (char *)operator_new(0x10);
  sprintf(_Dest,"W811702%02X%02X",4,(uint)bVar2);
  uVar1 = SendCommand(_Dest,8);
  operator_delete(_Dest);
  return uVar1;
}

Функция FUN_1000b578 устанавливает расписание измерений по часам. Ее особенностью является использование связанного списка, который, как правило, не применяется в практических задачах.

int __thiscall FUN_1000b578(void *this)
{
  byte timerValue;
  byte *_timers;
  wchar_t **currentSoftwareVersion;
  wchar_t **incomingVersion;
  int isNextVersionGreaterOrEqual;
  uint ActiveStateHour;
  int iVar1;
  uint hourIndex;
  CycleTimeInformation *currentCycleInfo;
  char *_Dest;
  undefined4 *unaff_FS_OFFSET;
  double ActiveStateTimestamp;
  int local_40;
  double NextStateTimestamp;
  int local_34;
  undefined2 HourlyReadInterval;
  dword Tone;
  CString local_28 [4];
  char *Destination;
  void *local_20;
  int isNewVersion;
  uint NextStateHour;
  byte *Timers;
  undefined4 local_10;
  undefined *puStack_c;
  undefined4 local_8;
  char *Command;
  CycleTimeInformation *nextCycleInfo;
  
  local_8 = 0xffffffff;
  puStack_c = &LAB_1000f944;
  local_10 = *unaff_FS_OFFSET;
  *unaff_FS_OFFSET = &local_10;
  local_20 = this;
  _timers = (byte *)operator_new(24);
  Timers = _timers;
  Destination = (char *)operator_new(128);
  for (iVar1 = 6; iVar1 != 0; iVar1 = iVar1 + -1) {
    _timers[0] = 0x80;
    _timers[1] = 0x80;
    _timers[2] = 0x80;
    _timers[3] = 0x80;
    _timers = _timers + 4;
  }
  if ((*(int *)(*(int *)((int)this + 0x448) + -8) != 0) ||
     (iVar1 = FUN_1000c2be((int)this), iVar1 == 0)) {
    isNewVersion = 0;
    currentSoftwareVersion = (wchar_t **)CString::CString(local_28,"02.11");
    local_8 = 0;
    incomingVersion = (wchar_t **)CString::Mid((CString *)((int)this + 0x448),&NextStateHour,10);
    isNextVersionGreaterOrEqual = wcscmp(*incomingVersion,*currentSoftwareVersion);
    CString::~CString((CString *)&NextStateHour);
    local_8 = 0xffffffff;
    CString::~CString(local_28);
    if (-1 < isNextVersionGreaterOrEqual) {
      isNewVersion = 1;
    }
    currentCycleInfo = *(CycleTimeInformation **)((int)local_20 + 0x464);
    while (Command = Destination, currentCycleInfo != (CycleTimeInformation *)0x0) {
      nextCycleInfo = currentCycleInfo->Next;
      ActiveStateTimestamp = currentCycleInfo->ActiveStateHour;
      local_40 = currentCycleInfo->v0;
      NextStateTimestamp = currentCycleInfo->NextStateHour;
      local_34 = currentCycleInfo->v1;
      HourlyReadInterval = *(undefined2 *)&currentCycleInfo->HourlyReadInterval;
      Tone = currentCycleInfo->Tone;
      ActiveStateHour = COleDateTime::GetHour((COleDateTime *)&ActiveStateTimestamp);
      NextStateHour = COleDateTime::GetHour((COleDateTime *)&NextStateTimestamp);
      timerValue = 0;
      if (((byte)HourlyReadInterval == 0) || (59 < (byte)HourlyReadInterval)) {
        if ((byte)HourlyReadInterval == 100) {
          timerValue = 120;
        }
      }
      else {
        timerValue = (byte)(60 / (byte)HourlyReadInterval);
      }
      hourIndex = 0;
      if (isNewVersion == 0) {
        if ((timerValue < 6) || (0x3c < timerValue)) {
          iVar1 = 0x3b9d;
          goto LAB_1000b735;
        }
      }
      else if ((timerValue != 0) && ((timerValue < 6 || (120 < timerValue)))) {
        iVar1 = 0x3b9e;
        goto LAB_1000b735;
      }
      do {
        if (ActiveStateHour < NextStateHour) {
          if (ActiveStateHour <= hourIndex) {
LAB_1000b6c1:
            if (hourIndex < NextStateHour) goto LAB_1000b6c6;
          }
        }
        else {
          if (hourIndex < ActiveStateHour) goto LAB_1000b6c1;
LAB_1000b6c6:
          Timers[hourIndex] = timerValue;
          if (Tone == 0) {
            Timers[hourIndex] = timerValue + 128;
          }
        }
        hourIndex = hourIndex + 1;
        currentCycleInfo = nextCycleInfo;
      } while (hourIndex < 24);
    }
    strcpy(Destination,"W814018");
    iVar1 = 0;
    _Dest = Command + 7;
    do {
      sprintf(_Dest,"%02X",(uint)Timers[iVar1]);
      iVar1 = iVar1 + 1;
      _Dest = _Dest + 2;
    } while (iVar1 < 24);
    iVar1 = SendCommand(Command,8);
LAB_1000b735:
    operator_delete(Timers);
    operator_delete(Destination);
  }
  *unaff_FS_OFFSET = local_10;
  return iVar1;
}

Функция FUN_1000b758. Оправляет время инициализации.

undefined4 FUN_1000b758(undefined param_1)
{
  char *_Dest;
  int Minute;
  int Hour;
  int Year;
  int Day;
  int Month;
  undefined4 uVar1;
  
  _Dest = (char *)operator_new(0x1e);
  Minute = COleDateTime::GetMinute((COleDateTime *)&param_1);
  Hour = COleDateTime::GetHour((COleDateTime *)&param_1);
  Year = COleDateTime::GetYear((COleDateTime *)&param_1);
  Year = Year % 100;
  Day = COleDateTime::GetDay((COleDateTime *)&param_1);
  Month = COleDateTime::GetMonth((COleDateTime *)&param_1);
  sprintf(_Dest,"W81D306%02X%02X%02X%02X%02X%02X",Month,Day,Year,Hour,Minute);
  uVar1 = SendCommand(_Dest,8);
  operator_delete(_Dest);
  return uVar1;
}

Функция FUN_1000b7c8. Устанавливает время устройства

undefined4 FUN_1000b7c8(undefined param_1)
{
  char *_Dest;
  int Month;
  int Day;
  int Minute;
  int Hour;
  undefined4 uVar1;
  
  _Dest = (char *)operator_new(0x1e);
  Month = COleDateTime::GetMonth((COleDateTime *)&param_1);
  Day = COleDateTime::GetDay((COleDateTime *)&param_1);
  Minute = COleDateTime::GetMinute((COleDateTime *)&param_1);
  Hour = COleDateTime::GetHour((COleDateTime *)&param_1);
  sprintf(_Dest,"T%2.2d%2.2d%2.2d%2.2d",Hour,Minute,Day,Month);
  uVar1 = SendCommand(_Dest,8);
  operator_delete(_Dest);
  return uVar1;
}

Перед началом инициализации также отправляется пароль GENERIC с пробелами в конце.

Команды инициализации:

  • R - Выполняет сброс устройства.

  • W8117020401 - Устанавливает флаги управления монитором. Префикс W811702, далее следует 04 и флаги, закодированные в одном байте.

  • W814018BCBCBCBCBCBC14141414141414141414141414141414BCBC - Устанавливает расписание времени измерения. Префикс W814018, далее идет расписание, закодированное в HEX-строке особым образом.

  • W81D3060C04180F2400 - Устанавливает время инициализации. Префикс W81D306, далее в HEX формате указываются месяц, день, год (двумя цифрами), час и минута.

  •  T15360412 - Устанавливает время устройства. Префикс T, далее идут час, минута, день и месяц.

  • W81600100, W818202200, W81920100 - Отправляют данные о пациенте.

  • W81DA0100 - Сбрасывает число отработанных показаний.

Таким образом я собрал достаточно данных для написания консольного приложения. Исходный код которого можно посмотреть на гитхабе.

Заключение

В результате проведенного реверс-инжиниринга программы мониторинга артериального давления Spacelabs OnTrak 90227 ABP Monitor мне удалось достичь своей цели — разработать консольное приложение для инициализации устройства и считывания данных. Весь процесс занял две недели.

В ходе работы я столкнулся с различными задачами, включая необходимость в эмуляции устройства, однако техническая смекалка и креативный подход помогли мне преодолеть эти трудности. Используя Arduino для создания эмулятора, я смог обойти ограничения, связанные с доступом к реальному устройству, и обеспечить стабильную среду для тестирования и отладки.

Интуитивно исключив команды и алгоритмы, которые не были критически необходимы для работы с устройством, я упростил себе задачу и ускорил процесс разработки консольного приложения. Оно успешно заработало с первого раза без ошибок на реальном устройстве, что подтвердило правильность моих предположений и подходов в этом проекте.

Я уверен, что полученный опыт будет полезен в будущих проектах и поможет мне справляться с более сложными задачами.

Комментарии (0)