mailto:wangkai0351@gmail.com
【未經贊成禁止轉載】git
賽門鐵克的震網STUXNET病毒分析報告中聲稱,github
震網病毒是替換掉Step7軟件中
S7OTBXDX.DLL
動態連接庫文件,該DLL文件包含了一些對PLC編程和調試有關鍵功能的函數,好比編程
- s7db_open and s7db_close
- s7ag_read_szl
- s7_event
- s7ag_test
- s7ag_link_in
- s7ag_bub_cycl_read_create
- s7ag_bub_read_var
- s7ag_bub_write_var
- s7ag_bub_read_var_seg
- s7ag_bub_write_var_seg
我programm files/system
路徑下找到了這個文件,可是Step7軟件的用戶協議中禁止了對其進行逆向分析數組
被許可方無權對軟件進行修改、反編譯或逆向工程。並且也不能提取任何單獨的部分,除非強制的版權法容許。網絡
這樣,咱們沒有權利反編譯它了。可是有權調試它?函數
**(諮詢西門子中國工業客戶服務中心,得知包括使用ollydbg和windbg在內的工具對工程軟件後臺運行的探測行爲,屬於*《用戶協議》中"逆向工程」範疇)**工具
我仍是從網絡上找到了一些蛛絲馬跡,好比著名的逆向分析S7comm
協議的成果——LIBNODAVE開源軟件(github地址)中調用了Step7的另外一個DLLS7ONLINX.DLL
。指針
LIBNODAVE
調用了S7ONLINX.DLL
其中的幾個函數(或者稱它們爲SCP_
函數簇,經過串口走S7comm on MPI
協議)調試
SCP_opencode
SCP_close
SCP_get_errno
SCP_send
SCP_receive
SetSinecHWnd
具體的調用方式的代碼以下
EXPORTSPEC HANDLE DECL2 openS7online(const char * accessPoint, HWND handle) { HMODULE hmod; int h,en; _setHWnd SetSinecHWnd; hmod=LoadLibrary("S7onlinx.dll"); if (daveDebug & daveDebugOpen) LOG2("LoadLibrary(S7onlinx.dll) returned %p)\n",hmod); SCP_open=GetProcAddress(hmod,"SCP_open"); if (daveDebug & daveDebugOpen) LOG2("GetProcAddress returned %p)\n",SCP_open); SCP_close=GetProcAddress(hmod,"SCP_close"); if (daveDebug & daveDebugOpen) LOG2("GetProcAddress returned %p)\n",SCP_close); SCP_get_errno=GetProcAddress(hmod,"SCP_get_errno"); if (daveDebug & daveDebugOpen) LOG2("GetProcAddress returned %p)\n",SCP_get_errno); SCP_send=GetProcAddress(hmod,"SCP_send"); if (daveDebug & daveDebugOpen) LOG2("GetProcAddress returned %p)\n",SCP_send); SCP_receive=GetProcAddress(hmod,"SCP_receive"); if (daveDebug & daveDebugOpen) LOG2("GetProcAddress returned %p)\n",SCP_receive); SetSinecHWnd=GetProcAddress(hmod,"SetSinecHWnd"); if (daveDebug & daveDebugOpen) LOG2("GetProcAddress returned %p)\n",SetSinecHWnd); en=SCP_get_errno(); h=SCP_open(accessPoint); en=SCP_get_errno(); LOG3("handle: %d error:%d\n", h, en); SetSinecHWnd(h, handle); return h; }; EXPORTSPEC HANDLE DECL2 closeS7online(int h) { SCP_close(h); }
調用上述代碼中定義的openS7online
函數和closeS7online
函數的方式可參考testS7online.c
文件
fds.rfd=openS7online(argv[adrPos], GetConsoleHwnd());//第二個參數是綁定到一個窗口上 closeS7online(fds.rfd);
能夠看出,調用openS7online
函數只是把SCP_
函數簇加載到或者說注入到進程的內存空間中,像SCP_open就至關於這個函數在當前進程中的函數入口地址了,因此在main函數開頭解析完調輸入參數後就要調用,其第一個參數argv[adrPos]是一個字符串指針,表明access point,也就是上位機串口的接入點;第二個參數,是綁定到執行main函數當前的窗口。
當前,已經把SCP_
函數簇加載到內存空間中了,下面咱們接着看testS7online.c
文件是如何調用SCP_
函數簇經過串口發送報文數據的。咱們的目的是,瞭解SCP_
函數簇的輸入和輸出參數是什麼?
在nodave.c
文件中,經過分別包裝SCP_send
和SCP_receive
寫了兩個函數。
先看SCP_send
函數
//nodave.c int DECL2 _daveSCP_send(int fd, uc * reqBlock) { S7OexchangeBlock* fdr; fdr=(S7OexchangeBlock*)reqBlock; fdr->headerlength=80; fdr->allways2 = 2; fdr->payloadStart= 80; return SCP_send(fd, fdr->payloadLength+80, reqBlock); }
能夠看到給SCP_send
函數傳遞了三個參數,分別是
類型 | 說明(本身胡猜) | |
---|---|---|
int | 發送報文使用串口的地址 | |
uc/unsigned char | 發送報文長度,具體報文的結構定義還不肯定 | |
uc/unsigned char | 發送報文數組的其實地址 |
函數返回類型是int,本身胡猜,多是函數執行成功否?
再看SCP_receive
函數
//nodave.c int daveSCP_receive(int h, uc * buffer) { int res, datalen; S7OexchangeBlock * fdr; fdr=(S7OexchangeBlock*) buffer; res=SCP_receive(h, 0xFFFF, &datalen, sizeof(S7OexchangeBlock), buffer); if (daveDebug & daveDebugByte) { _daveDump("header:",buffer, 80); _daveDump("data:",buffer+80, fdr->payloadLength); _daveDump("data:",buffer+80, fdr->payloadLength); } return res; }
能夠看到給SCP_receive
函數傳遞了五個參數,分別是
類型 | 說明(本身胡猜) | |
---|---|---|
int | 接收報文使用串口的地址(一般狀況下和發送報文用一個串口?) | |
int | 0xFFFF定值? | |
*int | 接收報文的長度 | |
int | S7OexchangeBlock是s7onlinx.dl和其調用者之間交換數據塊的結構體 | |
uc/unsigned char | 接收報文數據的起始地址 |
返回參數是int,本身胡猜,多是函數執行成功否?
至今,咱們以LIBNODAVE
開源軟件瞭解了STEP7的一個DLL文件中兩個關鍵函數的接口,以及研究者本身如何調用該DLL中的兩個函數,以近乎黑盒的形式研究這些函數。