• [工控安全][原创]SIEMENS SIMATIC STEP7软件中关键DLL文件分析(一)


    mailto:wangkai0351@gmail.com
    【未经同意禁止转载】

    赛门铁克的震网STUXNET病毒分析报告中声称,

    震网病毒是替换掉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_open

    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)
    ",hmod);
    
        SCP_open=GetProcAddress(hmod,"SCP_open");
        if (daveDebug & daveDebugOpen)
        	LOG2("GetProcAddress returned %p)
    ",SCP_open);
    
        SCP_close=GetProcAddress(hmod,"SCP_close");
        if (daveDebug & daveDebugOpen)
    	LOG2("GetProcAddress returned %p)
    ",SCP_close);
    
        SCP_get_errno=GetProcAddress(hmod,"SCP_get_errno");
        if (daveDebug & daveDebugOpen)
    	LOG2("GetProcAddress returned %p)
    ",SCP_get_errno);
    
        SCP_send=GetProcAddress(hmod,"SCP_send");
        if (daveDebug & daveDebugOpen)
    	LOG2("GetProcAddress returned %p)
    ",SCP_send);
    
        SCP_receive=GetProcAddress(hmod,"SCP_receive");
        if (daveDebug & daveDebugOpen)
    	LOG2("GetProcAddress returned %p)
    ",SCP_receive);
        
        SetSinecHWnd=GetProcAddress(hmod,"SetSinecHWnd");
        if (daveDebug & daveDebugOpen)
    	LOG2("GetProcAddress returned %p)
    ",SetSinecHWnd);
    
        en=SCP_get_errno();
        h=SCP_open(accessPoint);
        en=SCP_get_errno();
        LOG3("handle: %d  error:%d
    ", 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_sendSCP_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中的两个函数,以近乎黑盒的形式研究这些函数。

  • 相关阅读:
    mobileSelect学习
    使用qrcode生成二维码
    点点点右边有内容
    搜索框search
    input样式和修改
    art-template模板引擎高级使用
    Nodejs中的路径问题
    异步编程(回调函数,promise)
    在nodejs中操作数据库(MongoDB和MySQL为例)
    MongoDB数据库
  • 原文地址:https://www.cnblogs.com/bianmu-dadan/p/11064994.html
Copyright © 2020-2023  润新知