Live Messengerのメッセージを取得

投稿日 : 2008年4月30日 21:25

 コード書いてみました。流れるパケットを見て、Live Messengerのメッセージ内容だけ取り出します。Live Messengerの仕様が公開されているわけではないので、パケットを実際に解析してみて書いたものになります。

 まずは、パケットキャプチャを開始するメソッド。Socketオブジェクトを作って、設定後、BeginReceiveメソッドを呼びます。引数には、コールバック関数を指定しています。Bindメソッドを呼んでいるところで指定しているIpAddressはPrivate変数です。この後、出てきます。

Const BufferSize As Integer = 4096
Private Socket As Socket
Private Buffer(BufferSize) As Byte

Public Sub BeginReceive()
    Socket = New Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP)
    Socket.Blocking = False
    Socket.Bind(New IPEndPoint(IpAddress, 0))
    Socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AcceptConnection, True)
    Socket.IOControl(IOControlCode.ReceiveAll, New Byte() {1, 0, 0, 0}, New Byte() {0, 0, 0, 0})
    Socket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, New AsyncCallback(AddressOf ReceptionCallback), Nothing)
End Sub

 続いてコールバック関数の内容。パケットの中身がPrivate変数のBufferに入っているので、それを解析する自作メソッドParseを呼びます。最後にまたBeginReceiveメソッドを呼んで何度も受信するようにします。

Private Sub ReceptionCallback(ByVal ar As IAsyncResult)
    Dim size = Socket.EndReceive(ar)
    Parse(Buffer, size)
    Socket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, New AsyncCallback(AddressOf ReceptionCallback), Nothing)
End Sub

 Live Messengerでメッセージを受信した場合のデータ形式は次のようになります。vbCrLfは\r\nで表現してます。

MSG jz5@xxx MATSUE%20Yusuke 139\r\n
MIME-Version: 1.0\r\n
Content-Type: text/plain; charset=UTF-8\r\n
X-MMS-IM-Format: FN=MS%20UI%20Gothic; EF=; CO=0; CS=80; PF=0\r\n
\r\n
こんにちは

 1行目は、MSG、メールアドレス、表示名(URLエンコード形式)、データ長、\n\rとなります。データ長は、その直後の\n\rの次から始まるデータの長さを表しているみたい。表示名はURLエンコードされてますが、UTF-8でもエンコードされてると思う。スペースなどが%20になってるのかな? MSGで始まるデータは、メッセージを入力中を示すメッセージにも使用されています。その場合はContent-Typeがtext/x-msmsgscontrolになります。他にも使用されているかも。

 以上を踏まえて、Parseメソッドの内容。BufferはIPヘッダ+データの構成になってます。最低限の部分だけ調べてます。Live Messengerのメッセージのやりとりには通常1863番のポートが使用されているのでそのポートが指定されているか確認します。正しくはデータ内のcharset部分を確認する必要があるでしょうが、一律 UTF-8でエンコードしてます。メッセージ部分は\r\n\r\nの次から最後までとして取得しています。

Private Sub Parse(ByVal buffer() As Byte, ByVal size As Integer)

    Const tcpHeaderLength As Integer = 20
    Dim ipHeaderLength As Integer = (buffer(0) And &HF) * 4
    Dim totalLength As Integer = buffer(2) * 256 + buffer(3)
    Dim protocol = buffer(9)

    ' プロトコル&データ長チェック
    Const Tcp As Integer = 6
    If protocol <> Tcp OrElse _
       totalLength <= ipHeaderLength + tcpHeaderLength Then
        Exit Sub
    End If

    ' ポート番号チェック
    Const MessengerPort As Integer = 1863
    Dim sourcePort = buffer(20) * 256 + buffer(21)
    If sourcePort <> MessengerPort Then
        Exit Sub
    End If

    ' UTF-8デコード MSGから始まるかチェック
    Dim message = System.Text.Encoding.UTF8.GetString(buffer, ipHeaderLength + tcpHeaderLength, totalLength - ipHeaderLength - tcpHeaderLength)
    If Not message.StartsWith("MSG ") Then
        Exit Sub
    End If

    ' 表示名部分取得
    Dim match As RegularExpressions.Match
    Dim nick As String = Nothing
    match = Regex.Match(message, "^MSG\s(.+?)\s(?<nick>.+?)\s(\d+)\s*$", RegexOptions.Multiline)
    If match.Success Then
        nick = Uri.UnescapeDataString(match.Groups("nick").Value)
    Else
        Exit Sub
    End If

    ' Content-Type部分取得、text/plainの場合メッセージ表示
    match = Regex.Match(message, "^Content-Type:\s*(?<type>.+?);", RegexOptions.Multiline)
    If match.Success AndAlso match.Groups("type").Value = "text/plain" Then
        Dim p = message.IndexOf(vbCrLf & vbCrLf)
        If p > 0 Then
            Console.WriteLine("{0}: {1}", nick, message.Substring(p + 4))
        End If
    End If
End Sub

 最後のパケットを取得するIPアドレスを選択する部分を書きます。コンソールプログラムです。IPアドレスが羅列して、番号を入力するようにしました。

Sub Main()
    Dim addressList = Dns.GetHostEntry(Dns.GetHostName).AddressList
    For i = 0 To addressList.Count - 1
        Console.WriteLine("{0}: {1}", i, addressList(i))
    Next
    Console.Write("Input no > ")
    Dim input = Console.ReadLine

    IpAddress = addressList(CInt(input))

    BeginReceive()
    Console.ReadLine()
End Sub

 以上で完成です(?)。動かすと次のような感じ。パケット盗み取っているので、管理者権限で実行する必要があります。シェイクやエモーションはキャプチャしません。

実行結果

 動くんですが、終了処理がないです。きちんとする場合は、コールバックメソッド内で終了条件時には、次の受信をしないようにして終わる必要があります。

フィードバック

# SNzRTMcHlLA

2013/03/22 23:46 by http://crork.com/
BBH8Ec wow, awesome article.Much thanks again. Much obliged.

# udvtLZZSiT

2018/12/17 8:17 by https://www.suba.me/
WsEkIS Very goodd article. I aam dealing with a feew of thesse issuss as well..
コメントの入力
タイトル
名前
Url
コメント