1. 程式人生 > >GSM Sniffing入門之軟體篇:GSMTAP抓取與SMS(Short Message Service)

GSM Sniffing入門之軟體篇:GSMTAP抓取與SMS(Short Message Service)

From:http://www.cnblogs.com/k1two2/p/4539681.html

重點介紹如何利用50元左右的裝置,抓包並還原SMS簡訊內容:

ps:研究GSM Sniffing純屬個人興趣,能抓SMS報文只是撿了個明文傳輸的漏子,切勿用於非法用途。就像sylvain說的,osmocomBB並不是為抓包而實現的,如果沒有足夠的GSM相關知識,想實現還原語音通話內容根本就無從下手。

---------------------------------------------------------------------------------------------------

第二部分-軟體篇:GSMTAP抓取與SMS(Short Message Service)還原

之前介紹了OsmocomBB的硬體與刷機,這裡重點介紹下其附帶軟體的使用。

參考官方wiki可以知道osmocomBB的程式碼可以分為兩種:一種是在手機基帶晶片上跑的layer1(物理傳輸層);另一種是在PC上跑的與layer1通訊,提供上層服務的程式:

程式碼:
[[email protected] ~]# cd osmocom-bb/src/
[[email protected] src]# ls
Makefile  README.building  README.development  host  shared  target  target_dsp  wireshark

target下就是針對各手機的韌體,bin位於target/firmware/board/compal_e88下。Baseband firmware一節介紹了不同韌體的功能和對應程式,*.compalram是軟刷用的,斷電後需要重新刷機。*.e88flash/*.e88loader是配合loader使用的,刷入前需要參考 http://bb.osmocom.org/trac/wiki/flashing_new 把loader寫到手機中,然後在手機上用loader執行。
後面cell_log和ccch_scan都是對應layer1的,因為直接寫入有一定危險性,本文只演示軟刷(layer1.compalram)的使用方法。
回到src目錄下,接著看PC側的工具:

程式碼:
[[email protected] ~]# cd ~/osmocom-bb/src/
[[email protected] host]# ls
calypso_pll  fb_tools  gsmmap  layer23  osmocon  rita_pll

osmocon是刷入韌體,並與韌體通訊的程式,使用方法(注意C118選compal_e88/layer1.compalram.bin這個韌體):

程式碼:
$ cd host/osmocon/
$ ./osmocon -p /dev/ttyUSB0 -m c123xor ../../target/firmware/board/compal_e88/layer1.compalram.bin

將C118關機後,短按電源鍵就開始運行了。刷機過程和常見問題硬體篇都已經提過,這裡不再詳述。

layer23下,有實現不同功能的資料鏈路層/網路層程式,比如模擬手機功能的mobile(接入網路需要SIM卡),以及抓取相關資訊的雜項程式。直接進入misc目錄:

程式碼:
cd layer23/src/misc/

cell_log是一個掃描有效運營商頻率,並收集BCCH上基本資訊的工具,我們先用它來獲取運營商的ARFCN、MNC和MCC等資訊。這裡不需要gprs資料,直接使用這個引數:

程式碼:
-O --only-scan  Do a scan and show available ARFCNs, no data logging

./cell_log --only-scan
...
<000e> cell_log.c:248 Cell: ARFCN=56 PWR=-67dB MCC=460 MNC=00 (China, China Mobile)

例如這裡選取訊號最強的ARFCN=56 (China Mobile),有了這個就可以開始抓取Common Control Channel (CCCH)了:

程式碼:
./ccch_scan -a 56 -i 127.0.0.1

看到ccch_scan開始輸出burst內容後,就可以

程式碼:
sudo wireshark -k -i lo -f 'port 4729'

開啟Wireshark來抓GSMTAP,設定 gsm_sms 過濾器即可看到SMS報文內容:

-------------------------------------------------------------------------

為了加深對SMS傳輸的理解,我寫了個Python指令碼來重組簡訊的PDU。
下面部分需要些GSM網路相關的知識,推薦 GSM network and services 2G1723 2006 

從協議圖中得知,移動裝置(MS)和基站(BTS)間使用Um介面,最底層就是刷入手機的layer1物理傳輸層,之上分別是layer2資料鏈路層layer3網路層

位於圖中layer2的LAPDm,是一種保證資料傳輸不會出錯的協議。一個LAPDm幀共有23個位元組(184個位元),提供分片管理控制等功能:

layer3的協議則可以分為RR/MM/CM三種,這裡只列出嗅探相關的功能:

程式碼:
RR(Radio Resource Management):channel, cell控制等資訊,可以忽略
MM(Mobility Management):Location updating(如果需要接收方號碼,需要關注這個動作)
CM(Connection Management):Call Control(語音通話時的控制資訊,可以知道何時開始捕獲TCH), SMS(這裡的重點)

參考GSM的文件 TS 04.06 得知 LAPDm 的Address field欄位中,定義了 3.3.3 Service access point identifier (SAPI)

程式碼:

SAPI value  Related entity 
0  Call control signalling, mobility management signalling and radio resource management signalling
3  Short message service

SAPI=3就是我們要的Short message service,如圖:

3gpp的GSM文件看得比較暈,這裡直接對照Wireshark裡的gsm_sms報文分析,發現SMS幀實際是重組LAPDm的payload得到的。也就說如果想自己處理SMS幀,就必須也和Wireshark一樣重組LAPDm的payload,並解析其中的SMS PDU。

這是一個SAPI=3的LAPDm報文頭部。GSMTAP是一種偽頭部http://bb.osmocom.org/trac/wiki/GSMTAP,記錄了burst的一些基本資訊(如ChannelType,ARFCN,上行還是下行等)。因為是用ccch_scan捕獲的流量,編碼時只用關注 Channel Type: SDCCH/8 的LADPm協議。
為了方便訪問,定義GSMTAP類如下,傳入udp payload部分,解析GSMTAP並提供其後的資料:

程式碼: 複製程式碼
class GSMTAP:
  def __init__(self, gsmtap):
    self.gsmtap = gsmtap

    setattr(self, "version", ord(gsmtap[0]))
    setattr(self, "hdr_len", ord(gsmtap[1]) << 2)
    setattr(self, "payload_type", ord(gsmtap[2]))

    setattr(self, "time_slot", ord(gsmtap[3]))
    ARFCN = (ord(gsmtap[4])&0x3F)*0x100 + ord(gsmtap[5])
    UPLINK = ord(gsmtap[4]) >> 6
    setattr(self, "arfcn", ARFCN)
    setattr(self, "link", UPLINK)

    setattr(self, "signal_noise", ord(gsmtap[6]))
    setattr(self, "signal_level", ord(gsmtap[7]))
    # GSM Frame Number
    setattr(self, "channel_type", ord(gsmtap[12]))
    setattr(self, "antenna_number", ord(gsmtap[13]))
    setattr(self, "sub_slot", ord(gsmtap[14]))

  def get_payload(self):
    return self.gsmtap[self.hdr_len:]
複製程式碼 GSMTAP Header之後是 Link Access Procedure, Channel Dm,即LAPDm。參考TS 04.06有3個關鍵欄位: Address Field,Control Field,Length Field
Address Field
除了上面說的SAPI外都可以不關注。
Control Field比較關鍵,裡面記錄了該LAPDm的分片資訊。Frame type: Information frame說明當前是I幀(I frame),其餘bit為N(S)和N(R)。Send sequence number N(S)標記該分片的順序,從0開始遞增。看Wireshark原始碼說實際有些N(S)可能不是從0開始的,這裡組包就不判斷N(S)是否為0直接按順序附加。N(R)是Receive sequence number,看文件上I幀傳輸時N(R)的狀態沒看明白,直接默認同時間只有1個下行簡訊了,這樣收到的N(R)基本是一樣的(事實上大部分時候都是如此)
Length Field除了長度資訊,還有 More segments 標記,直到這個位為0才表示接收完一個完整的SMS報文

程式碼: 複製程式碼
class LAPDm:
  def __init__(self, lapdm):
    setattr(self, "lapdm", lapdm)

    setattr(self, "addr_field", ord(lapdm[0]))
    setattr(self, "lpd", (ord(lapdm[0])>>5)&0x3)
    setattr(self, "sapi", (ord(lapdm[0])>>2)&0x7)

    setattr(self, "ctrl_field", ord(lapdm[1]))
    setattr(self, "n_r", ord(lapdm[1])>>5)
    setattr(self, "n_s", (ord(lapdm[1])>>1)&0x7)

    setattr(self, "len_field", ord(lapdm[2]))
    setattr(self, "has_more", (ord(lapdm[2])>>1)&0x1)
    setattr(self, "length", ord(lapdm[2])>>2)

  def get_data(self):
    return self.lapdm[3:]
複製程式碼

之後就可以這樣,獲得LAPDm的相關資訊了:

程式碼: 複製程式碼
gsmtap = GSMTAP(gsm_payload)
lapdm = LAPDm(gsmtap.get_payload())

if (gsmtap.channel_type == 8) and (lapdm.sapi == 3):  # TS 04.06, 3.3.3, SAPI: 3 - Short message service
  debug_printf("LINK[%d] ARFCN=%d TIME_SLOT=%d CHANNEL=%d, N(R)=%d N(S)=%d, segment more[%d], payload len=%d\n" % \
    (gsmtap.link, gsmtap.arfcn, gsmtap.time_slot, gsmtap.channel_type, lapdm.n_r, lapdm.n_s, lapdm.has_more, lapdm.length))

  last_sms_payload += lapdm.get_data()    # 附加本次收到的資料
  if (lapdm.has_more == 0):      # 最後一個分片,解析整個 SMS payload
    hexdump(last_sms_payload)
    last_sms_payload = ""
複製程式碼

接著看wireshark中重組的payload,確認得到的last_sms_payload和wireshark中解析的一致。
在wireshark中展開一個重組後的SMS報文

可以看到,在 GSM SMS TPDU (GSM 03.40) SMS-DELIVER 之前,還有CP-DATA/RP-DATA頭,RP-DATA中有簡訊中心的資訊,但沒什麼作用直接跳過。我們只需要知道後面SMS TPDU的長度即可:

程式碼: 複製程式碼
class SMS:
  def __init__(self, payload):
    self.payload = payload

    iOff = 0
    # CP-DATA
    setattr(self, "protocol", ord(payload[iOff])&0xF); iOff+=1
    iOff += 2

    # RP-DATA (Network to MS)
    iOff += 2
    setattr(self, "RP_origin_len", ord(payload[iOff])); iOff+=1
    setattr(self, "RP_origin_ext", ord(payload[iOff]));
    setattr(self, "RP_origin", bcdDigits(payload[iOff+1:iOff+self.RP_origin_len]))
    iOff += self.RP_origin_len

    setattr(self, "RP_dest_len", ord(payload[iOff])); iOff+=1
    iOff += self.RP_dest_len

    setattr(self, "length", ord(payload[iOff])); iOff+=1
    setattr(self, "tpdu_off", iOff);

  def get_tpdu(self):
    return self.payload[self.tpdu_off:self.tpdu_off+self.length]
複製程式碼

呼叫 get_tpdu() 就會返回TPDU內容,裡面TP-Originating-Address就是傳送者的號碼,TP-User-Data就是我們要的簡訊內容。

程式碼: 複製程式碼
class TPDU:
  def __init__(self, tpdu):
    setattr(self, "tpdu", tpdu)
    
    iOff = 0
    # SMS-DELIVER
    iOff += 1
    setattr(self, "TP_origin_num", ord(tpdu[iOff])); iOff+=1
    setattr(self, "TP_origin_len", (self.TP_origin_num>>1)+(self.TP_origin_num%2))
    setattr(self, "TP_origin_ext", ord(tpdu[iOff])); iOff+=1
    setattr(self, "TP_origin", bcdDigits(tpdu[iOff:iOff+self.TP_origin_len]))
    iOff += self.TP_origin_len

    iOff += 2
    iOff += 7  # TimeStamp

    setattr(self, "tpu_len", ord(tpdu[iOff])); iOff+=1
    setattr(self, "data", tpdu[iOff:iOff+self.tpu_len])

  def get_data(self):
    return self.data.decode("utf-16be").encode("utf-8")
複製程式碼

中文在SMS中是UCS2編碼的,get_data() 是用python的utf-16be解碼原始資料,並轉成UTF-8輸出。

好了,加上process_sms_tpdu()函式,最終程式碼就是這樣:

程式碼: 複製程式碼
def process_sms_tpdu(sms_payload):
  hexdump(sms_payload)

  sms = SMS(sms_payload)
  tpdu = TPDU(sms.get_tpdu())
  debug_printf("[SMS from %s] %s" % (tpdu.TP_origin, tpdu.get_data()))

def handle_tcpdump_buffer(title, buffer):
  raw_struct = str2rawbuf(buffer)
  udp_packet = UDP(raw_struct)
  gsm_payload = udp_packet.get_payload()
  #hexdump(gsm_payload)

  gsmtap = GSMTAP(gsm_payload)
  lapdm = LAPDm(gsmtap.get_payload())

  if (gsmtap.channel_type == 8) and (lapdm.sapi == 3):    # TS 04.06, 3.3.3, SAPI: 3 - Short message service
    debug_printf("LINK[%d] ARFCN=%d TIME_SLOT=%d CHANNEL=%d, N(R)=%d N(S)=%d, segment more[%d], payload len=%d\n" % \
      (gsmtap.link, gsmtap.arfcn, gsmtap.time_slot, gsmtap.channel_type, lapdm.n_r, lapdm.n_s, lapdm.has_more, lapdm.length))

    global last_sms_payload
    last_sms_payload += lapdm.get_data()
    if (lapdm.has_more == 0):
      process_sms_tpdu(last_sms_payload)
      last_sms_payload = ""
複製程式碼

注:文末的 gsmtap_sms_decode_src.7z 裡有完整的解析指令碼 使用 ./ccch_scan -a ARFCN -i 127.0.0.1 將GSMTAP轉發到本機的4729埠後,可以用這個指令碼來重組SMS報文:

tcpdump -l -ilo -nXs0 udp and port 4729 | python2 -u show_gsmtap_sms.py

執行截圖:

-----------------------------------------------------------------------------------------

上面指令碼只是為了熟悉lapdm的重組,並未處理N(S)非零,以及併發時下行簡訊的重組建議有一定編碼能力的同學,可以參考wireshark原始碼進行資料還原:

程式碼: 複製程式碼
static void
dissect_lapdm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
            ... ...
            /* Rely on caller to provide a way to group fragments */
            fragment_id = (pinfo->circuit_id << 4) | (sapi << 1) | pinfo->p2p_dir;

            /* This doesn't seem the best way of doing it as doesn't
               take N(S) into account, but N(S) isn't always 0 for
               the first fragment!
             */
            fd_m = fragment_add_seq_next (&lapdm_reassembly_table, payload, 0,
                                pinfo,
                                fragment_id, /* guint32 ID for fragments belonging together */
                                NULL,
                                /*n_s guint32 fragment sequence number */
                                len, /* guint32 fragment length */
                                m); /* More fragments? */
            ... ...
}
複製程式碼

另外細心的各位可能會奇怪,下行簡訊裡怎麼沒有簡訊接受者的號碼,這裡有篇關於SMS傳輸的基本原理說明:
http://robinlea.com/pub/Amphol/Secur...arch_Labs.html
簡單來講,簡訊接受者的號碼、IMEI等資料,只有在"Location Update"時才會在網路中出現,並且是以加密形式傳輸的。當接收簡訊時,基站根據之前位置更新時註冊的資訊,判斷接收者的位置。所以,想要拿到接受者的號碼,需要破解A5/1演算法並還原出"Location Update"時的原文
Airprobe專案裡有介紹如何破解A5/1演算法找到Kc:https://srlabs.de/airprobe-how-to/ 只不過需要價格昂貴的USRP2...
另外還看到個RTL-SDR的文章(就是以前傳說中可以跟蹤飛機的電視棒),也支援Airprobe:
http://www.rtl-sdr.com/rtl-sdr-tutor...and-wireshark/
到此,GSM Sniffering入門算是告一段落了,感謝各位!

-----------------------------------------------------------------------------------------

關於抓上行簡訊或語音嗅探。看到這裡有篇討論:

這篇論文附錄裡有提到如何操作,他是在USRP2上實現的(A5/1 rainbow-table攻擊)。
OsmocomBB上好像做不到實時,不過mail list中倒是有些資料。TCH部分目前還是一頭霧水,如果有什麼比較好的思路可以探討一二