Reverse Engineering des chinesischen USB-IR-Transceiver-Protokolls


Ich bin auf einen chinesischen MicroUSB-IR-Transceiver gestoßen, und es bestand der Wunsch, ihn mit Windows an einen Computer anzuschließen. Der Transceiver ist ein sehr kompaktes GerĂ€t mit einem Micro-USB-Anschluss. Die einzige "offizielle" Option, um damit zu arbeiten, ist eine Android-Anwendung namens ZaZaRemote.

Bei Verbindung mit einem Computer ĂŒber einen Adapter wurde das GerĂ€t als HID-kompatibles USB-GerĂ€t \ VID_10C4 & PID_8468 definiert. Das Googeln mit dieser ID ergab keine Ergebnisse, und ich musste eine Protokollumkehr durchfĂŒhren.

UngĂŒltiges HID


Die GerĂ€teklasse wurde als USB \ Class_03 & SubClass_FF & Prot_FF definiert. Class_03 - HID-GerĂ€t, SubClass_FF - herstellerspezifisch. Der Treiber hidusb.sys wurde vom System automatisch installiert. Sie können mit diesem Treiber ĂŒber die HID-API arbeiten .

Nachdem ich ein einfaches kleines Programm entworfen hatte, konnte ich verschiedene Informationen ĂŒber das GerĂ€t erhalten:

HidD_GetManufacturerString: "Tiqiaa"
HidD_GetProductString: "Tview"

HidP_GetCaps:
  Usage 1
  UsagePage ff00
  InputReportByteLength 61
  OutputReportByteLength 61
  FeatureReportByteLength 0
  NumberLinkCollectionNodes 1
  NumberInputButtonCaps 0
  NumberInputValueCaps 2
  NumberInputDataIndices 2
  NumberOutputButtonCaps 0
  NumberOutputValueCaps 2
  NumberOutputDataIndices 2
  NumberFeatureButtonCaps 0
  NumberFeatureValueCaps 0
  NumberFeatureDataIndices 0

Es stellt sich heraus, dass der Austausch in Blöcken von maximal 61 Bytes durchgefĂŒhrt wird, es gibt 2 InputValue- und 2 OutputValue-Schnittstellen. Die Funktion HidP_GetValueCaps gibt detailliertere Informationen dazu zurĂŒck:

HidP_GetValueCaps(HidP_Input):
    UsagePage ff00
    ReportID fe
    LinkUsage 1
    LinkUsagePage ff00
    BitSize 8
    ReportCount 8

    UsagePage ff00
    ReportID 1
    LinkUsage 1
    LinkUsagePage ff00
    BitSize 8
    ReportCount 60

HidP_GetValueCaps(HidP_Output):
    UsagePage ff00
    ReportID fd
    LinkUsage 1
    LinkUsagePage ff00
    BitSize 8
    ReportCount 8

    UsagePage ff00
    ReportID 2
    LinkUsage 1
    LinkUsagePage ff00
    BitSize 8
    ReportCount 60

Von diesen Daten sind ReportID - die ID des Berichts (tatsĂ€chlich das Datenpaket) und ReportCount - seine GrĂ¶ĂŸe am interessantesten. Daten können mit den Funktionen HidD_SetOutputReport bzw. HidD_GetInputReport gesendet und empfangen werden. Nachdem ich mit diesen Funktionen mit unterschiedlichen ReportIDs und DatengrĂ¶ĂŸen experimentiert hatte, konnte ich keinen erfolgreichen Austausch erzielen. Nach dem Datenverkehr ĂŒber USB mit USBPcap stellte ich fest, dass die Daten nicht einmal versucht wurden, ĂŒbertragen zu werden. Es bestand der Verdacht, dass dies eine Art falsches HID ist.


SET_REPORT Anfrage blieb unbeantwortet

Umkehren der ZaZaRemote-Anwendung


In der APK-Datei dieser Anwendung habe ich die Bibliothek libtiqiaa_dev_usb.so gefunden. Es exportiert folgende Funktionen:

Java_com_icontrol_dev_TiqiaaUsbController_usbOpen
Java_com_icontrol_dev_TiqiaaUsbController_usbClose
Java_com_icontrol_dev_TiqiaaUsbController_usbSendCmd
Java_com_icontrol_dev_TiqiaaUsbController_usbSendIR
Java_com_icontrol_dev_TiqiaaUsbController_d

Nach dem Namen zu urteilen, erkennen sie den Austausch mit dem GerÀt. Fragmente, die dem Aufrufen virtueller Funktionen Àhneln, sind hÀufig in ihrem Code enthalten:

LDR     R3, [R0]
LDR     R3, [R3,#0x18]
BLX     R3

Register R0 ist das erste Argument fĂŒr diese Funktion. Diese Funktionen werden nur aus dem Java classes.dex-Code aufgerufen. Durch Dekompilieren dieser Datei erhalten wir ihren Typ:

private native boolean usbOpen(Context context);
private native void usbClose();
private native boolean usbSendCmd(UsbDeviceConnection usbdeviceconnection, UsbEndpoint usbendpoint, int j, int k);
private native boolean usbSendIR(Context context, UsbDeviceConnection usbdeviceconnection, UsbEndpoint usbendpoint, int j, byte abyte0[], int k);
private native IControlIRData d(byte abyte0[]);

Java-Code wurde verschleiert, aber einige Namen sind noch erhalten. In Bezug auf diese Bibliothek hat der Obfuscator nur den Namen der letzten Funktion verdorben, jedoch waren fast alle Namen im Haupt-Java-Code beschÀdigt.

Nachdem ich Java dekompilierten Code ein wenig studiert hatte, fand ich die folgenden Zeilen:

com.tiqiaa.icontrol.e.i.a("DeviceHolder", (new StringBuilder("send......cmdType=")).append(i1).append(",cmdId=").append(j1).toString());
boolean flag1 = a.b(i1, j1);

com.tiqiaa.icontrol.e.i.d("DeviceHolder", (new StringBuilder("send....................freq=")).append(i1).append(",cmdId=").append(j1).append(",buffer.length=").append(abyte0.length).append(" , device = ").append(a).toString());
boolean flag1 = a.a(i1, abyte0, j1);

Debug-Protokolle sind ein guter Freund eines Reverse Engineers.

Um native Methoden aus Java-Code aufzurufen, wird der Java Native Interface- Mechanismus verwendet . Die exportierte Funktion sollte folgendermaßen aussehen:

extern "C" JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, <java arguments>)

Jetzt können Sie die Art der Funktionen in der IDA einstellen:

bool __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbOpen(JNIEnv *env, jobject obj, struct Context *context);
void __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbClose(JNIEnv *env, jobject obj);
bool __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbSendCmd(JNIEnv *env, jobject obj, struct UsbDeviceConnection *usbdeviceconnection, struct UsbEndpoint *usbendpoint, int cmdType, int cmdId);
bool __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbSendIR(JNIEnv *env, jobject obj, struct Context *context, struct UsbDeviceConnection *usbdeviceconnection, struct UsbEndpoint *usbendpoint, int freq, jbyte buffer, int cmdId);
struct IControlIRData *__cdecl Java_com_icontrol_dev_TiqiaaUsbController_d(JNIEnv *env, jobject obj, jbyteArray buffer);

Jetzt erkennt der HexRays-Dekompiler JNI-Aufrufe und der Code wird viel verstÀndlicher. Beispielsweise wird der obige Aufruf wie folgt dekompiliert:

v5 = ((int (*)(void))(*env)->FindClass)();

Ergebnis der Dekompilierung der UsbSendCmd-Funktion
bool __cdecl Java_com_icontrol_dev_TiqiaaUsbController_usbSendCmd(JNIEnv *env, jobject obj, struct UsbDeviceConnection *usbdeviceconnection, struct UsbEndpoint *usbendpoint, int cmdType, int cmdId)
{
  char v6; // r5@5
  bool result; // r0@12
  char data[24]; // [sp+8h] [bp-18h]@12

  dword_B57C = 0;
  if ( UsbEndpoint_bulkTransfer )
  {
    if ( usbdeviceconnection )
    {
      if ( usbendpoint )
      {
        switch ( cmdType )
        {
          case 0:
            v6 = 'L';
            goto LABEL_12;
          case 2:
            v6 = 'R';
            goto LABEL_12;
          case 3:
            v6 = 'H';
            goto LABEL_12;
          case 4:
            v6 = 'O';
            goto LABEL_12;
          case 6:
            v6 = 'C';
            goto LABEL_12;
          case 7:
            v6 = 'V';
            goto LABEL_12;
          case 1:
            v6 = 'S';
LABEL_12:
            data[0] = 'S';
            data[1] = 'T';
            data[3] = v6;
            data[2] = cmdId;
            data[4] = 'E';
            data[5] = 'N';
            result = sub_3528(env, usbdeviceconnection, usbendpoint, data, 6);
            break;
          default:
            result = 0;
            break;
        }
      }
      else
      {
        result = 0;
      }
    }
    else
    {
      result = 0;
    }
  }
  else
  {
    result = UsbEndpoint_bulkTransfer;
  }
  return result;
}

Der Code ist elementar, das Nachrichtenformat ist offensichtlich: Er beginnt mit der Signatur „ST“ und enthĂ€lt dann ein Befehlstypbyte - eines der Zeichen {'L', 'R', 'H', 'O', 'C', 'V', 'S. '}, dann das Byte cmdId (nur eine inkrementelle Kennung, die dem Befehl und der Antwort darauf entspricht) und endet mit der Signatur "EN". Die generierte Nachricht wird von sub_3528 gesendet.

Funktionscode sub_3528
int __cdecl sub_3528(JNIEnv *env, struct UsbDeviceConnection *usbdeviceconnection, struct UsbEndpoint *usbendpoint, void *data, int size)
{
  JNIEnv v5; // r1@1
  JNIEnv *v6; // r4@1
  int result; // r0@2
  int v8; // r2@3
  int tsize; // r7@5
  size_t fragmSize; // r5@9
  int v11; // r0@11
  JNIEnv v12; // r3@11
  int v13; // r6@11
  int rdOffs; // [sp+10h] [bp-80h]@7
  int v15; // [sp+14h] [bp-7Ch]@15
  int jbyteArray; // [sp+18h] [bp-78h]@1
  int fragmCnt; // [sp+1Ch] [bp-74h]@7
  char *_data; // [sp+28h] [bp-68h]@1
  char buf[64]; // [sp+34h] [bp-5Ch]@7
  int _stack_chk_guard; // [sp+74h] [bp-1Ch]@1

  _data = (char *)data;
  v5 = *env;
  v6 = env;
  _stack_chk_guard = ::_stack_chk_guard;
  jbyteArray = ((int (*)(void))v5->NewByteArray)();
  if ( jbyteArray )
  {
    v8 = UsbPackCounter + 1;
    if ( UsbPackCounter + 1 > 15 )
      v8 = 1;
    tsize = size;
    UsbPackCounter = v8;
    if ( size > 1024 )
      tsize = 1024;
    j_j_memset(buf, 0, 0x40u);
    buf[0] = 2;
    buf[3] = (tsize / 56 & 0x7F)
           + ((((tsize + -56 * (tsize / 56 & 0x7F)) >> 31) - (tsize + -56 * (tsize / 56 & 0x7Fu))) >> 31);
    rdOffs = 0;
    fragmCnt = 0;
    buf[2] = UsbPackCounter;
    while ( 1 )
    {
      if ( rdOffs >= tsize )
        goto LABEL_25;
      fragmCnt = (fragmCnt + 1) & 0xFF;
      fragmSize = tsize - rdOffs;
      if ( tsize - rdOffs > 56 )
        fragmSize = 56;
      buf[1] = fragmSize + 3;
      buf[4] = fragmCnt;
      j_j_memcpy(&buf[5], &_data[rdOffs], fragmSize);
      ((void (__fastcall *)(JNIEnv *, int, _DWORD, signed int))(*v6)->SetByteArrayRegion)(v6, jbyteArray, 0, 61);
      v11 = ((int (__fastcall *)(JNIEnv *))(*v6)->ExceptionCheck)(v6);
      v12 = *v6;
      v13 = v11;
      if ( v11 )
      {
        ((void (__fastcall *)(JNIEnv *))v12->ExceptionClear)(v6);
        v13 = 0;
        goto return_r6_del;
      }
      if ( !dword_B2A4 )
      {
LABEL_25:
        v13 = 1;
        goto return_r6_del;
      }
      v15 = ((int (__fastcall *)(JNIEnv *))v12->CallIntMethod)(v6);
      if ( ((int (__fastcall *)(JNIEnv *))(*v6)->ExceptionCheck)(v6) )
      {
        ((void (__fastcall *)(JNIEnv *))(*v6)->ExceptionClear)(v6);
        goto return_r6_del;
      }
      if ( v15 < 0 )
        break;
      rdOffs += fragmSize;
    }
    v13 = 0;
return_r6_del:
    ((void (__fastcall *)(JNIEnv *, int))(*v6)->DeleteLocalRef)(v6, jbyteArray);
    result = v13;
  }
  else
  {
    ((void (__fastcall *)(JNIEnv *))(*v6)->ExceptionClear)(v6);
    result = 0;
  }
  if ( _stack_chk_guard != ::_stack_chk_guard )
    j_j___stack_chk_fail(result);
  return result;
}


Diese Funktion ist etwas komplizierter. Es ist ersichtlich, dass die maximale LĂ€nge der gesendeten Nachricht auf 1024 Bytes begrenzt ist. Die Nachricht ist in Fragmente unterteilt. Das Fragment besteht aus 5 Bytes des Headers und maximal 56 Bytes Daten - insgesamt 61 Bytes. Header-Struktur:

  • buf [0] = 2 ist eine Konstante. Erinnerst du dich an ReportID 2? Es scheint, dass es das ist. Und ReportCount 60 - die GrĂ¶ĂŸe der verbleibenden Daten - ist ebenfalls gleich.
  • buf [1] = fragmSize + 3 - FragmentdatengrĂ¶ĂŸe + 3, dh Die GrĂ¶ĂŸe wird aus dem Byte berechnet, das dieser Variablen folgt.
  • buf [2] = UsbPackCounter ist nur ein ZĂ€hler, 1..15.
  • Der Wert von buf [3], der durch den ausgefallenen Ausdruck berechnet wird, ist nur die Anzahl der Fragmente. Sie können ihn wie folgt umschreiben:

    buf[3] = tsize / 56;
    if (tsize % 56) buf[3]++;
  • buf [4] = fragmCnt - Fragmentnummer, 1..buf [3].

Gebildete Fragmente werden durch einen Aufruf von CallIntMethod gesendet. Es hat den folgenden Typ:

jint (JNICALL *CallIntMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...);

Es ist ersichtlich, dass HexRays diesmal nicht verwaltet wurde - in den Argumenten nur v6 = JNIEnv * env. Im Assembler-Code ist jedoch alles vorhanden:

LDR     R2, [R2,#(dword_B2A4 - 0xB284)] ; jmethodID methodID

methodID wird in der Variablen dword_B2A4 gespeichert. Mal sehen, woher es kam: Die



Aufnahme erfolgt in den Funktionen usbOpen und usbClose. Offensichtlich interessieren wir uns fĂŒr usbOpen.

Das gewĂŒnschte Fragment:

v27 = ((int (__fastcall *)(JNIEnv *, int, const char *, const char *))(*v4)->GetMethodID)(
        v4,
        v26,
        "bulkTransfer",
        "(Landroid/hardware/usb/UsbEndpoint;[BII)I");
dword_B2A4 = v27;

Dies ist also die UsbEndpoint :: BulkTransfer-Methode. Und nichts - im Zusammenhang mit HID!

Nun wollen wir sehen, wie IR-Codes ĂŒbertragen werden - usbSendIR-Funktion.

Es ist ziemlich groß, aber der Bereich, der die Botschaft mit dem Team bildet, ist verstĂ€ndlich.

Fragment der usbSendIR-Funktion
  buf[0] = 'S';
  buf[1] = 'T';
  buf[2] = cmdId;
  buf[3] = 'D';
  if ( freq > 255 )
  {
    LOBYTE(v36) = 0;
    v37 = -1;
    v38 = 0;
    while ( 1 )
    {
      v39 = (freq - IrFreqTable[v38] + ((freq - IrFreqTable[v38]) >> 31)) ^ ((freq - IrFreqTable[v38]) >> 31);
      if ( v37 != -1 && v39 >= v37 )
      {
        v39 = v37;
      }
      else
      {
        LOBYTE(v36) = v38;
        if ( !v39 )
          break;
      }
      if ( ++v38 == 30 )
        break;
      v37 = v39;
    }
  }
  else
  {
    v36 = (unsigned int)(freq - 1) <= 0x1C ? freq : 0;
  }
  buf[4] = v36;
  v40 = &buf[v22];
  v40[5] = 'E';
  v40[6] = 'N';

Wie bei anderen Befehlen beginnt alles mit „ST“, gefolgt von cmdId und dem Befehlscode 'D'. Dann wird das Byte, das die Frequenz bestimmt - wenn das Argument freq> 255 ist - in der HĂ€ufigkeitstabelle IrFreqTable nachgeschlagen, andernfalls wird es direkt kopiert. Dann gehen die Daten und alles endet mit "EN".

Die Funktion mit dem verschleierten Namen "d" erwies sich als Parser der empfangenen Daten.

Ändern des Treibers und Arbeiten mit BulkTransfer


Nachdem ich die HID-API studiert hatte, stellte ich fest, dass sie im Grunde nicht die Möglichkeit bietet, BulkTransfer zu verwenden. Sie mĂŒssen also den Treiber Ă€ndern. Der WinUsb- Treiber ist fĂŒr die Arbeit mit diesem GerĂ€t geeignet .
Nachdem ich die Inf-Datei geschrieben hatte, Ă€nderte ich den Treiber in WinUsb und versuchte, Befehle zu senden. Alles funktionierte und eine Reaktion wurde vom GerĂ€t empfangen - als Antwort auf das Senden von Befehlen (ĂŒber WinUsb_WritePipe) kam eine Antwort in einem Ă€hnlichen Format.

Dump-Austausch mit ZaZaRemote


Trotz frĂŒherer Erfolge konnte ich bisher nicht die Hauptsache erreichen - das GerĂ€t zur Übertragung von IR-Befehlen zu zwingen. Die Anwendung war zu groß und verwirrend und wollte nur den USB-Verkehr entleeren. Wie geht das bei einer Android-Anwendung? Der Eintrag wurde als Android-x86 auf VirtualBox-e gefunden. Trotz der Tatsache, dass x86 niemals ARM ist, können Sie native ARM-BinĂ€rdateien ĂŒber den NativeBridge-Mechanismus ausfĂŒhren. Durch die Installation und Konfiguration der erforderlichen Software konnte ich diese Anwendung in VirtualBox zum Laufen bringen.

Die angeforderten Berechtigungen schaffen definitiv Vertrauen in diese Software.


Durch Starten der Anwendung und Einrichten der USB-Weiterleitung konnte ich den USB-Verkehr abhören. So erhielt ich eine Folge von Befehlen zum Initialisieren des Empfangs und Sendens von IR-Befehlen sowie Beispieldaten von IR-Paketen.

Es stellte sich heraus, dass das GerĂ€t nicht nur IR-Befehle senden, sondern auch empfangen kann, aber der Empfang funktionierte ziemlich mittelmĂ€ĂŸig - aus einer Entfernung von 10 cm und dann jedes zweite Mal.

Austauschbeispiel
- :

OUT:
0040 02 09 01 01 01 53 54 12 56 45 4e .....ST.VEN

IN:
0040 01 30 07 01 01 53 54 12 56 30 01 30 30 30 30 30 .0...ST.V0.00000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 02 01 01 53 54 13 53 45 4e .....ST.SEN

IN:
0040 01 0a 08 01 01 53 54 13 53 09 45 4e 30 30 30 30 .....ST.S.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 3b 03 02 01 53 54 14 44 00 ff ff ff ff b7 7f .;...ST.D.......
0050 7f 1b a3 23 a3 23 a3 69 a3 23 a3 23 a3 23 a3 23 ...#.#.i.#.#.#.#
0060 a3 23 a3 69 a3 69 a3 23 a3 69 a3 69 a3 69 a3 69 .#.i.i.#.i.i.i.i
0070 a3 69 a3 69 a3 23 a3 23 a3 23 a3 69 a3 .i.i.#.#.#.i.

OUT:
0040 02 2f 03 02 02 23 a3 23 a3 23 a3 23 a3 69 a3 69 ./...#.#.#.#.i.i
0050 a3 69 a3 23 a3 69 a3 69 a3 69 a3 7f 7f 7f 7f 7f .i.#.i.i.i......
0060 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 57 45 ..............WE
0070 4e N

IN:
0040 01 0a 09 01 01 53 54 14 4f 09 45 4e 30 30 30 30 .....ST.O.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

- :

OUT:
0040 02 09 01 01 01 53 54 17 56 45 4e .....ST.VEN

IN:
0040 01 30 0c 01 01 53 54 17 56 30 01 30 30 30 30 30 .0...ST.V0.00000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 02 01 01 53 54 18 53 45 4e .....ST.SEN

0040 01 0a 0d 01 01 53 54 18 53 09 45 4e 30 30 30 30 .....ST.S.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 03 01 01 53 54 19 52 45 4e .....ST.REN

0040 01 0a 0e 01 01 53 54 19 52 13 45 4e 30 30 30 30 .....ST.R.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 04 01 01 53 54 1a 43 45 4e .....ST.CEN

IN:
0040 01 0a 0f 01 01 53 54 1a 43 13 45 4e 30 30 30 30 .....ST.C.EN0000
0050 30 30 30 2d 30 30 30 30 2d 30 30 30 30 2d 30 30 000-0000-0000-00
0060 30 30 2d 30 30 30 30 30 30 30 30 30 30 30 31 09 00-000000000001.
0070 45 4e ff ff ff df ff f9 ef ff df ff bf fb ff EN.............

OUT:
0040 02 09 05 01 01 53 54 1b 4f 45 4e .....ST.OEN

IN:
0040 01 3b 01 0e 01 53 54 00 44 ff ff ff ff ba 7f 7f .;...ST.D.......
0050 19 a4 21 a4 21 a4 68 a4 22 a4 21 a4 22 a4 22 a4 ..!.!.h.".!.".".
0060 22 a4 68 a4 68 a4 21 a4 68 a4 68 a4 68 a4 68 a4 ".h.h.!.h.h.h.h.
0070 68 a4 68 a4 22 a4 22 a4 22 a4 68 a4 22 fb ff h.h.".".".h."..
.....
0040 01 2f 01 0e 0e 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f ./..............
0050 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f ................
0060 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 3e 13 45 .............>.E
0070 4e 82 02 82 02 82 7f 7f 7f 7f 7f 7f 7f fb ff N..............

Es stellte sich heraus, dass das GerĂ€t Probleme mit der GrĂ¶ĂŸe der zu sendenden Daten hat - zusĂ€tzlich zur Antwort wird jeglicher MĂŒll ausgeschĂŒttet - normalerweise das, was vom vorherigen Paket ĂŒbrig geblieben ist.

Das Studium der MĂŒllkippe und nachfolgende Experimente ermöglichten es, die Funktionen aller Teams herauszufinden:

  • 'V' - Version - Anforderung einer Version - mein GerĂ€t erzeugt eine Null-GUID;
  • 'L' - IdleMode - Standby-Modus - In diesem Modus befindet sich das GerĂ€t nach der Stromversorgung oder fĂŒhrt diesen Befehl aus.
  • 'S' — SendMode — — ;
  • 'R' — RecvMode — — ;
  • 'D' — Data — — , — ;
  • 'O' — Output — — , — / ;
  • 'C' — Cancel — — , 'O';
  • 'H' — Unknown — .


Nachdem ich einen Speicherauszug mit Steuerbefehlen und IR-Paketen erhalten hatte, konnte ich eine vollwertige GerĂ€testeuerung implementieren - ein IR-Signal empfangen und senden. Um jedoch ein beliebiges IR-Signal zu synthetisieren, musste das Format bestimmt werden, in dem es codiert ist. Zu diesem Zweck habe ich einen IR-Fotodetektor an ein Oszilloskop angeschlossen und begonnen, die gesendeten Signale zu untersuchen. Durch Experimente habe ich das Codierungsformat herausgefunden: Das hohe Bit jedes Bytes bestimmt, ob der Sender ein- oder ausgeschaltet ist, und die unteren 7 Bits bestimmen die Zeit. Die Zeiteinheit betrug 16 Mikrosekunden. Zum Beispiel: 8A - der Sender ist 160 Mikrosekunden lang eingeschaltet; 8A 05 FF 83 - 160 ÎŒs ein, Pause 80 ÎŒs, 2,08 ms ein.

Beim Einschalten des Senders pulsiert die LED mit einer Frequenz von ~ 36,64 kHz. Theoretisch sollte diese HĂ€ufigkeit durch das Argument freq des Befehls usbSendIR bestimmt werden. Experimente haben jedoch gezeigt, dass das GerĂ€t ĂŒberhaupt nicht auf dieses Argument reagiert. Trotzdem akzeptierten meine HaushaltsgerĂ€te normalerweise die Signale dieses Transceivers.
Das Format der vom GerÀt im Empfangsmodus aufgezeichneten Daten erwies sich als Àhnlich.

TiqiaaUsbIr-Klasse und IR-Steuerung


Ich habe die Transceiver-Steuerung in Form einer C ++ TiqiaaUsbIr-Klasse implementiert und ein einfaches CaptureIR QT-Programm geschrieben. ZusĂ€tzlich zu den Funktionen zum Empfangen und Senden von IR-Signalen habe ich die Synthese und Decodierung von Signalen unter Verwendung des NEC-Protokolls implementiert. Dieses Protokoll wird beispielsweise in Fernbedienungen von LG-FernsehgerĂ€ten verwendet. Ich habe auch das Speichern und Laden von IR-Signalen im Originalformat und im LIRC-Format implementiert. Es gab eine Idee, ein Modul fĂŒr WinLirc zu erstellen, aber es stellte sich heraus, dass es sich um eine krumme und nicht vollstĂ€ndig implementierte API handelt. Daher habe ich diese Idee vorerst verschoben.

Quellen und kompiliertes Programm können hier heruntergeladen werden .

Ein Beispiel fĂŒr die Verwendung der TiqiaaUsbIr-Klasse:

std::vector<std::string> IrDev;
TiqiaaUsbIr::EnumDevices(IrDev); //  
TiqiaaUsbIr Ir;
Ir.Open(IrDev[0].c_str()); //  
Ir.SendNecSignal(0x0408); //   (LG POWER)
Ir.Close();

Erfasstes Einschaltsignal:



Es wird synthetisiert:



WĂ€hrend der Erfassung ist ein Fehler aufgetreten:



Zusammenfassung


WĂ€hrend der Recherche wurde das USB-Protokoll des Tiqiaa Tview IR-Transceivers vollstĂ€ndig wiederhergestellt, eine Inf-Datei des Treibers und eine Software fĂŒr die Arbeit damit wurden geschrieben.

Der in Betracht gezogene IR-Transceiver ist ein sehr billiges, erschwingliches und kompaktes GerĂ€t (5 US-Dollar fĂŒr Ali, Abmessungen 15 x 10 x 5 mm) zur Steuerung von HaushaltsgerĂ€ten und zur Untersuchung seiner IR-Protokolle. Leider erwies sich die Steuerung der Frequenz des Senders als nicht funktionsfĂ€hig, was in meinem Fall keine Probleme verursachte, aber es ist möglich, dass es eine Technik mit anspruchsvolleren EmpfĂ€ngern gibt.

Der Empfangsmodus ist aufgrund des geringen Radius und der geringen ErfassungszuverlĂ€ssigkeit fĂŒr die Verwendung als vollwertiger IR-EmpfĂ€nger ungeeignet - eine Aufzeichnung erfolgreicher Erfassungsentfernungen von ~ 30 cm, wĂ€hrend die Fernbedienung genau auf den EmpfĂ€nger gerichtet ist und selbst nicht alle Fokussignale normalerweise erfasst werden. Es ist jedoch nĂŒtzlich, um Signale zu erfassen und Protokolle fĂŒr IR-Fernbedienungen zu untersuchen.

Bonus


Interessante IR-Codes fĂŒr LG-Fernseher: PS Ich suche Informationen ĂŒber LG AccessUSB, weitere Details hier .

POWER 0408
POWERON 04C4
POWEROFF 04C5
IN_STOP 04FA
IN_START 04FB
POWERONLY 04FE
EZ_ADJUST 04FF



All Articles