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中的两个函数,以近乎黑盒的形式研究这些函数。