[번역] 호텔에서 잡히는 UDP STREAM을 리버싱 해보았다

최근에 호텔에 잠시 머물렀었다. 스마트 TV와 전자제품들이 있는 되게 고급진 호텔 중 하나였다. 다른 컴퓨터 너드들처럼 어떻게 작동하는지 궁금해졌고 WireShark를 열어보았다.

2046 포트에서 엄청난 양의 트래픽이 쏟아지는거 보고 되게 놀라웠다. 그래서 더 찾아봤는데 그닥 쓸모있는 데이터는 아니었다. 스탠다드 포트가 아니었고, 내가 직접 삽질을 해서 알아봤어야 했다.

처음에는 스마트 TV를 위한 방송 스트림 데이터인 줄 알았는데 그 네트워크 패킷의 길이가 영상 데이터라고 생각하기엔 너무 짧아 보였다. 오직 영상 프레임 하나라고 생각해도 말이다.

데이터를 살펴보자

UDP 패킷들은 내 IP로 전달되던 게 아니었고 난 ARP 스푸핑을 하고있지도 않았다. 그 패킷들이 모두에게 전달되고 있었다. 조금 더 삽질을 뚝딱 해보고 나서 그 패킷들은 멀티캐스트 패킷이란 걸 깨달았다! 이 말은 즉, 그 패킷들이 한 번만 전송되고 여러 디바이스들에서 동시에 전달받는다는 것이다. 또 다른 사실은, 모든 패킷이 모두 634 bytes 였다는 것이다.

파이썬으로 데이터들을 저장하고 분석해보기로 했다. 이 코드는 내가 멀티 패킷 받으려고 제일 처음으로 사용했던 코드이다. (이 코드에서 234.0.0.2는 WireShark에서 얻은 IP이다)

import socket
import struct

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 2046))

mreq = struct.pack("4sl", socket.inet_aton("234.0.0.2"), socket.INADDR_ANY)
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

while True:
data = s.recv(2048)
print(data)

소스를 살펴보면, 바이트들을 쉽게 읽을 수 있도록 binascii 를 사용해서 데이터를 16진수로 바꿔주기도 했다. 미친 척하고 콘솔에서 스크롤 내리면서 몇천 개의 패킷들을 보고나니, 첫 번째부터 15바이트까지는 똑같다. 아마도 프로토콜이나 패킷/명령어 ID 였을 거다. 근데 난 똑같은 데이터만 받았기 때문에 뭔지 알 수가 없다.

그놈의 오디오

패킷 맨 뒤에 있는 LAME3.91UUUUUUU이 스트링을 보기까지 되게 부끄러울 정도로 시간이 오래 걸렸다. 난 이걸 보고 MPEG로 압축된 오디오 데이터 인줄 알았다. test.mp3로 패킷 하나를 저장하고 mplayer로 실행해보니 되지 않았고, 그 플레이어는 mp3 파일을 test.mp3: data로만 인식했다. 근데 분명 그 패킷에는 test.mp3: data에 해당하는 “data”가 있었고, 뮤직 플레이어가 MPEG 오디오 파일을 실행할 때 그 data를 알고 있는 게 정상이었다. 그래서 다른 파이썬 스크립트를 짜서 패킷 데이터를 Offsets이랑 동시에 받아봤다.

이렇게 해서 test1 패킷에서 1바이트를, test2 패킷에서는 2byte를 건너 뛰는 식으로 파일을 저장 했다. 다음은 그때 사용한 파이썬 코드와 결과이다.
data = s.recv(2048)
for i in range(25):
open("test{}".format(i), "wb+").write(data[i:])

이거 이후에, file test*를 실행해봤는데 띠용~?! 이제 8 bytes만 건너뛰면 MPEG Audio data를 얻을 수 있다.

$ file test*
test0: data
test1: UNIF v-16624417 format NES ROM image
test10: UNIF v-763093498 format NES ROM image
test11: UNIF v-1093499874 format NES ROM image
test12: data
test13: TTComp archive, binary, 4K dictionary
test14: data
test15: data
test16: UNIF v-1939734368 format NES ROM image
test17: UNIF v-1198759424 format NES ROM image
test18: UNIF v-256340894 format NES ROM image
test19: UNIF v-839862132 format NES ROM image
test2: UNIF v-67173804 format NES ROM image
test20: data
test21: data
test22: data
test23: DOS executable (COM, 0x8C-variant)
test24: COM executable for DOS
test3: UNIF v-1325662462 format NES ROM image
test4: data
test5: data
test6: data
test7: data
test8: MPEG ADTS, layer III, v1, 192 kbps, 44.1 kHz, JntStereo
test9: UNIF v-2078407168 format NES ROM image
while True:
data = s.recv(2048)
sys.stdout.buffer.write(data[8:])

자, 이제 해야할건 처음 8 바이트를 건너뛰고, 패킷들을 계속 읽으면서 파일에다가 쓰기만 하면 됀다. 그럼 오디오 파일이 완벽하게 실행될거다.

근데 그 오디오 파일이 뭘까? 그냥 뜬금없이 들리는 걸까?? 스마트 TV랑 관련된 걸까? 호텔 시스템에 관련된 건가? 두근대는 마음으로 mp3파일을 실행해보았다.

$ python3 listen_2046.py > test.mp3
* wait a little to get a recording *
^C

$ mplayer test.mp3
MPlayer (C) 2000-2016 MPlayer Team
224 audio & 451 video codecs

Playing test.mp3.
libavformat version 57.25.100 (external)
Audio only file format detected.
=====
Starting playback...
A: 3.9 (03.8) of 13.0 (13.0) 0.7%

띠용?

아.. 이건

그냥 호텔 복도에서 엘레베이터 기다릴 때 나오는 노래였다. 엘리베이터 노래 때문에 몇 시간 동안 삽질했던 거다. 하 ㅋㅋㅋ 그래도 그 노래를 내 방에서 들을 수 있으니 다행이다.

출처: https://www.reddit.com/r/programming/comments/4kdmmb/reverse_engineering_a_mysterious_udp_stream_in_my/