Protokol reverse engineering Ngrok v2

Protokol reverse engineering Ngrok v2


ngrok adalah layanan yang memungkinkan Anda membuat terowongan ke komputer lokal pengguna. Dengan kata lain, alamat publik dicadangkan, semua panggilan yang diteruskan ke port lokal.

Sayangnya, sejak 2016, dukungan untuk versi open-source dari klien (ngrok v1) telah dihentikan, dan untuk menggunakan layanan ini, Anda perlu menjalankan versi tertutup (ngrok v2), yang dalam banyak kasus tidak dapat diterima. Artikel ini menjelaskan proses mempelajari protokol yang digunakan oleh klien resmi dan membuat klien terbuka alternatif.


Tetapi apakah itu perlu? Alternatif Ngrok


Anehnya, layanan ini memiliki sangat sedikit alternatif. Secara khusus, tiga:


  • serveo.net . Menyediakan fungsionalitas yang serupa, tetapi menggunakan penerusan port terbalik SSH, bukan klien khusus. Sayangnya, proyek ini sedang ditutup.

    Serveo dinonaktifkan sementara karena phishing.

    Serveo akan kembali dalam beberapa hari dengan beberapa batasan baru untuk membantu mencegah pelecehan. Terima kasih atas kesabaran anda!

  • localtunnel.me . Ini hanya menyediakan terowongan HTTP dengan distribusi berdasarkan header Host, dan dalam kasus HTTPS, data didekripsi di server dan dikirim ke aplikasi klien dalam teks yang jelas. Situs proyek saat ini tidak tersedia.
  • pagekite.net . Menyediakan terowongan HTTP dan TLS. Setelah masa uji coba 30 hari, Anda harus membayar untuk penggunaan lebih lanjut.

(PS Komentar menyarankan bahwa ada localhost.run yang menyediakan terowongan HTTP melalui penerusan port SSH, mirip dengan serveo.net. Namun, layanan ini sepertinya hanya menyediakan terowongan HTTP, tidak seperti ngrok)


Upaya naif # 1: mitmproxy


Mari kita coba mendengarkan lalu lintas aplikasi resmi menggunakan mitmproxy:


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

Aplikasi, tentu saja, mulai bersumpah pada sertifikat yang tidak valid. Namun, teks kesalahan menunjukkan bahwa ngrok sedang mencoba untuk menyelesaikan alamat server tunnel.us.ngrok.com melalui DNS-over-HTTPS:


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

Mari coba tarik tunnel.us.ngrok.com sendiri:


 $ 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. 

Tampaknya, klien menggunakan pinning sertifikat dengan sertifikat yang ditandatangani sendiri. Mari kita coba untuk mengabaikan kesalahan:


 $ 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 atas permintaan "panjang bingkai WNDINC ilegal" menyediakan perpustakaan untuk koneksi TCP multipleks ke Go . Pustaka yang sama disebutkan dalam masalah dengan panggilan untuk membuka kode sumber ngrok v2.


Perpustakaan Muxado


Periksa apakah ngrok benar-benar menggunakan pustaka muxado:


 $ 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 ... 

Beberapa kesimpulan dapat diambil dari output dari perintah ini (maaf untuk tautologi):


  1. ngrok benar-benar menggunakan perpustakaan ini.
  2. Penulis tidak mencoba untuk mengaburkan file yang dapat dieksekusi dengan cara apa pun, karena karakter dibiarkan di dalamnya.

Perhatikan juga bahwa kesalahan dari server diterima melalui koneksi aman (TLS), yang berarti protokol muxado digunakan di dalam sesi TLS. Ini menunjukkan bahwa lebih dari data muxado ditransmisikan dalam teks yang jelas, karena enkripsi tambahan akan menjadi berlebihan. Jadi, untuk menghapus dump lalu lintas yang tidak dienkripsi, cukup untuk mencegat panggilan (* stream). Baca dan (* stream). Tulis.


Abi


Sebelum Anda mencoba mencegat panggilan, Anda perlu memahami bagaimana parameter yang menarik bagi kami dikirimkan. Untuk melakukan ini, kami akan menulis program sederhana di Go menggunakan perpustakaan (netcat akan bertindak sebagai pihak penerima):


Kode
 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) } 

Jadi, untuk mencegat lalu lintas, kami tertarik pada:


  • Pengidentifikasi utas unik (diperlukan untuk membedakan beberapa utas yang aktif secara bersamaan).
  • Pointer ke buffer dengan data.
  • Panjang buffer data.

Output dari objdump di github.com/inconshreveable/muxado.(*stream).Fungsi penulisan (lucu bahwa pengembang Go tampaknya tidak repot dengan nama mangling.) Jelas menunjukkan pemuatan argumen dari stack:


  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) 

Masih harus dipahami dengan tepat di mana nilai-nilai yang kita butuhkan berada di tumpukan. Untuk melakukan ini, gunakan gdb dan tampilkan status stack pada saat fungsi dipanggil.


 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>} 

Elemen pertama dari array ini adalah alamat pengirim, dan tidak dapat berpartisipasi dalam argumen yang lewat. Dua yang terakhir, jelas, adalah alamat array dan panjangnya; karena penunjuk ke aliran masuk pertama dalam daftar argumen, logis untuk menganggap bahwa itu ada di sel kedua. Itu dapat (dengan reservasi) digunakan sebagai pengidentifikasi unik untuk aliran.

Jadi, sekarang kita tahu bagaimana argumen fungsi (* stream). Menulis terletak di memori (untuk (* stream). Baca semuanya persis sama, karena fungsi memiliki prototipe yang sama). Tetap menyadari intersepsi itu sendiri.


Upaya naif # 2: kait fungsi runtime


Mari kita coba untuk mengalihkan panggilan (* stream). Tulis ke fungsi proxy:


Sesuatu seperti ini
 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; } 

Saat mencoba menelepon ngrok dengan kait ini, kami mengalami kerusakan dari formulir berikut:


 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() ... 

Di sini kita sedang menunggu hambatan tak terduga di wajah goroutin. Faktanya adalah bahwa tumpukan goroutine dialokasikan secara dinamis: jika tidak ada cukup ruang di tumpukan yang ada, itu dialokasikan lagi di tempat lain, dan konten saat ini disalin. Sayangnya, fungsi-fungsi yang dihasilkan oleh gcc menyimpan pointer stack lama dalam register rbp (yang disebut frame pointer), dan ketika Anda kembali dari fungsi seperti itu, pointer stack mulai menunjuk ke stack lama yang sudah dibebaskan (use-after-free). Jadi C bukan penolong di sini.


Percobaan # 3: skrip gdb


Kami akan menulis skrip untuk gdb yang akan mencetak semua data yang dikirimkan:


 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 

Ini berfungsi, tetapi untuk dump lengkap, Anda perlu menyimpan data yang diterima. Dan di sini ada beberapa masalah:


  • Untuk membaca data yang diterima, Anda harus menunggu sampai fungsi menyelesaikan eksekusi. Masalah ini diselesaikan dengan menetapkan breakpoint pada pernyataan ret.
  • Suatu fungsi dapat membaca lebih sedikit data dari yang direncanakan, sedangkan jumlah byte yang benar-benar dibaca adalah salah satu nilai pengembalian fungsi. Anda perlu memahami cara mengembalikan nilai. (Juga sepele untuk mencetak tumpukan setelah fungsi dieksekusi. Angka yang diinginkan adalah $ rsp + 48).
  • Masalah ketiga dan paling penting. Keluaran gdb tidak dimaksudkan untuk penguraian otomatis (misalnya, lihat cetakan dari bagian ABI), oleh karena itu kesedihan yang diperoleh hanya cocok untuk analisis visual. (Sebenarnya, ini bukan masalah, karena protokolnya sangat sederhana dan diakui sekilas).

Percobaan # 4: perakitan


Membuka biner dengan ngrok objdump, Anda dapat melihat bahwa ada celah 0xc10 = 3088 byte antara bagian .text dan .rodata:


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

Kesenjangan yang sama hadir dalam file itu sendiri, di mana ruang kosong diisi dengan nol byte. Ini memungkinkan Anda untuk mengubah ukuran segmen yang berisi bagian .text yang direkam dalam file (cari / ganti di hex editor), dan tambahkan kode ke ruang pencatatan panggilan.

Instruksi transisi relatif pada arsitektur x86_64 membutuhkan waktu 5 byte: opcode (E9) + offset ke alamat akhir (masuk int). Karena ukuran file executable ngrok kurang dari 2 gigabyte, instruksi ini memungkinkan Anda untuk mentransfer kontrol ke mana saja di bagian .text, termasuk kode baru kami.

Instruksi pertama dari kedua fungsi tersebut membutuhkan 9 byte, sehingga 5 byte pertama dari instruksi tersebut dapat diganti dengan instruksi melompat:


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

Untuk memanggil fungsi asli, cukup ikuti instruksi asli dan pergi ke alamat func + 9

Dengan pernyataan ret dalam fungsi (* stream). Baca, semuanya jauh lebih menarik:


  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 

Instruksi ret (ditulis sebagai retq, kebalikan dari retf) hanya menempati 1 byte, sedangkan instruksi yang mengikutinya adalah target lompatan, jadi Anda tidak dapat mengubahnya. Namun, instruksi transisi tidak dilakukan di mana saja pada instruksi ret itu sendiri, sehingga tidak ada yang menghentikannya diganti dengan transisi bersama dengan instruksi sebelumnya (setelah transisi, tentu saja, itu harus diselesaikan).


Kode logger assembler lengkap
 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 
Program python untuk parsing log
 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)) 

Dengan demikian, sekarang kami memiliki alat yang berfungsi untuk menghapus dump lalu lintas dari ngrok. Lihat beraksi!


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", "AccountName": "\ 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 / TA / 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)) 

Dari dump ini Anda dapat dengan jelas melihat struktur internal protokol:


  • Jelas, aliran otorisasi dan pembuatan terowongan diprakarsai oleh klien, dan aliran dengan koneksi sendiri diprakarsai oleh server. Ini bukan dalam log, tetapi jelas karena alasan akal sehat.
  • Pada awal setiap aliran, sejumlah 32-bit ditransmisikan - jenis aliran. Ini 0 untuk otorisasi, 1 untuk membuat terowongan, dan 3 untuk koneksi masuk.
  • Aliran dengan tipe -1 adalah detak jantung. Inisiator koneksi secara berkala mengirimkan acak 4 byte di sana dan mengharapkan untuk menerimanya pada output. Ada 2 aliran di kedua arah.
  • Ketika koneksi masuk diterima, 32-bit tipe 3, nomor 64-bit L (little-endian) dan objek JSON dengan panjang L byte yang menggambarkan koneksi ditransmisikan. Setelah itu, data mentah ditransmisikan melalui koneksi tanpa paket layanan.

Kesimpulan


Karena muxado adalah pustaka sumber terbuka, protokol multiplexing dapat dipelajari dari sumbernya . Membawanya ke sini tidak masuk akal.

Hasilnya adalah pustaka Python untuk bekerja dengan protokol ngrok, dan klien konsol alternatif menggunakan pustaka ini. Github

PS Kritik konstruktif dipersilahkan.

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


All Articles