精华内容
下载资源
问答
  • 题目用MATLAffi行控制系统的动态性能分析 2.7初始条件已知三阶系统闭环传递函数为 2.7 G(s) 1 2 (s 1(s 0.8s 0.64) a 分析系统的动态性能 要求完成主要任务:包括课程设计工作量及其技术要求以及说明书撰写等...
  • PAGE 用MATLAB进行控制系统的动态性能分析 初始条件已知三阶系统闭环传递函数为 分析系统的动态性能 要求完成主要任务: 包括课程设计工作量及其技术要求以及说明书撰写等具体要求 用MATLAB函数编程求系统...
  • Hubtel.UssdFramework允许您在我们上轻松构建动态USSD应用程序。 主要特征 自动化会话管理。 完全异步。 可插拔会话存储。 包括RedisStore 。 ASP.NET MVC / Web API控制器。 动态路由。 控制器中...
  • 摘 要:本文提出在PC 机远程控制下利用一片FLASH 存储器实现多DSP 系统对多份用户代码加载并有选择重新载入用户程序的方法。在分析了DSP 自举加载步骤之后给出了系统硬件电路框图,并结合TI 公司DSP 芯片TMS...
  • FFdynamic-通过运行时控制动态音频/视频合成来扩展FFmpeg该项目包括两个部分:FFdynamic库和基于FFdynamic内容构建应用程序交互式FFdynamic-通过运行时控制动态音频/视频合成来扩展FFmpeg两个部分:FFdynamic...
  • Toolkit是用于自动控制动态优化软件环境和算法集合。 它提供了一个通用框架,可用于将各种算法用于直接最优控制包括模型预测控制,状态和参数估计以及鲁棒优化。 ACADO Toolkit被实现为独立C ++代码,并带有...
  • int ret,j,i;DictTable dictTable;DictField DictField;str curFieldName;;dictTable = new DictTable(tableNum(yourTableName));...//这里系统会包括其他11个父类字段,所以减去11for (j=1;j{ DictField=n
    int ret,j,i;
    DictTable dictTable;
    DictField DictField;
    str curFieldName;
    ;
    dictTable = new DictTable(tableNum(yourTableName));
    i=dictTable.fieldCnt()-11;//这里系统会包括表的其他11个父类字段,所以减去11
    for (j=1;j<=i;j++)
    {
        DictField=new DictField(dictTable.id(),dictTable.fieldCnt2Id(j));
        curFieldName=DictField.name();
        if(curFieldName!='Fieldname')
            yourTableName_ds.object(DictField.id()).allowEdit(false);
        else
            yourTableName_ds.object(DictField.id()).allowEdit(true);
    }
    展开全文
  • 提出了WindowsNT系.统下实时动态模拟环境中...各个任务中应用程序通过一些简单函数调用实现对本任务或其他任务运行状态的控制控制任务包括挂起、唤醒、终止、改变优先级等,该方法简便、实用高效,动态性好。
  • 动态加载驱动程序源代码

    热门讨论 2011-06-14 19:45:49
    源代码包含三种不同驱动加载方法,使用ZwSetSystemInformation函数加载驱动,使用NtLoadDriver函数加载驱动,使用服务控制管理器加载驱动,还包括三种线程注入技术,使用RtlCreateUserThread 函数注入线程,使用...
  • deckxstream是的控制器应用程序。 创建该应用程序是为了允许Linux使用Stream Deck。 该应用程序严重依赖于 NPM库。 您将需要安装udev规则和相关性才能进行安装。 见下文 特征 支持多种图像格式,包括PNG,SVG和动画...
  • 用于创建动态对象外观/代理实用程序,以允许将对象用作未明确实现接口 描述 我确信我们一直都处于不得不使用外部库(包括mscorlib)中情况,该类要么未实现任何接口,要么未实现可用于以下目的接口:...
  • 用法 这些dotfile是通过管理,因此您需要安装它。 要安装这些点文件,包含一个安装程序脚本。... :用于控制从MPD客户端播放扩展。 :用于从SoundCloud播放音乐扩展。 :用于扩展播放到Last.f
  • 控制屏幕保护程序API函数的C++类封装by 郭世龙简 介 前几天在codeproject上看到了一篇关于用C#封装控制的屏幕保护程序的文章,觉的很有用于是决定将其改装成C++类封装以供C++程序员使用。这个类提供了查询屏幕保护...
    控制屏幕保护程序API函数的C++类封装
    by 郭世龙
    简 介
          前几天在codeproject上看到了一篇关于用C#封装控制的屏幕保护程序的文章,觉的很有用于是决定将其改装成C++类封装以供C++程序员使用。这个类提供了查询屏幕保护程序信息的函数,包括是否启用、是否正在运行、查询和设置屏保等待时间、强制关闭屏幕保护程序等函数。user32.dll 动态链接库提供的SystemParametersInfo()API函数(需要包含头文件Windows.h)提供了对屏幕保护程序控制的功能。
      
    函数介绍
    • bool GetScreenSaverActive( )                  —— 判断屏幕保护程序是否开启,如果开启则返回true否则返回false
    • void SetScreenSaverActive(int Active)    —— 传递参数1来开启屏幕保护程序, 传递0来关闭屏幕保护程序
    • int GetScreenSaverTimeout( )                  —— 返回当前屏幕保护程序等待时间设置,以秒位单位
    • void SetScreenSaverTimeout(int Value)    —— 设置屏幕保护程序等待时间,以秒位单位
    • bool GetScreenSaverRunning( )          —— 判断当前屏幕保护程序是否正在运行,是则返回ture否则返回false
    • void KillScreenSaver( )                          —— 强制结束屏幕保护
      
    封装代码
          使用时,拷贝代码到程序中,引入这个ScreenSaverCtrl类文件即可。
     
    头文件:
    //ScreenSaverCtrl.h
    #pragma once
    #include "Windows.h"
    /*   //静态链接
    #pragma comment(lib,"User32.lib")   //在link时,链接到User32.lib文件

    extern "C"
    {
       WINUSERAPI BOOL WINAPI SystemParametersInfoW(__in UINT uiAction,__in UINT uiParam,__inout_opt PVOID pvParam,__in UINT fWinIni);
      WINUSERAPI BOOL WINAPI SystemParametersInfoA(__in UINT uiAction,__in UINT uiParam,__inout_opt PVOID pvParam,__in UINT fWinIni);
      WINUSERAPI BOOL WINAPI PostMessageA(__in_opt HWND hWnd,__in UINT Msg, __in WPARAM wParam,__in LPARAM lParam);
      WINUSERAPI HDESK WINAPI OpenDesktopA(__in LPCSTR lpszDesktop,__in DWORD dwFlags,__in BOOL fInherit,__in ACCESS_MASK dwDesiredAccess);
      WINUSERAPI BOOL WINAPI CloseDesktop(__in HDESK hDesktop);
      WINUSERAPI BOOL WINAPI EnumDesktopWindows( __in_opt HDESK hDesktop, __in WNDENUMPROC lpfn, __in LPARAM lParam);
      WINUSERAPI BOOL WINAPI IsWindowVisible(__in HWND hWnd);
      WINUSERAPI HWND WINAPI GetForegroundWindow(VOID);
      WINUSERAPI BOOL WINAPI EnumDesktopWindows(__in_opt HDESK hDesktop,__in WNDENUMPROC lpfn,__in LPARAM lParam);
     
    }
    */
    //BOOL CALLBACK KillScreenSaverFunc(HWND hWnd,LPARAM lParam);
    class ScreenSaverCtrl
    {
     public:
      ScreenSaverCtrl(void);
      static bool GetScreenSaverActive();
      static void SetScreenSaverActive(int Active);
      static int GetScreenSaverTimeout();
      static void SetScreenSaverTimeout(int Value);
      static bool GetScreenSaverRunning();
      static void KillScreenSaver();
     private:
      static BOOL CALLBACK KillScreenSaverFunc(HWND hWnd,LPARAM lParam);
      
     public:
      ~ScreenSaverCtrl(void);
    };
     
    实现文件:
     
    //ScreenSaverCtrl.cpp
    #include "ScreenSaverCtrl.h"
     
    /*
    BOOL CALLBACK KillScreenSaverFunc(HWND hWnd,LPARAM lParam)    //注意回调函数不能为普通成员函数,但可为静态成员函数
    {
      if( IsWindowVisible(hWnd) ) PostMessage( hWnd, WM_CLOSE, 0, 0 );
      return true;
    }
    */
     
    ScreenSaverCtrl::ScreenSaverCtrl(void
    {
     
    }
     
    bool ScreenSaverCtrl::GetScreenSaverActive( )
    {
      BOOL isActive = FALSE;  
    //一定是BOOL而不是bool否则运行时错误
      SystemParametersInfo(SPI_GETSCREENSAVEACTIVE,0,&isActive, 0);
      return isActive;
    }
     
    void ScreenSaverCtrl::SetScreenSaverActive( int Active )
    {
      int nullVar = 0;
      SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,Active, &nullVar, SPIF_SENDWININICHANGE );
    }
     
    int ScreenSaverCtrl::GetScreenSaverTimeout( )
    {
      int value = 0;
      SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0,&value, 0 );
      return value;
    }
      
    void ScreenSaverCtrl::SetScreenSaverTimeout(int Value )
    {
      int nullVar = 0;
      SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT,Value, &nullVar,SPIF_SENDWININICHANGE );
    }
      
    bool ScreenSaverCtrl::GetScreenSaverRunning( )
    {
      BOOL isRunning = FALSE;
      SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0,&isRunning, 0 );
      return isRunning;
    }
      
    void ScreenSaverCtrl:: KillScreenSaver( )
    {
      HDESK hDesktop = OpenDesktop(TEXT("Screen-saver"), 0,false,DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
      if( hDesktop != NULL)
      {
         EnumDesktopWindows(hDesktop,KillScreenSaverFunc,0);
         CloseDesktop( hDesktop );
      }
      else
      {
         PostMessage( GetForegroundWindow( ), WM_CLOSE, 0, 0 );
      }
    }
     
     //注意回调函数不能为非静态成员函数,因为其有隐含参数this 而静态成员函数没有this指针
    BOOL ScreenSaverCtrl::KillScreenSaverFunc(HWND hWnd,LPARAM lParam)   
    {
      if( IsWindowVisible(hWnd) ) PostMessage( hWnd, WM_CLOSE, 0, 0 );
      return true;
    }
     
    ScreenSaverCtrl::~ScreenSaverCtrl(void)
    {

    }
     
    测试程序
     
    编译运行环境
                   操作系统:Windows XP SP2
                   编译器:VS2005
     
    测试程序界面
     测试程序界面
    测试程序下载
     
    展开全文
  • 用于控制VLC媒体播放器手势识别应用程序 该项目旨在通过使用动态手势作为输入来控制vlc媒体播放器。 Vlc与手势相关各种功能包括播放/暂停,停止,增大音量,减小音量和更改纵横比(裁剪)。 源代码内容: ...
  • 帮老师5岁儿子写一个交换礼物程序,学了一下午把大致东西...直接给出我代码,有点小乱,一下午乱七八糟学习,哈哈哈,这里面包括动态改变Label中Test,test中字体设置,botton中字体设置,以及

    Step1 导入tkinter模块

    在控制面版(不知道的话windows 键+ R 键)输入:

     pip install tkinter-i https://pypi.tuna.tsinghua.edu.cn/simple

    Step2 我的代码

    直接给出我的代码,有点小乱,一下午的乱七八糟的学习,哈哈哈,这里面包括了动态改变Label中的Test,test中的字体设置,botton中字体的设置,以及控件的位置等等,我刚开始也遇到一些问题,就是写出来的界面不居中,这个是需要调节的,所以我也有把生成的界面放在屏幕的正中间,还有就是按钮的命令的设置等等。基本内容就是这么多,需要什么在程序中找即可:

    import tkinter as tk
    from PIL import ImageTk,Image
    import random
    #初始化Tk()
    global myWindow
    myWindow = tk.Tk()
    #设置标题
    myWindow.title('Gift Exchanging')
    #设置窗口大小
    width = 800
    height = 500
    
    global I
    I=[]
    num = list(range(1, 55))
    for i in range(27):
        pair1 = random.choice(num)
        num.remove(pair1)
        pair2 = random.choice(num)
        num.remove(pair2)
        pair = (pair1, pair2)
        I.append(pair)
    
    
    def create():
        myWindow.destroy()
        global myWindow1
        myWindow1 = tk.Tk()
        # 设置标题
        myWindow1.title('Gift Exchanging_1')
        # 设置窗口大小
        width = 800
        height = 500
        screenwidth = myWindow1.winfo_screenwidth()
        screenheight = myWindow1.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        myWindow1.geometry(alignstr)
        text = tk.StringVar()
        text.set(I[0])
        status = tk.IntVar()
        def pair():
            I.remove(I[0])
            text.set(I[0])
            # if I[0] != None:
            #     L=tk.Text(myWindow1, text=I[0], fg='orange', width=50, height=20, font=('黑体',10)).pack()
            #     I.remove(I[0])
        b1 = tk.Button(myWindow1, text='Pair Begin', command=pair,bg="green", font=('Helvetica 10 bold'), relief='raised',
                       width=20,
                       height=3)
        L = tk.Label(myWindow1, textvariable=text, fg='orange', width=50, height=20, font=('黑体', 180))
        b1.pack(side='top')
        L.pack()
        myWindow1.mainloop()
    
    
    #设置窗口是否可变长、宽,True:可变,False:不可变
    myWindow.resizable(width=False, height=False)
    #获取屏幕尺寸以计算布局参数,使窗口居屏幕中央
    
    image2=Image.open(r'New_year.JPG')
    
    background_image = ImageTk.PhotoImage(image2)
    w = background_image.width()+200
    h = background_image.height()
    myWindow.geometry('%dx%d+0+0' % (w,h))
    background_label = tk.Label(myWindow, image=background_image)
    background_label.place(x=0, y=0, relwidth=1, relheight=1)
    #创建两个按钮
    b1=tk.Button(myWindow, text='Quit',bg="yellow",font=('Helvetica 10 bold'), relief='raised', width=20, height=3)
    b1.pack(side='bottom',expand='y',)
    b2=tk.Button(myWindow, text='Begin ', bg="orange",command=create,relief='raised',font=('Helvetica 10 bold'),width=20, height=3)
    b2.pack(side='bottom',expand='y')
    
    screenwidth = myWindow.winfo_screenwidth()
    screenheight = myWindow.winfo_screenheight()
    alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2)
    myWindow.geometry(alignstr)
    #进入消息循环
    myWindow.mainloop()

    step3 结果

    有两个botton,点击Begin即立马进入Pair过程,弹出第二个界面。

    在第二个界面,点击Pair Begin,就开始做他们班上的52个同学两两随机配对,点一次配对一次,不重复配对。 

    step4 打包

    1、导入pyinstaller模块和pywin32模块,导入方式上面已讲。

    2、找到所写文件所在目录(图片等放在同一个目录)

    在控制面版输入所在目录的路径,如我的所在目录:

    则输入:

    点击回车,输入dir,就可在空面版看到你所在程序目录,选择你要打包的文件,输入

    pyinstaller -F +你要打包的py文件

    如:

    成功就会显示(画红线处为生成的exe所在路径):

    完工!

    注:一定要把用到过的图片等复制和exe应用程序一个文件夹里面,不然运行不出来。

     

    展开全文
  • 该方法统一并为所有系统资源提供基本命名,结构和访问控制机制。 单个文件服务协议(与Plan 99P相同)使所有这些资源都可以以统一方式在整个网络中导入或导出,而不受位置影响。 应用程序只需将所需资源...
  • 如何控制程序的动态布局 检查地址范围 3.1 可执行文件里有什么 对于这个问题,相信有经验开发人员都能给出这样回答:可执行文件包含了源文件编译生成可执行指令和数据。在日常开发中,理解到这个程度已经...

    本文将探讨程序的静态布局和动态布局。静态布局指可执行文件在硬盘上的内部布局,而动态布局,则是程序被系统加载到内存之后的布局。主要包括以下几部分内容:

    • 可执行文件里有什么
    • 影响程序静态布局的因素
    • 如何控制程序的动态布局
    • 检查地址范围

    3.1 可执行文件里有什么

    对于这个问题,相信有经验的开发人员都能给出这样的回答:可执行文件包含了源文件编译生成的可执行指令和数据。在日常开发中,理解到这个程度已经足够,但本文会做更深入的探究,以帮助读者扩展在某些方向上的技术基础,比如黑客技术。

    在 Linux 中,二进制可执行文件的标准格式叫做 ELF(Executable and Linkable Format)。从名字可以看出,它同时兼容可执行文件和可链接文件。本文重点关注可执行文件,可链接文件将在后面课程中讨论动态链接库时再深入讲解。

    3.1.1 ELF 文件的两大组成部分

    概括地讲,一个 ELF 文件包含一个固定长度的文件头和多个可扩展的数据块。其中,文件头是整个可执行文件的总地图,描述了整个文件的组织结构。可扩展数据块分为两类,对应着不同的视图——在链接视图下,数据块的单位是节(Section),用多个节区头索引所有内容;而在执行视图下,数据块的单位是段(Segment),用程序头(Program Header)索引所有的段。如下图所示:

    9d25ec40-149d-11e9-a7eb-e97b85bf6ba0

    图 1 ELF 文件在不同视图下的结构

    我们用日常生活中一个更直观的例子做下类比,帮大家加深理解。城市里满大街的车,从用户视角来看,可以分成家用车、商务车、警车、工程车、公交车等。而在汽车动力工程师眼里,就会被分成汽油车、柴油车和电动车。这两类人的不同视角就好比 ELF 文件的不同视图,从不同视角对车的分类就好比 ELF 文件中的节和段。

    3.1.2 组成部分之文件头

    下面用一个实际程序近距离观察下 ELF 的文件头。示例代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    
    static char static_data[16] = "I'm Static Data";
    static char raw_static_data[40960];
    static const char const_data[16] = "I'm Const Data";
    
    int main(int args,char ** argv)
    {
        printf("Message In Main\n");
    
        return 0;
    }
    

    通过 gcc -o elfdemo elfdemo.c 命令编译链接该程序,接着使用 readelf -h 命令查看生成的可执行文件的文件头,可看到如下信息:

    3d9fb4d0-0b1b-11e9-9130-41b9fd18af28

    图 2 可执行文件 ELF 头信息

    我们对图中信息做下解读。

    • 生成的可执行文件的大小是 6836 字节,文件格式是 64 位的 Linux 标准可执行文件(ELF 64-bit LSB Executable),目标平台是 x86-64。
    • 程序的入口地址是 0x4003c0,该文件的 ELF 头的大小是 64 字节。
    • 文件中有 8 个 Program Header,每个 Program Header 的长度是 56 字节 ,信息存放在从文件头算起 64 字节的位置。
    • 另外还有 29 个 Section Header,每个 Section Header 的大小是 64 字节,信息位于从文件头算起 2632 字节的位置。

    根据这些信息,就可以依次定位到每个 Header,再根据 Header 中对每个段的描述,便可解析出文件内容。

    3.1.3 组成部分之程序头和节区头

    使用 readelf 命令的 -S 和 -l 选项可分别列出程序头和节区头的内容,结果如下(如果你是第一次接触这些内容,请先关注输出信息的总体结构,不要陷入到输出列表的信息细节中去):

    [root@localhost elf]# readelf -S elfdemo
    There are 29 section headers, starting at offset 0xa48:
    
    Section Headers:
      [Nr] Name          Type       Address           Offset     Size              EntSize          Flags  Link  Info  Align
      [ 0]               NULL       0000000000000000  00000000   0000000000000000  0000000000000000           0     0     0
      [ 1] .interp       PROGBITS   0000000000400200  00000200   000000000000001c  0000000000000000   A       0     0     1
      [ 2] .note.ABI-tag NOTE       000000000040021c  0000021c   0000000000000020  0000000000000000   A       0     0     4
      [ 3] .hash         HASH       0000000000400240  00000240   0000000000000024  0000000000000004   A       4     0     8
      [ 4] .dynsym       DYNSYM     0000000000400268  00000268   0000000000000060  0000000000000018   A       5     1     8
      [ 5] .dynstr       STRTAB     00000000004002c8  000002c8   000000000000003d  0000000000000000   A       0     0     1
      [ 6] .gnu.version  VERSYM     0000000000400306  00000306   0000000000000008  0000000000000002   A       4     0     2
      [ 7] .gnu.version_r VERNEED   0000000000400310  00000310   0000000000000020  0000000000000000   A       5     1     8
      [ 8] .rela.dyn     RELA       0000000000400330  00000330   0000000000000018  0000000000000018   A       4     0     8
      [ 9] .rela.plt     RELA       0000000000400348  00000348   0000000000000030  0000000000000018   A       4    11     8
      [10] .init        PROGBITS    0000000000400378  00000378   0000000000000018  0000000000000000  AX       0     0     4
      [11] .plt         PROGBITS    0000000000400390  00000390   0000000000000030  0000000000000010  AX       0     0     4
      [12] .text        PROGBITS    00000000004003c0  000003c0   0000000000000268  0000000000000000  AX       0     0     16
      [13] .fini        PROGBITS    0000000000400628  00000628   000000000000000e  0000000000000000  AX       0     0     4
      [14] .rodata      PROGBITS    0000000000400640  00000640   0000000000000030  0000000000000000   A       0     0     16
      [15] .eh_frame_hdr PROGBITS   0000000000400670  00000670   0000000000000024  0000000000000000   A       0     0     4
      [16] .eh_frame    PROGBITS    0000000000400698  00000698   000000000000007c  0000000000000000   A       0     0     8
      [17] .ctors       PROGBITS    0000000000600718  00000718   0000000000000010  0000000000000000  WA       0     0     8
      [18] .dtors       PROGBITS    0000000000600728  00000728   0000000000000010  0000000000000000  WA       0     0     8
      [19] .jcr         PROGBITS    0000000000600738  00000738   0000000000000008  0000000000000000  WA       0     0     8
      [20] .dynamic     DYNAMIC     0000000000600740  00000740   0000000000000190  0000000000000010  WA       5     0     8
      [21] .got         PROGBITS    00000000006008d0  000008d0   0000000000000008  0000000000000008  WA       0     0     8
      [22] .got.plt     PROGBITS    00000000006008d8  000008d8   0000000000000028  0000000000000008  WA       0     0     8
      [23] .data        PROGBITS    0000000000600900  00000900   0000000000000020  0000000000000000  WA       0     0     16
      [24] .bss         NOBITS      0000000000600920  00000920   000000000000a020  0000000000000000  WA       0     0     32
      [25] .comment     PROGBITS    0000000000000000  00000920   000000000000003e  0000000000000001  MS       0     0     1
      [26] .shstrtab    STRTAB      0000000000000000  0000095e   00000000000000e7  0000000000000000           0     0     1
      [27] .symtab      SYMTAB      0000000000000000  00001188   00000000000006a8  0000000000000018          28    50     8
      [28] .strtab      STRTAB      0000000000000000  00001830   0000000000000284  0000000000000000           0     0     1
    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings)
      I (info), L (link order), G (group), x (unknown)
      O (extra OS processing required) o (OS specific), p (processor specific)
    

    输出列表 1 节区头信息

    输出列表 1 列出了节区头信息。[Nr] 列是 Section 的编号,Name 列是段名,Type 列表示节区的类型,Address 表示该节区占据的虚拟内存起始地址,Offset 表示该节区在可执行文件中的存放位置(相对文件开始的偏移量),Size 列说明了该节区的大小。

    从上面输出中可以看到,示例 ELF 文件包含了 29 个不同类型的节区,以及每个节区在文件中的位置和大小等信息。其中,.text 中存放的是源程序编译生成的可执行机器指令,.rodata、.data 和 .bss 中存放的是程序数据。

    [root@localhost elf]# readelf -l elfdemo
    Elf file type is EXEC (Executable file)
    Entry point 0x4003c0
    There are 8 program headers, starting at offset 64
    
    Program Headers:
      Type           Offset             VirtAddr           PhysAddr            FileSiz            MemSiz              Flags  Align
      PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040  0x00000000000001c0 0x00000000000001c0  R E    8
      INTERP         0x0000000000000200 0x0000000000400200 0x0000000000400200  0x000000000000001c 0x000000000000001c  R      1
          [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
      LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000  0x0000000000000714 0x0000000000000714  R E    200000
      LOAD           0x0000000000000718 0x0000000000600718 0x0000000000600718  0x0000000000000208 0x000000000000a228  RW     200000
      DYNAMIC        0x0000000000000740 0x0000000000600740 0x0000000000600740  0x0000000000000190 0x0000000000000190  RW     8
      NOTE           0x000000000000021c 0x000000000040021c 0x000000000040021c  0x0000000000000020 0x0000000000000020  R      4
      GNU_EH_FRAME   0x0000000000000670 0x0000000000400670 0x0000000000400670  0x0000000000000024 0x0000000000000024  R      4
      GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000  0x0000000000000000 0x0000000000000000  RW     8
    
     Section to Segment mapping:
      Segment Sections...
       00
       01     .interp
       02     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
       03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
       04     .dynamic
       05     .note.ABI-tag
       06     .eh_frame_hdr
       07
    

    输出列表 2 程序头信息

    输出列表 2 展示了程序头信息。该表指出 ELF 文件有八个段,并且列出了每个段在文件中的起始地址(Offset 列)、程序加载后占据的虚拟地址(VirtAddr 列)、物理地址(PhysAddr 列)、在文件中占据的空间大小(FileSiz 列)、在内存中占据的空间大小(MemSiz 列)等信息。另外,还列出了节区与段的映射关系,指出了哪些节区在运行时会归入哪个段。通过段和节区的位置和大小信息,也可以进一步核对这些映射关系。

    3.1.4 ELF 文件的细节结构

    至此,虽然还没有探究每块数据的具体含义,但是一个可执行文件的结构骨架已经展示在我们面前了。现在我们已经可以更加细致地回答“可执行文件里有什么?”这个问题了。以上面的示例程序为例,它的细节结构描述如下。

    • 首先是 64 字节的 ELF 文件头,其中记录了该文件的类型、目标平台等信息,而且索引了整个文件需要的节区头和段头信息,详细信息如图 2 所示。
    • 接下来是 8 个程序头,每个 56 字节,占据了接下来的 448 字节,详细信息如输出列表 2 所示。
    • 紧接着是多个节区,从第 512 字节(0x00000200)开始,依次存放着从 .interp 到 .shstrtab 的 26 个节区,一直占据到了文件 2628 字节的位置(.shstrtab 节区的偏移 0x0000095e + 大小 0xe7),详细信息如输出列表 1 所示。
    • 然后从 2632 字节的位置开始存放节区头,一共有 29 个节区头,每个 64 字节,一共占据 1856 字节的空间,所以一直占据到文件 4487(0x1187)字节的位置。程序头的起始地址从 2632 开始而不是节区结束的 2629 字节开始,这是源于地址对齐的要求。
    • 最后,从 4488(0x1188)字节的位置开始,依次放置 .symtab 和 .strtab 节区,这两个节区分别占据了 1704 字节(0x6a8)和 644(0x0284)字节。

    至此,整个文件的所有 6836 个字节就被填满了。

    3.2 影响程序静态布局的因素

    认识了可执行文件的整体结构,我们继续讨论影响静态布局(节区信息)的因素。输出列表 1 所示的 29 个节区中,有几个节区与源程序的关系非常密切,下面就详细说说这几个节区。

    3.2.1 .text 节区

    该节区存储了源文件编译生成的机器指令。对开发者来说,这可能是最重要的一个节区,所有的程序逻辑都放在这里。但开发者对该节区能做的控制却很少,能影响它的因素只有开发者写的程序逻辑,以及编译时使用的选项。比如,使用 -O1 优化选项编译程序可以生成尽量紧凑的 .text 节区,而用 -O2 优化选项会使编译器倾向于生成执行速度更快的指令组合,但有可能让 .text 节区的体积轻微地增大。

    3.2.2 .rodata 节区

    该节区存储了程序中的常量数据。比如示例程序中的常量数组:

    static const char const_data[16] = "I'm Const Data"
    

    以及 printf 语句中的常量字符串 Message In Main。有 objdump 的结果为证:

    6c1899f0-0a6f-11e9-818b-29d006bce549


    另外,通过输出列表 2 还可以看到,程序运行时,.rodata 节区会被加载到与 .text 节区相同的段内,而且该段的属性如 Flags 列所显示的,只有 R(读)和 E(执行),而没有 W(写)权限,当程序试图修改该地址处的内容时,会因触发 Segment Violation 而令程序终止,由此实现对该节区内数据的保护。

    但需要注意的是,只有静态和全局的 const 数据才能享受这样的待遇。在某函数内声明的局部常量,只能靠编译器的语义检查报告错误或警告,而不会通过设置存储区权限来防止修改。例如,某函数内的如下代码片段,会被编译器认为存在语法错误而编译失败:

    const int const_value = 100;
    const_value = 200;
    

    但是,并不能阻止使用下面代码修改已经用 const 修饰过的变量值:

    const int const_value = 100;
    int * ptr = (int *)&const_value;
    *ptr = 200;
    

    因为在函数内部声明的 const_value,其本质上还是一个函数内的局部变量,存储区在该函数的栈帧内,而程序对该内存区拥有修改的权限。

    相应地,用同样方法试图修改全局或静态常量数据的值,如下所示:

    char * pc = (char *)const_data;
    *pc = 'X';
    

    编译器并不会报告任何错误,编译可以通过。但当程序运行到第二行代码时,就会因为 Segment Violation 而崩溃,原因在于程序对该位置的内存区没有修改权限

    3.2.3 .data 节区

    所有的全局和静态的已初始化变量会存放在这个节区中,比如下面示例程序中的 static_data。

    static char static_data[16] = "I'm Static Data";
    

    同样有 objdump 的结果为证:

    72720240-0a70-11e9-915a-ed6502011c51


    该节区运行时会被加载到标号为 03 的段内,权限设置为 R(可读)和 W(可写)。

    3.2.4 .bss 节区

    该节区存储了所有未初始化或初始化为 0 的全局和静态变量。比如示例程序中的如下变量,就会被放入这个节区中。

    static char raw_static_data[40960];
    

    仔细观察这个节区的信息,就会发现,虽然该节区的大小是 0xa020 字节,但它之后的 .comment 节区与该节区有相同的 Offset 值,也就是说,.bss 节区在可执行文件中不占据任何空间,加载到内存区之后才会被分配内存。

    仔细观察程序头信息,也可以进一步验证这个结果。在程序头信息中,.bss 在标号 03 的段中,而该段的 FileSiz 值为 0x0208,MemSiz 值为 0xa228,其差值刚好是 .bss 节区的大小。

    该节区的设计初衷就是为了节省目标文件的存储空间。变量未被初始化,或者虽被初始化了,但值为 0,就没必要浪费空间,再在目标文件中存储大量的 0 值。

    3.2.5 .got 和 .plt 节区

    这两个节区存储了动态链接用到的全局入口表和跳转表。当你的程序中用到动态链接库中的某个函数时,会在该节区内记录相应的数据。详细内容将在后面课程中深入介绍动态链接库时再展开。

    3.2.6 用扩展属性指定节区

    除了变量类型会影响数据的存储节区以外,GCC 还支持用 GUN C 的一个扩展属性指定数据的存储节区。

    __attribute__((section("personal"))) char g_array[1024];
    

    例如上面代码,就会把 g_array 放到新的 personal 节区中。但使用该特性时,应该清楚如此声明的全局或静态变量,不管有没有初始化,都会在可执行文件中占据相应的存储空间,不能再享受 .bss 段所带来的节省存储空间的好处

    3.3 控制程序的动态布局

    控制程序的动态布局,就是控制 ELF 文件中程序头的信息,而这些信息由链接器(LD)负责组装。所以,控制了链接器的组装行为,即可控制程序的动态布局。

    控制链接器的组装行为由链接控制脚本(后缀通常为 lds)负责。该脚本描述了如何把输入文件中的各部分组装到生成文件中,并控制输出文件中的每个段在内存中的排布。开发人员平时很少接触到该脚本,是因为 GCC 提供的默认链接脚本已经能够满足绝大多数需求了。

    3.3.1 链接控制脚本

    生硬地讲解链接脚本的语法和功能是很无趣的,所以我决定先抛出一个具体的需求,通过这个需求探索链接脚本的功能和用法

    需求是这样的,在应用中,有一块静态长度的数据非常重要,我们需要保护这块数据,确保它不会被意料之外的数组溢出或者错误的指针修改。如果真发生意料之外的修改,那么就让程序崩溃,以让该逻辑错误尽早地暴露出来。同时,该数据块又不是完全只读的,某些特定情况下,还需修改这块数据的内容。需要长期稳定运行的软件产品,其配置数据通常会有这样的需求。

    我们分析下这个需求。当意料之外的修改发生时,要实现程序崩溃功能,这块数据首先应该被设置为只读。当遇到特定情况,要对数据内容修改时,又需要为这块内存添加修改权限。可以使用系统调用 mprotect 来实现,但要使用这个系统调用,还需要把这块受保护数据的存储地址按照内存页的大小对齐。

    同时想到可以用 GUN C 的扩展特性自定义节区,于是我们有了如下解决方案:

    • 把受保护的变量放入一个自定义的节区,让该节区的加载地址按照内存页大小对齐,并让它之后的其他节区从新的内存页开始布局;
    • 受保护的内容初始化完毕之后,用 mprotect 系统调用将受保护内容所占的内存页都设置为写保护
    • 为受保护变量添加专门的数据修改 API,API 中先为要修改的内存页添加写权限,修改完成后马上移除写权限

    把数据放入自定义的节区很简单,只要使用 GUN C 的属性标记就可以了:

    __attribute__((section(".protect"))) SecurityDataStruct g_secData;
    

    如何控制它的内存地址呢?想想默认生成的可执行文件,.text 和 .rodata 节区的数据会被放入同一个段,并被设置为 RE(Read)权限。接下来,会排布 .data 和 .bss 等节区所在的数据段,并将权限设置为 RW(Read & Write),所以数据段的地址一定是从一个新的内存页开始的。默认脚本是怎么实现这个效果的呢?

    3.3.2 脚本中的 SECTIONS 块

    我们用 ld --verbose 命令查看下默认链接脚本的内容,看看能发现什么。输出结果如下(实际输出的内容很多,为了方便讲解,只保留了一部分结果,并添加了行标号):

    1  OUTPUT_FORMAT("elf64-x86-64")
    2  ENTRY(_start)
    3  SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64");
    4  SECTIONS
    5  {
    6    
    7    PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); 
    8    . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
    9    .init           :  {  KEEP (*(.init))  } =0x90909090
    10   .text           :
    11   {
    12     *(.text.unlikely .text.*_unlikely)
    13     *(.text .stub .text.* .gnu.linkonce.t.*)
    14   } =0x90909090
    15   .fini           :  {  KEEP (*(.fini)) } =0x90909090
    16   PROVIDE (etext = .);
    17   .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
    18   . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); 
    19   . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
    20   .data           :
    21   {
    22     *(.data .data.* .gnu.linkonce.d.*)
    23     SORT(CONSTRUCTORS)
    24   }
    25   _edata = .; PROVIDE (edata = .);
    26   __bss_start = .;
    27   .bss            :
    28   {
    29    *(.dynbss)
    30    *(.bss .bss.* .gnu.linkonce.b.*)
    31   }
    32   . = ALIGN(64 / 8);
    33   _end = .; PROVIDE (end = .);
         ......
    

    可以看到,一个链接脚本中最主要的部分是 SECTIONS 块,里面定义了生成的节区的映射关系,基本格式为:

    .output : { *(.input)}
    

    它表示把输入文件中名字为 xxx.input 的节区内容合并到输出文件的 .output 节区。另外,第 8、18、19 行出现了:

    . = xxxx
    

    表示把当前的组装地址定位到内存地址 xxxx 处。18、19 行表示组装完 .rodata 节区之后,对 . 执行的一些计算,即把当前的组装地址移动到下一个内存页的开始位置,这样 .data 的开始地址便从下一个内存页开始了。

    仿照其内容,我们可以在 .bss 节区之前增加我们自定义的 .protect 节区:

    . = . - (. & (CONSTANT (COMMONPAGESIZE) - 1)) + (CONSTANT (COMMONPAGESIZE));
    .protect        : { *(.protect)}
    . = . - (. & (CONSTANT (COMMONPAGESIZE) - 1)) + (CONSTANT (COMMONPAGESIZE));
    

    使用这个自定义 lds 脚本重新编译源程序(使用 GCC 的 -Wl 选项为 linker 传递自定义参数 -T protect.lds,指定使用的自定义链接控制脚本):

    gcc -o elfdemo elfdemo.c -Wl,-T protect.lds
    

    用 readelf -S 查看生成的可执行文件,其中可以看到新加的 .protect 节区,如下图所示:

    从中可以发现,新加的 .protect 节区放在了 .data 节区之后,加载地址从一个新的内存页 0x602000 开始,虽然其大小只有 0x404 字节,但独占了一个内存页,其后的 .bss 节区从新的内存页地址 0x603000 位置开始。这样,我们就可以自由地对保护数据所在的内存页执行权限修改操作,而不影响其他节区内的数据。

    3.4 检查地址范围

    细心的读者可能还会发现,在默认链接文件的输出内容中有如下这样的内容:

    25   _edata = .; PROVIDE (edata = .);
    26   __bss_start = .;
    

    它们有什么用呢?

    实际上,它们是在链接脚本中定义的一些符号。在这里,这些标号用来记录当前的组装地址。在程序中我们可以这样使用:

    int main()
    {
       extern char __bss_start;
       printf(".bss Start Address: %p\n", &__bss_start);
       return 0;
    }
    

    仿照此方法,可以给我们的保护数据段也增加两个范围标号:

      . = . - (. & (CONSTANT (COMMONPAGESIZE) - 1)) + (CONSTANT (COMMONPAGESIZE));
      __protect_start = .;
      .protect        : { *(.protect)}
      __protect_end = .;
      . = . - (. & (CONSTANT (COMMONPAGESIZE) - 1)) + (CONSTANT (COMMONPAGESIZE));
    

    程序可以通过这两个标号的地址,精确地判定给定的地址是不是有效的保护区地址:

    #define IS_PROTECT_ADDRESS(ptr)  (((char *)ptr >= &__protect_start && (char *)ptr < &__protect_end) ? 1 : 0)
    

    需要注意的是,链接脚本中定义的标号只是用来标定一个地址,并不会为它准备额外的存储空间,也就是说,&__protect_start 和 &g_secData 会得到相同的地址。所以,永远不要对 __protect_start 执行赋值操作。

    默认的链接脚本中还提供了 __executable_start、__etext、_edata、__bss_start 等导出标号,分别用于标记代码段的开始地址、代码段的结束地址、数据段的结束地址、bss 段的开始地址等,应用程序可以按需选用。当需要更多标记地址时,在链接控制脚本里自定义添加就可以了。

    3.5 总结

    本文带领读者深入到了 Linux 平台上可执行文件的内部,着重了解了可执行文件内部的信息是如何组织的,以及程序中不同类型的变量在可执行文件中是如何存放的,然后通过一个真实需求窥探了链接控制脚本的功能

    通过本文的学习,读者应该对可执行文件的底层运作细节有更深刻的理解,同时有能力实现下面这些需求,至少会有实现思路:

    • 如果手上有某个英文版应用的可执行程序,如何对其进行汉化?
    • 若要将可执行文件中的某些资源提取出来,该如何写程序?
    • 如何实现程序自检,也就是确认自己的代码段数据没有被人暴力修改过?
    • 一旦程序被外部修改,将无法正确执行,该如何实现?
    • 如何保护保存在可执行文件中的资源或敏感数据?

    总之,了解越深入,对应用的控制能力也就越强,也就能实现更酷的功能!

    3.6 答疑与交流

    为了让订阅课程的读者更快更好地掌握课程的重要知识点,我们为每个课程配备了课程学习答疑群服务,邀请作者定期答疑,尽可能保障大家学习效果。同时帮助大家克服学习拖延问题!

    购买课程后,一定要添加小助手伽利略微信 GitChatty6(也可扫描下面二维码),备注:Linux 系统编程 ,并将支付截图发给她,小助手会拉你进课程学习群。

    681b8d40-18c9-11e9-a363-49890a096896
    展开全文
  • 本书从MATLAB仿真的角度系统地介绍了滑模变结构控制的基本理论、基本方法和应用技术,是作者多年来从事控制系统教学和科研工作的结晶,同时融入了国内外同行近年来所取得的新成果。 本书是在第2版基础上修改而成的,...
  • QCAT1.2.1控制分配工具箱,包括:直接分配,动态分配以及仿真算例等等。 程序列表: 1. Control allocation using interior point method; 2. Control allocation using sequential least squares; 3. Control ...
  • 该插件允许将Views定义为一个类,从而提供了一个强大面向对象工厂界面,可以使用可配置对象动态构建应用程序的UI。 该插件主要功能包括: 面向对象设计:每个UI元素都作为一个对象进行定义和操作,通过将...
  • 《滑模变结构控制MATLAB仿真(第2版)》从MATLAB仿真角度系统地介绍了滑模变结构控制的基本理论、基本方法和应用技术,是作者多年来从事控制系统教学和科研工作的结晶,同时融入了国内外同行近年来所取得的最新成果。...
  • 实验名称: TEC-2机微程序设计实验 ... 1、TEC-2机的控制器部件关键内容包括: (1)7片LS6116RAM组成56位字长程序控制存储器 (2)微指令寄存器PLR,由7片8位寄存器芯片(6片LS374和1片LS273) ...
  • 进程和程序的区别

    万次阅读 2018-02-28 15:38:30
    一、 进程是动态的程序是静态程序是有序代码集合,进程是程序的执行。...进程是一个状态变化过程,程序可以长久保存三、进程和程序的组成不同,进程组成包括程序、数据和进程控制块(即进程状态信息)...
  • LabVIEW的动态事件注册

    2021-01-19 23:02:11
    动态事件可使事件仅在应用程序的某个部分发生,也可在应用程序运行时改变产生事件VI或控件。使用动态注册,可在子VI中处理事件而不是仅在产生事件VI中处理事件。  处理动态注册事件主要包括以下4个步骤。 ...
  • 写在前面 2018年春节假期,受朋友鼓励和内心指引,对近两年所学到知识进行了系统沉淀和总结。 ...代码清单,资源控制功能扩展,让代码更简洁和实用,父类控制器中编写好了数据增删
  • 本编程手册适用于具有C语言编程基础或Windows环境下使用动态链接库的基础,同时具有一定运动控制工作经验,对伺服或步进控制的基本结构有一定了解的工程开发人员。 编程手册的主要内容: 本手册由十二章内容组成。...
  • 此PublicGallery模板适用于希望在动态画廊中展示其ArcGIS Online地图,图层和移动应用程序的任何人。 。 特征 公共地图库(PMG)模板是为希望在动态Web画廊中展示其ArcGIS Online地图,图层和移动应用程序的任何人...
  • 进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元; 程序是放到磁盘的可执行文件 进程是指程序执行的实例 进程和程序的区别: 进程是动态的,程序是静态的; 进程是暂时的,程序使...
  • 进程与程序的区别

    2018-09-16 16:04:26
    1.动与静:进程是动态的,而程序是静态程序是有序代码集合,进程是程序的集合。 2.永久与暂时:进程是暂时程序是...3.结构:进程组成包括程序 数据和进程控制块 4.进程与程序的关系都可以是一对多。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,005
精华内容 802
关键字:

动态控制的程序包括