Реверс-инжиниринг программы мониторинга артериального давления: от анализа к разработке

от автора

Начало

Передо мной встала задача провести реверс-инжиниринг программы мониторинга артериального давления для устройства 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 для создания эмулятора, я смог обойти ограничения, связанные с доступом к реальному устройству, и обеспечить стабильную среду для тестирования и отладки.

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

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


ссылка на оригинал статьи https://habr.com/ru/articles/868092/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *