[工控安全][原創]SIEMENS SIMATIC STEP7軟件中關鍵DLL文件分析(二)

mailto:wangkai0351@gmail.com
【未經贊成禁止轉載】git

由於軟件著做權和軟件用戶協議的限制,咱們沒有辦法對STEP7軟件的關鍵DLL文件進行逆向工程,或者動態調試,這可真是一個頭疼的問題。github

咱們能夠考慮另外一條更爲狹窄的逆向之路了——藉助震網病毒的研究成果。編程

賽門鐵克的震網STUXNET病毒分析報告中聲稱,函數

震網病毒是替換掉Step7軟件中S7OTBXDX.DLL動態連接庫文件,劫持掉該DLL文件包含了一些對PLC編程和調試有關鍵功能的函數,完整列出ui

  • s7db_open
  • s7db_close
  • s7blk_read
  • s7blk_write
  • s7ag_link_in
  • s7blk_findnext
  • s7blk_findfirst
  • s7blk_delete
  • s7ag_read_szl
  • s7_event
  • s7ag_test
  • s7ag_stop
  • s7ag_bub_cycl_read_create
  • s7ag_bub_read_var
  • s7ag_bub_write_var
  • s7ag_bub_read_var_seg
  • s7ag_bub_write_var_seg

github上有項目公開了一部分震網病毒STUXNET的反編譯結果,震網病毒和咱們關心的s7otbxdx.dll關係很大,因此咱們嘗試從震網病毒樣本中瞭解s7otbxdx.dll的內部構造。this

https://github.com/Laurelai/decompile-dump調試

咱們觀察到項目中789F6F8DE3F140CF5D73BEF0B8ABAF78.c文件中,確實列出了一些咱們上面羅列的s7簇函數。code

毫無疑問,使用STEP7軟件中的某些功能,就會觸發這些函數,上位機經過s7comm協議和PLC交換狀態和數據。orm

我抽一個s7ag_read_szl函數/功能來細緻研究一下。對象

賽門鐵克的病毒分析報告《W32.Stuxnet Dossier》中聲稱

Used to query PLC information, through a combination of an ID and an index (it can be used for instance to get the PLC type.) The export modifies the API’s return information if it’s called with specific ID=27, index=0.

能夠看出來s7ag_read_szl函數的功能是讀取PLC的系統狀態列表(read system status list,status在德文中是以'z'開頭的,因此szl就沿用到了如今)。

可是賽門鐵克報告中說震網用了SZL-id=27/index=0,這個我是很難理解的。

按照《SIMATIC 用於S7-300/400系統和標準函數的系統軟件 卷1/2》和wireshark s7comm dissector的表述,我更傾向於震網用了SZL-id=0x1c/index=0用於識別PLC的系列是否是6es7 315-2。

下面是發送s7ag_read_szl(SZL-id=0x1c/index=0)後,PLC響應報文。

0000   ff 09 00 d6 00 1c 00 00 00 22 00 0a 00 01 53 37   ÿ..Ö....."....S7
0010   33 30 30 2f 45 54 32 30 30 4d 20 73 74 61 74 69   300/ET200M stati
0020   6f 6e 5f 31 00 00 00 00 00 00 00 00 00 00 00 02   on_1............
0030   50 4c 43 5f 31 00 00 00 00 00 00 00 00 00 00 00   PLC_1...........
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050   00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0060   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070   00 00 00 04 4f 72 69 67 69 6e 61 6c 20 53 69 65   ....Original Sie
0080   6d 65 6e 73 20 45 71 75 69 70 6d 65 6e 74 00 00   mens Equipment..
0090   00 00 00 00 00 05 53 20 43 2d 42 31 55 33 39 33   ......S C-B1U393
00a0   31 34 32 30 31 31 00 00 00 00 00 00 00 00 00 00   142011..........
00b0   00 00 00 00 00 00 00 07 43 50 55 20 33 31 35 2d   ........CPU 315-
00c0   32 20 50 4e 2f 44 50 00 00 00 00 00 00 00 00 00   2 PN/DP.........
00d0   00 00 00 00 00 00 00 00 00 08                     ..........

三重證據證實了,賽門鐵克報告中的描述可能有錯,除非SZL-id=27/index=0是一個隱藏的功能(我未了解)。

下面,咱們嘗試剖開s7ag_read_szl函數內部看看(相比s7ag*函數簇中的其餘函數,這個函數是最簡單的)。

int __cdecl s7ag_read_szl()
{
  int v1; // eax@1

  v1 = sub_1000BE40((int)sub_100026EA);
  return ((int (*)(void))v1)();
}

sub_1000BE40函數的功能是,把這個函數劫持到sub_100026EA函數處。

void __stdcall sub_100026EA(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
{
  int v8; // eax@1
  char v9; // [sp+0h] [bp-28h]@1
  int v10; // [sp+10h] [bp-18h]@1
  char v11; // [sp+14h] [bp-14h]@1
  char *v12; // [sp+18h] [bp-10h]@1
  int v13; // [sp+24h] [bp-4h]@1

  v12 = &v9;
  sub_1000F9F2();
  v13 = 0;
  v8 = sub_1000D2B6(a1, a2, a3, a4, a5, a6, a7, a8);
  unknown_libname_13(v8);
  LOBYTE(v13) = 1;
  v10 = sub_100034DB(&v11);
  LOBYTE(v13) = 0;
  sub_100034F7((int *)&v11);
  JUMPOUT(*(unsigned int *)loc_10002758);//函數執行最後,調回loc_10002758,猜想這是s70tbxdx.dll中s7ag_read_szl函數的真實內存地址
}

sub_100026EA函數傳入了a1~a8一共八個參數,這就有點意思了,由於調用它的s7ag_read_szl一個參數都沒有。咱們有理由猜想:這八個參數中,必定有表示SZL-id/index的兩個參數。到底是那兩個呢,咱們近距離觀察sub_1000D2B6函數,由於八個參數又所有傳到了這個函數裏。

int __cdecl sub_1000D2B6(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
{
  int result; // eax@1
  int v9; // ecx@1

  v9 = (int)operator new(0x40u);
  result = 0;
  if ( v9 )
    result = sub_1000906E(v9, a1, a2, a3, a4, a5, a6, a7, a8);
  return result;
}

這個函數先new分配了一塊內存,int v9[64],把首地址v9連帶八個參數又傳入了sub_1000906E函數。

int __thiscall sub_1000906E(int this, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9)
{
  int v10; // esi@1

  v10 = this;
  *(_DWORD *)this = &off_1001C428;//還出如今s7blk_read函數中 對象的函數成員 構造函數?
  *(_DWORD *)(this + 40) = a5;
  *(_DWORD *)(this + 44) = a6;
  *(_DWORD *)(this + 48) = a7;
  *(_DWORD *)(this + 52) = a8;
  *(_DWORD *)(this + 32) = a3;
  *(_DWORD *)this = &off_100211D8;//對象的函數成員 析構函數?
  *(_DWORD *)(this + 28) = a2;
  *(_DWORD *)(this + 36) = a4;
  *(_DWORD *)(this + 56) = a9;
  sub_10003B6C(this + 4, a2, a3, a4);
  return v10;
}

真的不容易,終於看到s7ag_read_szl這個函數是怎麼樣構造報文了,可是函數最後又調用了sub_10003B6C。

int __thiscall sub_10003B6C(int this, int a2, int a3, int a4)
{
  int result; // eax@1

  *(_DWORD *)this = a2;
  *(_DWORD *)(this + 4) = a3;
  result = a4;
  *(_DWORD *)(this + 8) = a4;
  return result;
}

咱們把sub_1000906E函數和sub_10003B6C函數,這一連串的結構體/對象構造過程看一下,很明顯sub_10003B6C說明this+4這裏又有一個小結構體/對象,屬於has a關係,有多是繼承?

其實,我犯了一個錯誤,我忘記了如今只是在分析震網病毒,這裏只是構造了一個結構體/對象,並非最終報文的形式。這個結構體是s7otbxdx.dll與調用dll的進程交換數據的一種數據格式,咱們簡單把它叫作S7otbxdx_exchangeBlock。只能大膽猜想,S7otbxdx_exchangeBlock和報文格式有很大關聯度,可是不能說二者就是一一對應的關係。

下面,咱們嘗試逆向分析這個S7otbxdx_exchangeBlock的結構體/對象。

首先,S7otbxdx_exchangeBlock的空間是由new創造的,那麼先由new的大小,統計一下,有幾種S7otbxdx_exchangeBlock。由於震網病毒只劫持了十幾個函數,因此咱們只能從這些函數中一窺究竟。

sizeof(S7otbxdx_exchangeBlock) 相關函數 基類
0x40u s7ag_read_szl sub_10003B6C(this + 4, a2, a3, a4)
s7ag_bub_read_var sub_10003B83(this + 4, a2, a3)
s7ag_bub_write_var sub_10003B83(this + 4, a2, a3)
0x48u s7blk_read sub_10003B6C(this + 4, a2, a3, a4)
s7blk_findfirst sub_10003B6C(this + 4, a2, a3, a4)
s7blk_findnext sub_10003B6C(this + 4, a2, a3, a4)
s7_event sub_10003B6C(this + 4, a2, a3, a4)
0x4cu s7blk_delete sub_10003B6C(this + 4, a2, a3, a4)
0x38u s7blk_write sub_10003B6C(v10 + 4, *(_DWORD )(v10 + 28), (_DWORD )(v10 + 32), (_DWORD *)(v10 + 36));
0x30u s7ag_link_in sub_10003B6C(this + 4, a2, a3, a4)
0x44u s7ag_bub_read_var_seg sub_10003B83(this + 4, a2, a3)
s7ag_bub_write_var_seg sub_10003B83(this + 4, a2, a3)
0x50u S7ag_bub_cycl_read_create sub_10003B83(this + 4, a2, a3)
0x70u S7ag_test sub_10003B83(this + 4, a2, a3)

其實我猜想,這些對象包含的基類頗有多是它們對應報文的參數段(parameter)的一些字段。

爲何這麼說呢?之後咱們從報文的特徵來對應一下。

這樣,咱們簡單認識了震網病毒樣本中的s7ag_read_szl函數,固然咱們假設STEP7 s7otbxdx.dll中的s7ag_read_szl函數也長這個樣子。

至於事實上究竟如何?不讓反編譯和動態調試s7otbxdx.dll,也沒辦法瞭解。