Reverse Engineering des Ngrok v2-Protokolls

Reverse Engineering des Ngrok v2-Protokolls


ngrok ist ein Dienst, mit dem Sie Tunnel zum lokalen Computer des Benutzers erstellen können. Mit anderen Worten ist eine öffentliche Adresse reserviert, an die alle Anrufe an den lokalen Port weitergeleitet werden.

Leider wurde seit 2016 die Unterstützung für die Open-Source-Version des Clients (ngrok v1) eingestellt, und um den Dienst zu nutzen, müssen Sie die geschlossene Version (ngrok v2) ausführen, was in vielen Fällen nicht akzeptabel ist. Dieser Artikel beschreibt das Erlernen des von einem offiziellen Client verwendeten Protokolls und das Erstellen eines alternativen offenen Clients.


Aber ist es notwendig? Ngrok Alternativen


Seltsamerweise hat dieser Service nur sehr wenige Alternativen. Im Einzelnen drei:


  • serveo.net . Bietet ähnliche Funktionen, verwendet jedoch die SSH-Weiterleitung über den umgekehrten Port und keinen benutzerdefinierten Client. Leider ist das Projekt derzeit geschlossen.

    Serveo ist wegen Phishing vorübergehend deaktiviert.

    Serveo wird in wenigen Tagen mit einigen neuen Einschränkungen zurückkehren, um Missbrauch zu unterbinden. Vielen Dank für Ihre Geduld!

  • localtunnel.me . Es wird nur ein HTTP-Tunnel mit einer Verteilung bereitgestellt, die auf dem Host-Header basiert. Bei HTTPS werden die Daten auf dem Server entschlüsselt und im Klartext an die Clientanwendung gesendet. Die Projektsite ist derzeit nicht verfügbar.
  • pagekite.net . Bietet HTTP- und TLS-Tunnel. Nach einer 30-tägigen Testphase müssen Sie für die weitere Verwendung bezahlen.

(PS Die Kommentare deuten darauf hin, dass es localhost.run gibt , das HTTP-Tunnel über SSH-Portweiterleitung bereitstellt, ähnlich wie serveo.net. Anscheinend stellt der Dienst jedoch im Gegensatz zu ngrok nur HTTP-Tunnel bereit.)


Naiver Versuch # 1: Mitmproxy


Versuchen wir, den offiziellen Anwendungsdatenverkehr mit mitmproxy zu überwachen:


$ mitmproxy $ http_proxy=http://127.0.0.1:8080 https_proxy=http://127.0.0.1:8080 ngrok http 8080 #     

Die Anwendung beginnt natürlich auf einem ungültigen Zertifikat zu schwören. Der Fehlertext zeigt jedoch, dass ngrok versucht, die Serveradresse tunnel.us.ngrok.com über DNS-over-HTTPS aufzulösen:


 Get https://dns.google.com/resolve?cd=true&name=tunnel.us.ngrok.com&type=AAAA: x509: certificate signed by unknown authority 

Versuchen wir, tunnel.us.ngrok.com selbst zu ziehen:


 $ curl https://tunnel.us.ngrok.com/ curl: (60) SSL certificate problem: unable to get local issuer certificate More details here: https://curl.haxx.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above. 

Anscheinend verwendet der Client das Anheften von Zertifikaten mit einem selbstsignierten Zertifikat. Versuchen wir den Fehler zu ignorieren:


 $ curl -k https://tunnel.us.ngrok.com Warning: Binary output can mess up your terminal. Use "--output -" to tell Warning: curl to output it to your terminal anyway, or consider "--output Warning: <FILE>" to save to a file. $ curl -k --output - https://tunnel.us.ngrok.com     -illegal WNDINC frame length: 0x474554 

Google stellt auf Anforderung der "illegalen WNDINC-Frame-Länge" eine Bibliothek für Go to Multiplex-TCP-Verbindungen zur Verfügung. Dieselbe Bibliothek wird im Zusammenhang mit einem Aufruf zum Öffnen von Quellcodes von ngrok v2 erwähnt.


Muxado-Bibliothek


Überprüfen Sie, ob ngrok die Muxado-Bibliothek wirklich verwendet:


 $ nm ./ngrok | grep muxado 00000000008ae2c0 T github.com/inconshreveable/muxado.(*addr).Network 00000000008ae2e0 T github.com/inconshreveable/muxado.(*addr).String 0000000000e31b40 B github.com/inconshreveable/muxado.bufferClosed 0000000000e31b50 B github.com/inconshreveable/muxado.bufferFull 00000000008ad430 T github.com/inconshreveable/muxado.Client 0000000000e31b60 B github.com/inconshreveable/muxado.closeError 00000000008b4c00 T github.com/inconshreveable/muxado.(*condWindow).Broadcast 00000000008b2ed0 T github.com/inconshreveable/muxado.(*condWindow).Decrement 00000000008b2da0 T github.com/inconshreveable/muxado.(*condWindow).Increment 00000000008b2d30 T github.com/inconshreveable/muxado.(*condWindow).Init ... 

Aus der Ausgabe dieses Befehls können verschiedene Schlussfolgerungen gezogen werden (Entschuldigung für die Tautologie):


  1. ngrok benutzt diese Bibliothek wirklich.
  2. Der Autor hat nicht versucht, die ausführbare Datei in irgendeiner Weise zu verschleiern, da darin noch Zeichen vorhanden waren.

Beachten Sie außerdem, dass der Fehler vom Server über eine sichere Verbindung (TLS) empfangen wurde. Dies bedeutet, dass das Muxado-Protokoll in einer TLS-Sitzung verwendet wird. Dies deutet darauf hin, dass über Muxado Daten im Klartext übertragen werden, da eine zusätzliche Verschlüsselung überflüssig wäre. Um unverschlüsselte Speicherauszüge zu entfernen, müssen Sie nur die Aufrufe (* stream) .Read und (* stream) .Write abfangen.


Abi


Bevor Sie versuchen, Anrufe abzufangen, müssen Sie wissen, wie die für uns interessanten Parameter übertragen werden. Dazu schreiben wir unter Verwendung der Bibliothek ein einfaches Programm auf Go (netcat fungiert als empfangende Partei):


Code
 package main import "net" import "github.com/inconshreveable/muxado" func main() { var conn net.Conn conn, _ = net.Dial("tcp", "127.0.0.1:1234") sess := muxado.Client(conn, &muxado.Config{}) conn, _ = sess.Open() data := []byte("Hello, world!") conn.Write(data) } 

Um den Verkehr abzufangen, interessieren wir uns für:


  • Eindeutige Thread-ID (zur Unterscheidung mehrerer gleichzeitig aktiver Threads erforderlich).
  • Zeiger auf einen Puffer mit Daten.
  • Die Länge des Datenpuffers.

Die Ausgabe von objdump auf dem github.com/inconshreveable/muxado.(*stream).Write-Funktion (lustig, dass Go-Entwickler sich anscheinend nicht mit der Namensverfälschung zu beschäftigen schienen)


  4de2d6: 48 8b 44 24 58 mov 0x58(%rsp),%rax 4de2db: 48 89 44 24 08 mov %rax,0x8(%rsp) 4de2e0: 48 8b 44 24 60 mov 0x60(%rsp),%rax 4de2e5: 48 89 44 24 10 mov %rax,0x10(%rsp) 4de2ea: 48 8b 44 24 68 mov 0x68(%rsp),%rax 4de2ef: 48 89 44 24 18 mov %rax,0x18(%rsp) 

Es bleibt abzuwarten, wo genau die Werte liegen, die wir auf dem Stack benötigen. Verwenden Sie dazu gdb und zeigen Sie den Status des Stapels zum Zeitpunkt des Funktionsaufrufs an.


 Thread 1 "test" hit Breakpoint 1, github.com/inconshreveable/muxado.(*stream).Write (buf=..., err=..., n=<optimized out>, s=<optimized out>) at /home/sergey/muxado/src/github.com/inconshreveable/muxado/stream.go:81 81 func (s *stream) Write(buf []byte) (n int, err error) { (gdb) set language c Warning: the current language does not match this frame. (gdb) p {char*[4]}$rsp $1 = { 0x4e0cbf <main.main+319> "H\213l$XH\203\304`\303\350\002A\367\377\351\255\376\377\377", '\314' <repeats 13 times>, "dH\213\f%\370\377\377\377H;a\020vKH\203\354\bH\211,$H\215,$\017\266\005k\003\025", 0xc0000b4000 "", 0xc000014300 "Hello, world!", 0xd <error: Cannot access memory at address 0xd>} 

Das erste Element dieses Arrays ist die Rücksprungadresse und kann nicht an der Übergabe von Argumenten teilnehmen. Die letzten beiden sind offensichtlich die Adresse des Arrays und seine Länge; Da der Zeiger auf den Stream in der Argumentliste an erster Stelle steht, ist es logisch anzunehmen, dass er sich in der zweiten Zelle befindet. Sie kann (unter Vorbehalt) als eindeutige Kennung für den Stream verwendet werden.

Jetzt wissen wir also, wie sich die Argumente der Funktion (* stream) .Write im Speicher befinden (für (* stream) .Lesen Sie alles genau gleich, da die Funktionen den gleichen Prototyp haben). Es bleibt die Überwachung selbst zu realisieren.


Naiver Versuch Nr. 2: Laufzeitfunktions-Hooks


Versuchen wir, Anrufe umzuleiten (* stream) .Schreiben Sie zur Proxy-Funktion:


Ungefähr so
 unsigned long long write_hook() { volatile long long* rsp = RSP(); void* stream = (void*)rsp[1]; char* buf = (char*)rsp[2]; long long len = rsp[3]; UNSET_HOOK(); unsigned long long ans; PUSH(rsp[3]); PUSH(rsp[2]); PUSH(rsp[1]); CALL(syscall_write); POP(24, ans); SET_HOOK(); return ans; } 

Wenn wir versuchen, ngrok mit diesem Hook aufzurufen, kommt es zu einem Absturz der folgenden Form:


 unexpected fault address 0x0 fatal error: fault [signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x4a2dc6] goroutine 1 [running]: runtime.throw(0xac7ca8, 0x5) /usr/local/Cellar/go/1.8.3/libexec/src/runtime/panic.go:596 +0x95 fp=0xc42016f9f0 sp=0xc42016f9d0 runtime.sigpanic() ... 

Hier warten wir auf ein unerwartetes Hindernis gegenüber Goroutinen. Tatsache ist, dass der Goroutine-Stapel dynamisch zugewiesen wird: Wenn im vorhandenen Stapel nicht genügend Speicherplatz vorhanden ist, wird er an einer anderen Stelle erneut zugewiesen und der aktuelle Inhalt wird kopiert. Leider speichern die von gcc generierten Funktionen den alten Stapelzeiger im rbp-Register (dem sogenannten Frame-Zeiger), und wenn Sie von einer solchen Funktion zurückkehren, zeigt der Stapelzeiger auf den bereits freigegebenen alten Stapel (use-after-free). C ist hier also kein Helfer.


Versuch 3: GDB-Skript


Wir werden ein Skript für gdb schreiben, das alle übertragenen Daten ausgibt:


 set language c break github.com/inconshreveable/muxado.(*stream).Write commands set $stream={void*}($rsp+8) set $buf={char*}($rsp+16) set $len={long long}($rsp+24) p $stream p (*$buf)@$len p $len cont end run tcp 8080 -log stdout -log-format logfmt -log-level debug 

Dies funktioniert, aber für einen vollständigen Speicherauszug müssen Sie die empfangenen Daten speichern. Und hier gibt es mehrere Probleme:


  • Um die empfangenen Daten zu lesen, müssen Sie warten, bis die Ausführung der Funktion abgeschlossen ist. Dieses Problem wird durch Festlegen eines Haltepunkts in der ret-Anweisung behoben.
  • Eine Funktion kann weniger Daten als geplant lesen, während die Anzahl der tatsächlich gelesenen Bytes einer der Rückgabewerte der Funktion ist. Sie müssen verstehen, wie Rückgabewerte übertragen werden. (Es ist auch trivial, einen Stapel zu drucken, nachdem die Funktion ausgeführt wurde. Die gewünschte Zahl liegt bei $ rsp + 48.)
  • Das dritte und wichtigste Problem. Die gdb-Ausgabe ist nicht für das automatische Parsen vorgesehen (siehe z. B. den Ausdruck im Abschnitt ABI), daher sind die so erhaltenen Speicherauszüge nur für die visuelle Analyse geeignet. (Eigentlich ist das kein Problem, da das Protokoll extrem einfach ist und auf einen Blick erkannt wird).

Versuch 4: Montage


Wenn Sie die Binärdatei mit ngrok objdump öffnen, sehen Sie, dass zwischen den Abschnitten .text und .rodata eine Lücke von 0xc10 = 3088 Byte besteht:


  9773eb: e9 50 ff ff ff jmpq 977340 <type..eq.[2]github.com/kevinburke/cli.Flag>   .rodata: 0000000000978000 <type.*>: 

Dieselbe Lücke befindet sich in der Datei selbst, in der der leere Bereich mit null Bytes gefüllt ist. Auf diese Weise können Sie die Größe des Segments ändern, das den in der Datei aufgezeichneten Textabschnitt enthält (Suchen / Ersetzen im Hex-Editor), und dem Anrufprotokollierungsbereich Code hinzufügen.

Der relative Übergangsbefehl in der x86_64-Architektur benötigt 5 Bytes: Opcode (E9) + Offset zur endgültigen Adresse (Int mit Vorzeichen). Da die Größe der ausführbaren ngrok-Datei deutlich unter 2 Gigabyte liegt, können Sie mit dieser Anweisung die Steuerung an eine beliebige Stelle im Textabschnitt übertragen, einschließlich unseres neuen Codes.

Der erste Befehl beider Funktionen benötigt 9 Bytes, sodass die ersten 5 Bytes des Befehls durch einen Sprungbefehl ersetzt werden können:


  8b0e70: 64 48 8b 0c 25 f8 ff mov %fs:0xfffffffffffffff8,%rcx 8b0e77: ff ff 

Um die ursprüngliche Funktion aufzurufen, befolgen Sie einfach die ursprüngliche Anweisung und rufen Sie die Adresse func + 9 auf

Mit der ret-Anweisung in der Funktion (* stream) .Read ist alles viel interessanter:


  8b0f6d: 7f 22 jg 8b0f91 <github.com/inconshreveable/muxado.(*stream).Read+0xa1> ... 8b0f8c: 48 83 c4 58 add $0x58,%rsp 8b0f90: c3 retq 8b0f91: 48 8b 5c 24 60 mov 0x60(%rsp),%rbx 

Die ret-Anweisung (im Gegensatz zu retf als retq geschrieben) belegt nur 1 Byte, während die darauf folgende Anweisung ein Sprungziel ist, sodass Sie es nicht ändern können. Der Übergangsbefehl wird jedoch nirgends am Befehl ret selbst ausgeführt, sodass nichts daran hindert, ihn zusammen mit dem vorherigen Befehl durch den Übergang zu ersetzen (natürlich muss er nach dem Übergang abgeschlossen werden).


Vollständiger Assembler-Logger-Code
 section .text org 0x9773f0 use64 write_pre_hook: ;      (*stream).Write push dword 0x74697277 call log add rsp, 8 mov rcx, [fs:-8] ;   (*stream).Write    jmp 0x8b0e79 read_post_hook: ;     0x8b0f8c add rsp, 0x58 ;    ret, .  push dword 0x64616572 call log add rsp, 8 ret log: ;  ,         push rdi push rsi push rdx push rax ;; stack layout: ;; rsp+32 ret ;; rsp+40 kind ('read' or 'writ') ;; rsp+48 ret0 ;; rsp+56 &stream ;; rsp+64 buf ;; rsp+72 len ;; rsp+80 unknown ;; rsp+88 n ;; rsp+96 err ;       mmap mov rax, 9 mov rdi, 0 mov rsi, 44 add rsi, [rsp+72] mov rdx, 3 push r10 push r8 push r9 mov r10, 34 mov r8, -1 mov r9, 0 syscall test rax, rax js segfault pop r9 pop r8 pop r10 ;     mov edi, [rsp+40] mov [rax], edi lea rdi, [rax+4] lea rsi, [rsp+56] push rcx mov rcx, 40 ;up to rsp+96 rep movsb ;    mov rsi, [rsp+72] ;rsp+64 mov rcx, [rsp+80] ;rsp+72 rep movsb pop rcx ;  write mov rdi, 3 mov rsi, rax mov rdx, 44 add rdx, [rsp+72] push rax call writeall ;   pop rdi mov rsi, 44 add rsi, [rsp+72] mov rax, 11 syscall test rax, rax jnz segfault ;   pop rax pop rdx pop rsi pop rdi ret writeall: mov rax, 1 syscall test rax, rax js segfault add rsi, rax sub rdx, rax test rdx, rdx jnz writeall ret segfault: ;          mov [0], rax 
Python-Programm zum Analysieren von Protokollen
 import sys stream = sys.stdin.buffer while True: chunk = stream.read(44) if not chunk: break assert len(chunk) == 44 kind = chunk[:4].decode('ascii') assert kind in ('read', 'writ') str_id = hex(int.from_bytes(chunk[4:12], 'little')) l = int.from_bytes(chunk[20:28], 'little') n = int.from_bytes(chunk[36:], 'little') if kind == 'read' else l buf = stream.read(l) assert len(buf) == l if '--full-data' not in sys.argv: buf = buf[:n] print('((%r, %s, %d), (%r, %d))'%(kind, str_id, l, buf, n)) 

Somit haben wir jetzt ein Werkzeug zum Entfernen von Verkehrsdumps aus ngrok. Probieren Sie es in Aktion aus!


Dump
 (('writ', 0xc420326600, 4), (b'\x00\x00\x00\x00', 4)) (('writ', 0xc420148e00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420326600, 227), (b'{"Version":["2"],"ClientId":"","Extra":{"OS":"linux","Arch":"amd64","Authtoken":"3FjYRxVDd2QvNkX13h82k_6Thwfp93PUZEpsz3vYe5v","Version":"2.2.8","Hostname":"tunnel.us.ngrok.com","UserAgent":"ngrok/2","Metadata":"","Cookie":""}}\n', 227)) (('read', 0xc420326600, 512), (b'{"Version":"2","ClientId":"df05b949e58359ea6901cff60935531d","Error":"","Extra":{"Version":"prod","Cookie":"lh7YagbqJ9ixLYyE05ZDMPvaYNVm5isu$xF1Mp8fDc689269YUGlGNAV/0XRyrEH390rwGqILZqYS5+qDUNbMn2l4puKD2CJHAgI83yo49aopujf0uhPBm4t997BTBvpFSg+zrgnrW9cRNuO8ApSe2+OPpUuPK0GZYZ1bpbz7Pod7cJycwVIgDFZZXLxEeNdXylQxSax9YOxgxcHeLBa79OjqrJpEUUWYtTNiMa5wxkr0AwKh","AccountName":"\xd0\xa1\xd0\xb5\xd1\x80\xd0\xb3\xd0\xb5\xd0\xb9 \xd0\x9b\xd0\xb8\xd1\x81\xd0\xbe\xd0\xb2","SessionDuration":0,"PlanName":"Free"}}', 430)) (('read', 0xc420148a00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420148200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc420148200, 111), (b'{"Id":"","Proto":"https","Opts":{"Hostname":"","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 111)) (('read', 0xc420148200, 512), (b'{"Id":"39b1e32e134eff8671b02268945643f9","URL":"https://deb82e2e.ngrok.io","Proto":"https","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"WUiWWfM9kRbpFpXoEOCydiJdEob7BKN0$EHrXSWq/fY/mRDRSTNqkVWEVCDJUyBdMSU5uSEMH5RHq5D9W1gA1BTWTUEUbltyhQIlhTJvGxezhDeOYqGe5CwNFHnIOVNidToULds48FCVdWc0zRC3Djyack74P9mQ11VHKQKAXPzXUXlUbo6TRkwMWKrpN0q93pmL3fQamRP6cREZTl2YMdnFUZtwHwyh4LGacxGAvdCP867rTKBL/3eWLdkcF2lSPdHuH8V51RzCMWMIbvmtyySzE', 512)) (('read', 0xc420148200, 1024), (b'cOIiZ09W6pMPTHoTcih0"}}', 23)) (('writ', 0xc4200ea200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc4200ea200, 127), (b'{"Id":"","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 127)) (('read', 0xc4200ea200, 512), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","URL":"http://deb82e2e.ngrok.io","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"G4nIrca8GTvq4H62sTmqdb144FmhMgrg$U6TwkKWafv/3+bFM5AP7xIFfkWqx+HUsYWhkYXivrtMfcqan0mKZx99LHGI7mm5lOMmvI+Kdy7WF/GnwrMDXrRFwhYowczaWKRKnUimnNtndq7rdttMevFabwe5WSzwf+IZhWzQ2yvcW31+qVuS7F6uykUSw+mnBNtsdXFSNpToagqQOM66A8LT+l3f3OOHKrWpdq39Bz2RfoRmXaRpkDrdfT6vPUQd6S8uVUnv3t2173Ik7AgT9PlzOMJ', 512)) (('read', 0xc4200ea200, 1024), (b'hhVDbeM2HP+qV6S5I="}}', 21)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('read', 0xc420148a00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148a00, 4), (b'\x00ys7', 4)) (('read', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('writ', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc4200ea200, 4), (b'\x00\x00\x00\x03', 4)) (('read', 0xc4200ea200, 8), (b'M\x00\x00\x00\x00\x00\x00\x00', 8)) (('read', 0xc4200ea200, 77), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","ClientAddr":"***.***.**.***:17815"}', 77)) (('read', 0xc4200ea200, 32768), (b'GET / HTTP/1.1\r\nHost: deb82e2e.ngrok.io\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15 Midori/6\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU\r\nX-Forwarded-For: ***.***.**.***\r\n\r\n', 359)) (('writ', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('writ', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc4200ea200, 32768), (b'', 0)) xF1Mp8fDc689269YUGlGNAV / 0XRyrEH390rwGqILZqYS5 + qDUNbMn2l4puKD2CJHAgI83yo49aopujf0uhPBm4t997BTBvpFSg + zrgnrW9cRNuO8ApSe2 + OPpUuPK0GZYZ1bpbz7Pod7cJycwVIgDFZZXLxEeNdXylQxSax9YOxgxcHeLBa79OjqrJpEUUWYtTNiMa5wxkr0AwKh", "Kontoname ": „\ xd0 \ XA1 \ xd0 \ XB5 \ xd1 \ x80 \ xd0 \ xB3 \ xd0 \ XB5 \ xd0 \ XB9 \ xd0 (('writ', 0xc420326600, 4), (b'\x00\x00\x00\x00', 4)) (('writ', 0xc420148e00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420326600, 227), (b'{"Version":["2"],"ClientId":"","Extra":{"OS":"linux","Arch":"amd64","Authtoken":"3FjYRxVDd2QvNkX13h82k_6Thwfp93PUZEpsz3vYe5v","Version":"2.2.8","Hostname":"tunnel.us.ngrok.com","UserAgent":"ngrok/2","Metadata":"","Cookie":""}}\n', 227)) (('read', 0xc420326600, 512), (b'{"Version":"2","ClientId":"df05b949e58359ea6901cff60935531d","Error":"","Extra":{"Version":"prod","Cookie":"lh7YagbqJ9ixLYyE05ZDMPvaYNVm5isu$xF1Mp8fDc689269YUGlGNAV/0XRyrEH390rwGqILZqYS5+qDUNbMn2l4puKD2CJHAgI83yo49aopujf0uhPBm4t997BTBvpFSg+zrgnrW9cRNuO8ApSe2+OPpUuPK0GZYZ1bpbz7Pod7cJycwVIgDFZZXLxEeNdXylQxSax9YOxgxcHeLBa79OjqrJpEUUWYtTNiMa5wxkr0AwKh","AccountName":"\xd0\xa1\xd0\xb5\xd1\x80\xd0\xb3\xd0\xb5\xd0\xb9 \xd0\x9b\xd0\xb8\xd1\x81\xd0\xbe\xd0\xb2","SessionDuration":0,"PlanName":"Free"}}', 430)) (('read', 0xc420148a00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420148200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc420148200, 111), (b'{"Id":"","Proto":"https","Opts":{"Hostname":"","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 111)) (('read', 0xc420148200, 512), (b'{"Id":"39b1e32e134eff8671b02268945643f9","URL":"https://deb82e2e.ngrok.io","Proto":"https","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"WUiWWfM9kRbpFpXoEOCydiJdEob7BKN0$EHrXSWq/fY/mRDRSTNqkVWEVCDJUyBdMSU5uSEMH5RHq5D9W1gA1BTWTUEUbltyhQIlhTJvGxezhDeOYqGe5CwNFHnIOVNidToULds48FCVdWc0zRC3Djyack74P9mQ11VHKQKAXPzXUXlUbo6TRkwMWKrpN0q93pmL3fQamRP6cREZTl2YMdnFUZtwHwyh4LGacxGAvdCP867rTKBL/3eWLdkcF2lSPdHuH8V51RzCMWMIbvmtyySzE', 512)) (('read', 0xc420148200, 1024), (b'cOIiZ09W6pMPTHoTcih0"}}', 23)) (('writ', 0xc4200ea200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc4200ea200, 127), (b'{"Id":"","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 127)) (('read', 0xc4200ea200, 512), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","URL":"http://deb82e2e.ngrok.io","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"G4nIrca8GTvq4H62sTmqdb144FmhMgrg$U6TwkKWafv/3+bFM5AP7xIFfkWqx+HUsYWhkYXivrtMfcqan0mKZx99LHGI7mm5lOMmvI+Kdy7WF/GnwrMDXrRFwhYowczaWKRKnUimnNtndq7rdttMevFabwe5WSzwf+IZhWzQ2yvcW31+qVuS7F6uykUSw+mnBNtsdXFSNpToagqQOM66A8LT+l3f3OOHKrWpdq39Bz2RfoRmXaRpkDrdfT6vPUQd6S8uVUnv3t2173Ik7AgT9PlzOMJ', 512)) (('read', 0xc4200ea200, 1024), (b'hhVDbeM2HP+qV6S5I="}}', 21)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('read', 0xc420148a00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148a00, 4), (b'\x00ys7', 4)) (('read', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('writ', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc4200ea200, 4), (b'\x00\x00\x00\x03', 4)) (('read', 0xc4200ea200, 8), (b'M\x00\x00\x00\x00\x00\x00\x00', 8)) (('read', 0xc4200ea200, 77), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","ClientAddr":"***.***.**.***:17815"}', 77)) (('read', 0xc4200ea200, 32768), (b'GET / HTTP/1.1\r\nHost: deb82e2e.ngrok.io\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15 Midori/6\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU\r\nX-Forwarded-For: ***.***.**.***\r\n\r\n', 359)) (('writ', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('writ', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc4200ea200, 32768), (b'', 0)) " WUiWWfM9kRbpFpXoEOCydiJdEob7BKN0 $ EHrXSWq / fy / mRDRSTNqkVWEVCDJUyBdMSU5uSEMH5RHq5D9W1gA1BTWTUEUbltyhQIlhTJvGxezhDeOYqGe5CwNFHnIOVNidToULds48FCVdWc0zRC3Djyack74P9mQ11VHKQKAXPzXUXlUbo6TRkwMWKrpN0q93pmL3fQamRP6cREZTl2YMdnFUZtwHwyh4LGacxGAvdCP867rTKBL / 3eWLdkcF2lSPdHuH8V51RzCMWMIbvmtyySzE‘, (('writ', 0xc420326600, 4), (b'\x00\x00\x00\x00', 4)) (('writ', 0xc420148e00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420326600, 227), (b'{"Version":["2"],"ClientId":"","Extra":{"OS":"linux","Arch":"amd64","Authtoken":"3FjYRxVDd2QvNkX13h82k_6Thwfp93PUZEpsz3vYe5v","Version":"2.2.8","Hostname":"tunnel.us.ngrok.com","UserAgent":"ngrok/2","Metadata":"","Cookie":""}}\n', 227)) (('read', 0xc420326600, 512), (b'{"Version":"2","ClientId":"df05b949e58359ea6901cff60935531d","Error":"","Extra":{"Version":"prod","Cookie":"lh7YagbqJ9ixLYyE05ZDMPvaYNVm5isu$xF1Mp8fDc689269YUGlGNAV/0XRyrEH390rwGqILZqYS5+qDUNbMn2l4puKD2CJHAgI83yo49aopujf0uhPBm4t997BTBvpFSg+zrgnrW9cRNuO8ApSe2+OPpUuPK0GZYZ1bpbz7Pod7cJycwVIgDFZZXLxEeNdXylQxSax9YOxgxcHeLBa79OjqrJpEUUWYtTNiMa5wxkr0AwKh","AccountName":"\xd0\xa1\xd0\xb5\xd1\x80\xd0\xb3\xd0\xb5\xd0\xb9 \xd0\x9b\xd0\xb8\xd1\x81\xd0\xbe\xd0\xb2","SessionDuration":0,"PlanName":"Free"}}', 430)) (('read', 0xc420148a00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420148200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc420148200, 111), (b'{"Id":"","Proto":"https","Opts":{"Hostname":"","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 111)) (('read', 0xc420148200, 512), (b'{"Id":"39b1e32e134eff8671b02268945643f9","URL":"https://deb82e2e.ngrok.io","Proto":"https","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"WUiWWfM9kRbpFpXoEOCydiJdEob7BKN0$EHrXSWq/fY/mRDRSTNqkVWEVCDJUyBdMSU5uSEMH5RHq5D9W1gA1BTWTUEUbltyhQIlhTJvGxezhDeOYqGe5CwNFHnIOVNidToULds48FCVdWc0zRC3Djyack74P9mQ11VHKQKAXPzXUXlUbo6TRkwMWKrpN0q93pmL3fQamRP6cREZTl2YMdnFUZtwHwyh4LGacxGAvdCP867rTKBL/3eWLdkcF2lSPdHuH8V51RzCMWMIbvmtyySzE', 512)) (('read', 0xc420148200, 1024), (b'cOIiZ09W6pMPTHoTcih0"}}', 23)) (('writ', 0xc4200ea200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc4200ea200, 127), (b'{"Id":"","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 127)) (('read', 0xc4200ea200, 512), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","URL":"http://deb82e2e.ngrok.io","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"G4nIrca8GTvq4H62sTmqdb144FmhMgrg$U6TwkKWafv/3+bFM5AP7xIFfkWqx+HUsYWhkYXivrtMfcqan0mKZx99LHGI7mm5lOMmvI+Kdy7WF/GnwrMDXrRFwhYowczaWKRKnUimnNtndq7rdttMevFabwe5WSzwf+IZhWzQ2yvcW31+qVuS7F6uykUSw+mnBNtsdXFSNpToagqQOM66A8LT+l3f3OOHKrWpdq39Bz2RfoRmXaRpkDrdfT6vPUQd6S8uVUnv3t2173Ik7AgT9PlzOMJ', 512)) (('read', 0xc4200ea200, 1024), (b'hhVDbeM2HP+qV6S5I="}}', 21)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('read', 0xc420148a00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148a00, 4), (b'\x00ys7', 4)) (('read', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('writ', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc4200ea200, 4), (b'\x00\x00\x00\x03', 4)) (('read', 0xc4200ea200, 8), (b'M\x00\x00\x00\x00\x00\x00\x00', 8)) (('read', 0xc4200ea200, 77), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","ClientAddr":"***.***.**.***:17815"}', 77)) (('read', 0xc4200ea200, 32768), (b'GET / HTTP/1.1\r\nHost: deb82e2e.ngrok.io\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15 Midori/6\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU\r\nX-Forwarded-For: ***.***.**.***\r\n\r\n', 359)) (('writ', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('writ', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc4200ea200, 32768), (b'', 0)) " G4nIrca8GTvq4H62sTmqdb144FmhMgrg $ U6TwkKWafv / (('writ', 0xc420326600, 4), (b'\x00\x00\x00\x00', 4)) (('writ', 0xc420148e00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420326600, 227), (b'{"Version":["2"],"ClientId":"","Extra":{"OS":"linux","Arch":"amd64","Authtoken":"3FjYRxVDd2QvNkX13h82k_6Thwfp93PUZEpsz3vYe5v","Version":"2.2.8","Hostname":"tunnel.us.ngrok.com","UserAgent":"ngrok/2","Metadata":"","Cookie":""}}\n', 227)) (('read', 0xc420326600, 512), (b'{"Version":"2","ClientId":"df05b949e58359ea6901cff60935531d","Error":"","Extra":{"Version":"prod","Cookie":"lh7YagbqJ9ixLYyE05ZDMPvaYNVm5isu$xF1Mp8fDc689269YUGlGNAV/0XRyrEH390rwGqILZqYS5+qDUNbMn2l4puKD2CJHAgI83yo49aopujf0uhPBm4t997BTBvpFSg+zrgnrW9cRNuO8ApSe2+OPpUuPK0GZYZ1bpbz7Pod7cJycwVIgDFZZXLxEeNdXylQxSax9YOxgxcHeLBa79OjqrJpEUUWYtTNiMa5wxkr0AwKh","AccountName":"\xd0\xa1\xd0\xb5\xd1\x80\xd0\xb3\xd0\xb5\xd0\xb9 \xd0\x9b\xd0\xb8\xd1\x81\xd0\xbe\xd0\xb2","SessionDuration":0,"PlanName":"Free"}}', 430)) (('read', 0xc420148a00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420148200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc420148200, 111), (b'{"Id":"","Proto":"https","Opts":{"Hostname":"","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 111)) (('read', 0xc420148200, 512), (b'{"Id":"39b1e32e134eff8671b02268945643f9","URL":"https://deb82e2e.ngrok.io","Proto":"https","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"WUiWWfM9kRbpFpXoEOCydiJdEob7BKN0$EHrXSWq/fY/mRDRSTNqkVWEVCDJUyBdMSU5uSEMH5RHq5D9W1gA1BTWTUEUbltyhQIlhTJvGxezhDeOYqGe5CwNFHnIOVNidToULds48FCVdWc0zRC3Djyack74P9mQ11VHKQKAXPzXUXlUbo6TRkwMWKrpN0q93pmL3fQamRP6cREZTl2YMdnFUZtwHwyh4LGacxGAvdCP867rTKBL/3eWLdkcF2lSPdHuH8V51RzCMWMIbvmtyySzE', 512)) (('read', 0xc420148200, 1024), (b'cOIiZ09W6pMPTHoTcih0"}}', 23)) (('writ', 0xc4200ea200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc4200ea200, 127), (b'{"Id":"","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 127)) (('read', 0xc4200ea200, 512), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","URL":"http://deb82e2e.ngrok.io","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"G4nIrca8GTvq4H62sTmqdb144FmhMgrg$U6TwkKWafv/3+bFM5AP7xIFfkWqx+HUsYWhkYXivrtMfcqan0mKZx99LHGI7mm5lOMmvI+Kdy7WF/GnwrMDXrRFwhYowczaWKRKnUimnNtndq7rdttMevFabwe5WSzwf+IZhWzQ2yvcW31+qVuS7F6uykUSw+mnBNtsdXFSNpToagqQOM66A8LT+l3f3OOHKrWpdq39Bz2RfoRmXaRpkDrdfT6vPUQd6S8uVUnv3t2173Ik7AgT9PlzOMJ', 512)) (('read', 0xc4200ea200, 1024), (b'hhVDbeM2HP+qV6S5I="}}', 21)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('read', 0xc420148a00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148a00, 4), (b'\x00ys7', 4)) (('read', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('writ', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc4200ea200, 4), (b'\x00\x00\x00\x03', 4)) (('read', 0xc4200ea200, 8), (b'M\x00\x00\x00\x00\x00\x00\x00', 8)) (('read', 0xc4200ea200, 77), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","ClientAddr":"***.***.**.***:17815"}', 77)) (('read', 0xc4200ea200, 32768), (b'GET / HTTP/1.1\r\nHost: deb82e2e.ngrok.io\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15 Midori/6\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU\r\nX-Forwarded-For: ***.***.**.***\r\n\r\n', 359)) (('writ', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('writ', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc4200ea200, 32768), (b'', 0)) + IZhWzQ2yvcW31 + qVuS7F6uykUSw + mnBNtsdXFSNpToagqQOM66A8LT + l3f3OOHKrWpdq39Bz2RfoRmXaRpkDrdfT6vPUQd6S8uVUnv3t2173Ik7AgT9PlzOMJ‘, (('writ', 0xc420326600, 4), (b'\x00\x00\x00\x00', 4)) (('writ', 0xc420148e00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420326600, 227), (b'{"Version":["2"],"ClientId":"","Extra":{"OS":"linux","Arch":"amd64","Authtoken":"3FjYRxVDd2QvNkX13h82k_6Thwfp93PUZEpsz3vYe5v","Version":"2.2.8","Hostname":"tunnel.us.ngrok.com","UserAgent":"ngrok/2","Metadata":"","Cookie":""}}\n', 227)) (('read', 0xc420326600, 512), (b'{"Version":"2","ClientId":"df05b949e58359ea6901cff60935531d","Error":"","Extra":{"Version":"prod","Cookie":"lh7YagbqJ9ixLYyE05ZDMPvaYNVm5isu$xF1Mp8fDc689269YUGlGNAV/0XRyrEH390rwGqILZqYS5+qDUNbMn2l4puKD2CJHAgI83yo49aopujf0uhPBm4t997BTBvpFSg+zrgnrW9cRNuO8ApSe2+OPpUuPK0GZYZ1bpbz7Pod7cJycwVIgDFZZXLxEeNdXylQxSax9YOxgxcHeLBa79OjqrJpEUUWYtTNiMa5wxkr0AwKh","AccountName":"\xd0\xa1\xd0\xb5\xd1\x80\xd0\xb3\xd0\xb5\xd0\xb9 \xd0\x9b\xd0\xb8\xd1\x81\xd0\xbe\xd0\xb2","SessionDuration":0,"PlanName":"Free"}}', 430)) (('read', 0xc420148a00, 4), (b'\xff\xff\xff\xff', 4)) (('writ', 0xc420148200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc420148200, 111), (b'{"Id":"","Proto":"https","Opts":{"Hostname":"","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 111)) (('read', 0xc420148200, 512), (b'{"Id":"39b1e32e134eff8671b02268945643f9","URL":"https://deb82e2e.ngrok.io","Proto":"https","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"WUiWWfM9kRbpFpXoEOCydiJdEob7BKN0$EHrXSWq/fY/mRDRSTNqkVWEVCDJUyBdMSU5uSEMH5RHq5D9W1gA1BTWTUEUbltyhQIlhTJvGxezhDeOYqGe5CwNFHnIOVNidToULds48FCVdWc0zRC3Djyack74P9mQ11VHKQKAXPzXUXlUbo6TRkwMWKrpN0q93pmL3fQamRP6cREZTl2YMdnFUZtwHwyh4LGacxGAvdCP867rTKBL/3eWLdkcF2lSPdHuH8V51RzCMWMIbvmtyySzE', 512)) (('read', 0xc420148200, 1024), (b'cOIiZ09W6pMPTHoTcih0"}}', 23)) (('writ', 0xc4200ea200, 4), (b'\x00\x00\x00\x01', 4)) (('writ', 0xc4200ea200, 127), (b'{"Id":"","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":""},"Extra":{"Balance":false,"Token":""}}\n', 127)) (('read', 0xc4200ea200, 512), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","URL":"http://deb82e2e.ngrok.io","Proto":"http","Opts":{"Hostname":"deb82e2e.ngrok.io","Auth":"","Subdomain":"","HostHeaderRewrite":false,"LocalURLScheme":""},"Error":"","Extra":{"Token":"G4nIrca8GTvq4H62sTmqdb144FmhMgrg$U6TwkKWafv/3+bFM5AP7xIFfkWqx+HUsYWhkYXivrtMfcqan0mKZx99LHGI7mm5lOMmvI+Kdy7WF/GnwrMDXrRFwhYowczaWKRKnUimnNtndq7rdttMevFabwe5WSzwf+IZhWzQ2yvcW31+qVuS7F6uykUSw+mnBNtsdXFSNpToagqQOM66A8LT+l3f3OOHKrWpdq39Bz2RfoRmXaRpkDrdfT6vPUQd6S8uVUnv3t2173Ik7AgT9PlzOMJ', 512)) (('read', 0xc4200ea200, 1024), (b'hhVDbeM2HP+qV6S5I="}}', 21)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x00ys7', 4)) (('read', 0xc420148a00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148a00, 4), (b'\x00ys7', 4)) (('read', 0xc420148e00, 4), (b'\x00ys7', 4)) (('writ', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('writ', 0xc420148a00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc420148e00, 4), (b'\x11|\xb9\x99', 4)) (('read', 0xc4200ea200, 4), (b'\x00\x00\x00\x03', 4)) (('read', 0xc4200ea200, 8), (b'M\x00\x00\x00\x00\x00\x00\x00', 8)) (('read', 0xc4200ea200, 77), (b'{"Id":"9714ddd4cb111adf6599f099cec98482","ClientAddr":"***.***.**.***:17815"}', 77)) (('read', 0xc4200ea200, 32768), (b'GET / HTTP/1.1\r\nHost: deb82e2e.ngrok.io\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15 Midori/6\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU\r\nX-Forwarded-For: ***.***.**.***\r\n\r\n', 359)) (('writ', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148a00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('read', 0xc420148e00, 4), (b'\x0e\xb2\xc0\x01', 4)) (('writ', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('writ', 0xc420148a00, 4), (b'X\xa3?+', 4)) (('read', 0xc420148e00, 4), (b'X\xa3?+', 4)) (('read', 0xc4200ea200, 32768), (b'', 0)) 

Aus diesem Speicherauszug können Sie die interne Struktur des Protokolls klar ersehen:


  • Offensichtlich werden die Berechtigungs- und Tunnelerstellungsflüsse vom Client initiiert, und die Flüsse mit den Verbindungen selbst werden vom Server initiiert. Dies steht nicht in den Protokollen, ist aber aus Gründen des gesunden Menschenverstands offensichtlich.
  • Zu Beginn jedes Streams wird eine 32-Bit-Nummer übertragen - die Art des Streams. Dies ist 0 für die Autorisierung, 1 für das Erstellen eines Tunnels und 3 für eingehende Verbindungen.
  • Ein Stream vom Typ -1 ist ein Herzschlag. Der Verbindungsinitiator sendet dort in regelmäßigen Abständen 4 Bytes nach dem Zufallsprinzip und erwartet deren Empfang am Ausgang. Es gibt 2 solche Ströme in beide Richtungen.
  • Wenn eine eingehende Verbindung empfangen wird, werden ein 32-Bit-Typ 3, eine 64-Bit-Nummer L (Little-Endian) und ein JSON-Objekt der Länge L Bytes übertragen, die die Verbindung beschreiben. Danach werden Rohdaten ohne Service-Pakete über die Verbindung übertragen.

Fazit


Da muxado eine Open-Source-Bibliothek ist, kann das Multiplexing-Protokoll von der Quelle gelernt werden . Es macht keinen Sinn, es hierher zu bringen.

Das Ergebnis war eine Python-Bibliothek für die Arbeit mit dem ngrok-Protokoll und ein alternativer Konsolen-Client, der diese Bibliothek verwendet. Github

PS Konstruktive Kritik ist willkommen.

Source: https://habr.com/ru/post/de485564/


All Articles