精华内容
下载资源
问答
  • 算符优先算法中优先函数的构造

    千次阅读 2020-11-04 10:29:09
    算符优先分析中“优先函数的简单构造!”


            这里我介绍的是一种简单的方法,不是书本的那个方法。是我后面那个参考资料上面的作者想出来的。因为这个是在知网上面找到的,是1997年的一篇文章。我就是一个总结,然后画几个比较清楚的图而已(因为从知网上下载的的pdf上面的图有点不太清楚)

    优先函数树形构造法

            我们教材上面的就是陈火旺老师的那本,然后方法就是找节点。这个方法虽然简单,但是操作起来的却有些麻烦,因为我们作图的时候,难免就会看不清楚,或者数错。下面介绍的树形构造法就可以避免这个问题,仅仅只需要根据优先矩阵就可以得出正确结果。

    操作步骤

    对于一个优先表先做如下几个步骤(假设优先函数存在)
    1 ) 对于f(a)对应行中“a>=b 的所有终结符b , 把g(b)作为f(a) 的子树。
    2) 对g(a) 对应列中有b <=a 的. 把f(a) 作为g(b)的子树.
    3 ) 对f(a)重做第( 1 ) 步, 对g(b) 重做第( 2 ) 步。
    4) 重复出现的f或g , 作记号不再对其执行第( 1) , ( 2) 步
    方向图
    5) 构造f(a)函数时, 以f(a) 为根结点, 按( 1 ) ,(2 ),( 3),( 4) 执行.
    6) 构造g(b)函数时, 以g(b) 为根结点, 按(2 ) ,( 1 ) , ( 3 ) , ( 4 ) 执行.
    按照以上5步先画出树,然后查树的节点数(包括根节点,做记号的不查),即可以得到根节点的优先函数值。
    但是我觉得这个操作步骤2有点问题。应该是b>=a的时候,就把f(a)作为g(b)的子树。我也自己做了实验,发现这样做才是正确的。不过也可能是我没有理解原作者的意思。反正,就目前看来,我都是按照如果b<=a,就把f(a)作为g(b)的子树。
    所以,下面我举例的例子,第(2)步都是按照“b>=a的时候,就把f(a)作为g(b)的子树”这个来做的。

    举例操作

            假如现有一个优先矩阵是这样的:这个例子是《编译原理》陈火旺老师(下面我说的教材,没有特别说明也是指的这本教材)那本书上面的。在90页。
    在这里插入图片描述
    那么我们现在用树形构造法来试试怎么得到这个优先函数。因为最后的答案是去掉了 i 和 # 之后得到的,所以我下面也将不会考虑 i 和 # 号。至于为什么不考虑 i 和 #,,就是因为这两个不是算符,我们算符优先函数主要针对的就是算符直接的优先级。但是优先关系矩阵中是给出了 i 和 # 与其他算符之间的优先关系的。我这里其实是有一个疑问的:因为在优先函数这里没有 i 的优先关系,那么在使用优先函数来分析一个句子是不是这个文法定义的合法的句子的时候,那么什么时候该移进这个 i 呢?希望知道的小伙伴可以在下面留言,我们讨论一下。

            计算f(+)的优先函数值。

    1. 从优先矩阵中可以得到:’+‘ >= 的算符有;’+’,’)’,’#'三个。因为不考虑i 和 #(下面就不提醒这一点了),所以f(+)的孩子节点有两个,分别是g(+)和g()),如下图所示。
      在这里插入图片描述
    2. 现在就来找g(’+’) 的子树。我这里还是采用的是如果b>=a,就把f(a)作为g(b)的子树。从优先表中可以看到的就是’+‘ >= 的算符有:’(’。注意,我这里说的’+‘是g(’+‘),也就是从’+‘那一列中寻找。在这里插入图片描述
      所以,g(’+’)的子树就是f(’(’);在这里插入图片描述
    3. 然后找到g(’)’) >=的算符:在这里插入图片描述
      但是f(’(’)已经出现过了(作为g(’+’)子树出现的),所以就不用画出来了。
    4. 然后就是寻找f(’(’) >=的算符。在这里插入图片描述但是g(’)’)也已经出现过了,所以这里也不用画出来了。
    5. 最后每一个节点我们都已经检查过了,没有可以重新添加的了。也就是趋于稳定了(编译原理里面好多都是这样子的,得使所有都不再变化的时候,算法就算结束了)。我们数一下节点的个数,就是4个,和书本95页给出的答案是一样的。

            同理,g(’+’)的树也可以这样画出来。我就给出最后的树,就不一一分析了。
    在这里插入图片描述

    自己的思考

            本质上书本上的画图和这里的画一颗树,都是优先级高的指向优先级低的,所以入度为0的节点,给的优先函数值应大一点,出度为0的,给出的优先函数值应该小一点。

            上面提到的这个简单的方法其本质善和我们教材上的方法是一样的。你可以将教材上的方法中的图给截取一部分出来。例如,你截取以g(’+’)作为顶点的树去看,发现就是和我上面画的一样。

            还有注意的一点就是:我们求出一组优先函数之后,就可以构造出无数组优先函数。所以,如果你求出来的和参考答案给出的不是一样的,也不一定是你错了。只要你的优先函数之间的关系和参考答案上给出的关系是一样的就可以了。

    参考资料

    构造优先函数的更简单方法_树型构造_潘群娜
    这个知网上我找到的一篇关于这个优先函数构造比较简单的方法,如果大家对上面的博客解释的不太清楚的话,可以去知网上看这个原作者写的文章。

    展开全文
  • 编译原理 优先函数实现表达式计算

    千次阅读 2020-04-23 00:36:27
    优先函数实现表达式计算 左/右 + * ( ) i # + ·> · · ·> · ·> * ·> ·> · ·> · ·> ( · · · = · ) ·> ·> ·> ·> i ·> ·> ·> ...

    优先函数实现表达式计算

    左/右 + * ( ) i #
    + ·> <· <· ·> <· ·>
    * ·> ·> <· ·> <· ·>
    ( <· <· <· <·
    ) ·> ·> ·> ·>
    i ·> ·> ·> ·>
    # <· <· <· <·

    由优先表构造优先函数

    优先函数 + * ( ) i #
    f 6 8 2 8 8 1
    g 4 7 9 2 9 1

    本程序使用C语言编写
    支持整数加减乘除带括号的运算
    核心部分和数据结构中利用栈实现表达式运算几乎相同
    程序的难点在于优先级<、=、>分类讨论

    #include<stdio.h>
    #include<stdlib.h>
    #include <string.h>
    //优先函数
    int fplus = 6;
    int gplus = 4;
    
    int fmul = 8;
    int gmul = 7;
    
    int fleft = 2;
    int gleft = 9;
    
    int fright = 8;
    int gright = 2;
    
    int fi = 8;
    int gi = 9;
    
    int fpound = 1;
    int gpound = 1;
    
    char expression[128];
    int flag = 1; 
    
    void Error()
    {
        printf("错误\n");
        exit(0);
    }
        
    void Input()
    {
    	//char over[] = {'#', '\0'};
        printf("请输入表达式,以#结尾:"); 
        gets(expression); 
    	//printf("Input: %s\n",expression);
    }
    
    int f(char opt)
    {
        if(opt == '+' || opt == '-')
        {
    		return fplus;
        }
        else if (opt == '*' || opt == '/')
        {
            return fmul;
        }
        else if (opt == '(')
        {
            return fleft;
        }
        else if (opt == ')')
        {
            return fright;
        }
        else if (opt == 'i')
        {
            return fi;
        }
        else if (opt == '#')
        {
            return fpound;
        }
        return -1;
    }
        
    int g(char opt)
    {
        if(opt == '+' || opt == '-')
        {
            return gplus;
        }
        else if (opt == '*' || opt == '/')
        {
            return gmul;
        }
        else if (opt == '(')
        {
            return gleft;
        }
        else if (opt == ')')
        {
            return gright;
        }
        else if (opt == 'i')
        {
            return gi;
        }
        else if (opt == '#')
        {
            return gpound;
        }
        return -1;
    }
        
    int In(char num)
    {
        if(num == '0' ||num == '1' || num == '2' || num == '3' || num == '4' || 
    	   num == '5'|| num == '6' || num == '7' || num == '8' || num == '9')
    	   return 1;
        else
            return -1;
    }
        
    int Operation(int a, char opt, int b) 
    {
    	switch(opt)
    	{
    		case '+': return a + b; 
    		case '-': return a - b; 
    		case '*': return a * b; 
    		case '/': return a / b; 
    		default : return 0;
    	} 
    } 
    
    int main() 
    {
    	int index_e = 0;	// 字符串下标
    	int	index_oprt = 0; // 算符栈下标
    	int index_opnd = 0; // 算量栈下标
        int Sn[100];
    	char Sp[100];
    	int num1;
    	int num2;
    	char temp[100];
        Sp[0] = '#';
    
        Input();
    	//printf("算符栈: %s\n",Sp);
        while( flag == 1 )
        {
    		int t = 0;
            if( Sp[index_oprt] == '#' && expression[index_e] == '#' )
            {
    			//printf("都为#,结束\n");
                flag = 0;
            }
            else if(Sp[index_oprt] == '(' && expression[index_e] == ')')
            {
    			//printf("读到括号\n");
                index_oprt--;
                index_e++;
            }
            else if(In(expression[index_e]) == 1)
            {
    			//printf("读到数字\n");
                while(In(expression[index_e]) == 1)
                {
                    temp[t] = expression[index_e];
    				t++;
                    index_e++;
                }
    			//printf("temp: %s\n",temp);
    			//atof()是把字符串转换成double
                Sn[index_opnd++] = atof(temp);
    			//printf("number %d\n",Sn[index_opnd-1]);
                //清空temp
    			for(int m=0; m<100; m++)
    				temp[m] = '\0';
            }
            else if(f(Sp[index_oprt]) < g(expression[index_e]))
            {
    			//printf("%d %d\n",f(Sp[index_oprt]),g(expression[index_e]));
    			//printf("小于\n");
                Sp[index_oprt + 1] = expression[index_e];
                index_oprt++;
                index_e++;
    			//printf("%c 入栈\n",Sp[index_oprt]);
    		}
            else if(f(Sp[index_oprt]) > g(expression[index_e]))
            {
    			//printf("大于\n");
    			//出栈两个数
                num2 = Sn[index_opnd - 1];
    			index_opnd--;
                num1 = Sn[index_opnd - 1];
                index_opnd--;
    			//算符栈出一个,算完了结果再入算量栈
    			Sn[index_opnd] = Operation(num1, Sp[index_oprt], num2);
                index_opnd++;
                index_oprt--;
            }
            else
            {
                Error();
            }
        }
        printf("表达式的值为:%d\n", Sn[0]);
    	return 0;
    }
    

    运行截图:
    在这里插入图片描述

    展开全文
  • 自底向上分析方法,也称...重复这一过程,直到栈中只剩文法的开始符号时,则分析成功,也就确认输入串是文法的句子。 5.1 自底向上优先分析法概述 5.2 简单优先分析法 5.3 算符优先分析法 ...

    自底向上分析方法,也称移进-归约分析法。

    实现思想:

    • 对输入符号串自左向右进行扫描,并将输入符逐个移入一个栈中,边移入边分析,一旦栈顶符号串形成某个句型的句柄时,就用该产生式的左部非终结符代替相应右部的文法符号串,这称为归约
    • 重复这一过程,直到栈中只剩文法的开始符号时,则分析成功,也就确认输入串是文法的句子。
      在这里插入图片描述
      在这里插入图片描述

    5.1 自底向上优先分析法概述

    在这里插入图片描述

    5.2 简单优先分析法

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    5.3 算符优先分析法

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • IDA 汇编命令分析以及函数调用过程

    千次阅读 2017-08-15 19:39:41
    dll的文件,入口函数DllEntryPoint: .text:000000018000525C ; BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) .text:000000018000525C public DllEntryPoint .te

    dll的文件,入口函数DllEntryPoint:

    .text:000000018000525C ; BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
    .text:000000018000525C                 public DllEntryPoint
    .text:000000018000525C DllEntryPoint   proc near               ; DATA XREF: .pdata:0000000180009474o
    .text:000000018000525C
    .text:000000018000525C arg_0           = qword ptr  8
    .text:000000018000525C arg_8           = qword ptr  10h
    .text:000000018000525C
    .text:000000018000525C                 mov     [rsp+arg_0], rbx
    .text:0000000180005261                 mov     [rsp+arg_8], rsi
    .text:0000000180005266                 push    rdi
    .text:0000000180005267                 sub     rsp, 20h
    .text:000000018000526B                 mov     rdi, r8
    .text:000000018000526E                 mov     ebx, edx
    .text:0000000180005270                 mov     rsi, rcx
    .text:0000000180005273                 cmp     edx, 1
    .text:0000000180005276                 jnz     short loc_18000527D
    .text:0000000180005278                 call    sub_180005688
    .text:000000018000527D
    .text:000000018000527D loc_18000527D:                          ; CODE XREF: DllEntryPoint+1Aj
    .text:000000018000527D                 mov     r8, rdi         ; lpvReserved
    .text:0000000180005280                 mov     edx, ebx        ; fdwReason
    .text:0000000180005282                 mov     rcx, rsi        ; hinstDLL
    .text:0000000180005285                 mov     rbx, [rsp+28h+arg_0]
    .text:000000018000528A                 mov     rsi, [rsp+28h+arg_8]
    .text:000000018000528F                 add     rsp, 20h
    .text:0000000180005293                 pop     rdi
    .text:0000000180005294                 jmp     __DllMainCRTStartup
    .text:0000000180005294 DllEntryPoint   endp


    arg_0           = qword ptr  8:声明arg_0变量等于从地址rbp:8处取出一个四字宽度的值,地址rbp:8指向一个4字(8个字节)的值,且取别名为arg_0,以后要用到时,直接使用别名。C语言相当于*(rbp+8)赋给了arg_0。可参考地址

    esp:32位栈指针寄存器(extended stack pointer),该指针指向系统栈最上面栈的顶部。

    ebp:32位基址指针寄存器(extended base pointer),该指针指向系统栈(整个系统)中最上面栈帧(一个函数)的底部。

    函数栈帧:esp和ebp之间内存空间为当前栈帧,ebp表示当前栈帧底部,esb表示当前栈帧顶部。



    eip:指令寄存器(extended instruction pointer),该指针指向下一条待执行的指令地址。


    参考地址

    rsp:64位的栈顶指针,相较于rbp处于低地址

    rbp:64位的栈底指针,相较于rsp处于高地址

    rbx:64位通用寄存器,64位基地址

    rsi:用来向函数参数的指针,源

    rdi:用来向函数参数的指针,目的

    ebx:用来向函数参数的指针

    call:将当前相对地址(IP)压入栈中,调用CALL后的子程序。

    lea:全称为load effective address,从存储器读数据到寄存器,实质是将存储器的有效地址写入到目的操作数,C语言的&

    将源操作数(即存储单元的有效地址偏移地址)传送到目的操作数(运行时执行

    左边的是目的操作数,保存操作结果,该指令目的操作数只能是8个通用寄存器之一

    右边的是源操作数,该指令的源操作数只能是一个存储单元,表达存储单元有多种寻址方式(Intel汇编格式)

    jae: 大于等于转移

    ja:前者大于后者转移,用于无符号比较(jump if above)

    jl:条件跳转指令,用于有符号数 小于跳转,jump less。还有类似jg 大于跳转jump grater


    xor: 异或,相同为0,不同为1。

    xor <op1> <op2>

    op1异或op2 后 结果放入op1

    xor eax, eax 与 mov eax, 0作用相同,只是所占字节不同而已,前面为2字节,后面为5字节

    1.它一般用来判断两个值是否相等

    比如:

    xor     rcx, rsp

    判断rcx与rsp是否相等,相等则rcx为0,不等,则为1

    2.它一般用来给一个值清0

    比如:

    xor     edx, edx

    将edx赋值为0


    jz: jump if zero(结果为0,则设置ZF零标志位为1,并跳转)

    jnz:jump if not zero


    test:逻辑运算指令,根据结果设置标志寄存器ZF,结果本身不保存。运算结果为0时,ZF零标志置1,否则清0

    test ax,bx与test bx,ax效果一样。

    它一般用来判断寄存器值是否为0

    比如:

    test ax, ax 

    jz short loc_1400012C8

    判断ax是否为0,为0,则跳转到子函数loc_1400012C8处执行


    cmp:比较指令, cmp的功能相当于减法指令,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器
    位来得知比较结果。  

    比如:

    mov ax,8 

    mov bx,3 

    cmp ax,bx  

    执行后:

    ax=8,ZF=0,PF=1,SF=0,CF=0,OF=0.  

    通过cmp指令执行后,相关标志位的值就可以看出比较的结果。 


    cmp ax,bx的逻辑含义是比较ax,bx中的值。

    如果执行后: 

    ZF=1则AX=BX 

    ZF=0则AX!=BX 

    CF=1则AX<BX 

    CF=0则AX>=BX 

    CF=0&ZF=0则AX>BX 
    CF=1|ZF=1则AX<=BX

    比如:

    mov eax, 10
    mov ebx, 10
    cmp eax, ebx 

    相减求出结果,若为0,则ZF置1,反之结果为1,则ZF清0
    jz  SOME_WHERE ;检查ZF,为1就跳
    jnz SOME_WHERE ;检查ZF,为0就跳


    db:以表达式的值的字节形式初始化代码空间,只能位于CODE段之内,否则会发生错误。

    格式:[标号:] db 表达式

    表达式中可包含符号,字符串或表达式等项。各项之间以逗号隔开,字符串用引号括起来。括号内的标号时可选的,如果使用了标号,则标号的值将时表达式表中第一个字节的地址。

    retn:段内转移子程序返回。

    movzx:零扩展传送

    setb:将其后寄存器位置1

    neg:求其后寄存器的补码

    lock:使CPU产生一个LOCK#信号,确保在多处理器系统或多线程竞争的环境下互斥的使用这个内存地址,当指令执行完毕时,锁定动作消失。

    cmpxchg:比较交换指令,第一个操作数先与al/ax/eax比较;若相等则ZF置1,且第二个操作数覆盖第一个操作数。反之,ZF清0,第一个操作数覆盖al/ax/eax。在80486以上的CPU中支持。

    .text:004092D9                 lock cmpxchg [esi], ecx

    jmp short opr:段内直接短跳转转移

    因为偏移量需向前或向后跳转,因此它时一个有符号的数值。这种转移格式只允许在-128~+127字节范围内转移。

    .text:004092DF                 jz      short loc_4092EC

    offset:取偏移地址。(编译时执行)

    mov reg,offset xxx 比 lea reg,xxx 的指令长度少一个字节,且快一个时钟,因此,应优先选用 mov offset 指令

    栈:汇编内把一段内存空间定义为一个栈,栈总是先进后出。

    进栈指令Push:

    push reg/mem/seg

    sp<---sp-2

    ss<---reg/mem/seg

    先使堆栈指令sp减2,随后把一个字操作数存入堆栈顶部。其操作单元只能是字,进栈时低字节存放在低地址,高字节存放在高地址,sp相应的向低地址移动两个字节单元。

    出栈指令:

    pop reg/seg/mem; reg/seg/mem<--ss:[sp],sp<-sp+2

    出栈指令把栈顶的一个字传送至指定的目的操作数,然后堆栈指针sp加2。其操作单元只能是字,字从栈顶弹出时,低地址字节送低字节,高地址字节送高字节。
    POP SS堆栈可以用来临时存放数据,以便随时恢复它们。也常用于子程序见传递参数。
    注意几点:
    1.因为堆栈指针sp总是指向已经存入数据的栈顶(不是空单元),所以push指令是将(SP)减2,后将内容压栈(即先修改SP是指指向空单元,后压入数据),而POP是先从栈顶弹出一个字,后将堆栈指针SP加2.
    2.PUSH CS是合法的,但是POP CS是不合法的。
    3.因为SP总是指向栈顶,而用PUSH和POP指令存取数时都是在栈顶进行的,所以堆栈是先进后出或叫后进先出的。栈底在高地址,堆栈是从高地址向低地址延伸的,所有栈底就是最初的栈顶。
    4.用PUSH指令和POP指令时只能按字访问堆栈,不能按字节访问堆栈。
    5.PUSH和POP指令都不影响标志。

    .text:00404200    var_1B4         = dword ptr -1B4h

    字单元赋值给变量var_1B4,它的值是ebp-1B4h。表示被调函数的局部变量地址。

    .text:00404730 arg_0           = dword ptr  8

    表示调用函数传入被调函数的参数值。都是以ebp为基址进行偏移。

    .text:00404736                 mov     eax, ___security_cookie

    ___security_cookie该值是一个全局变量,其在编译阶段就被编译器所创建,随后写入.data区,保存在PE中;___security_cookie用来与局部变量与ebp之间的一个地址(它是在函数执行返回地址前的一个四字节地址,只要它被修改了,说明栈溢出了。可以先于返回地址被发现。。。)进行对比,确认该值是否与当时push到栈中的值是否一致,若不一致说明,栈溢出覆盖修改了这个值,那么就需要终止程序执行了。

    ___security_cookie详细可参考地址


    .text:00404746 loc_404746:                             ; CODE XREF: sub_404730:loc_4047C3↓j

    代码交叉引用,其中loc_404746是被引用者,引用者为loc_4047C3,向下箭头↓表示引用者位置在下面。

    箭头↓后面的j表示调用类型为jump。在IDA中,指令转交控制权的方式叫做流(flow):

    有三种基本流:

    1.普通流:表示一条指令到另一条指令的顺序流,为所有非分支指令的默认执行流(比如SUB,ADD指令等)。

    2.调用流↓p(Procedure):如果IDA认为某个函数并不返回,那么,在调用该函数时,它就不会为该函数分配普通流。由函数调用导致的交叉引用使用此后缀。也会有多个交叉引用出现情况,类似下面的跳转流。

    3.跳转流↓j(Jump):每个无条件分支指令和条件分支指令将分配到一个跳转流。也会有多个交叉引用出现,比如:



    .text:00404730 sub_404730      proc near               ; DATA XREF: sub_404120+71↑o

    数据交叉引用:用于跟踪二进制文件访问数据的方式。数据交叉引用与IDA数据库中任何牵涉到虚拟地址的字节有关(数据交叉引用与栈变量毫无关系)。

    最常用的三种数据交叉引用:

    1.读取交叉引用

    2.写入交叉引用

    3.偏移量交叉引用

    更详细可参考地址


    函数调用过程:

    参数入栈:

    将参数从右到左的顺序依次压入系统栈中

    返回地址入栈:

    将当前代码区调用指令的下一条指令地址压入栈中,待函数返回时继续执行代码区跳转(处理器从当前代码区跳到被调用函数的入口处)

    栈帧调整:

    保存当前栈帧状态,已备后面恢复本栈帧时使用(EBP入栈)

    将当前栈帧切换到新栈帧(将esp装载进ebp,更新栈帧底部)

    给新栈帧分配空间(把esp减去所需空间的大小,抬高栈顶)


    更详细可参考地址


    以下代码是经过IDA反汇编一个exe文件出来的一个小片段:

    04 loc_402A04:                             ; CODE XREF: sub_4029E0+29↓j  // 代码交叉引用,即该子函数会在下面以jump形式被调用
    .text:00402A04                 mov     al, [ecx] // 将ecx寄存器所指向的值赋给寄存器al [ecx]-存储器寻址
    .text:00402A06                 inc     ecx //ecx自减1
    .text:00402A07                 test    al, al //  al与al,判断是否al为0
    .text:00402A09                 jnz     short loc_402A04 // 若非0值,则跳转到loc_402A04处执行,即循环。
    .text:00402A0B                 xor     eax, eax // 将eax寄存器清0
    .text:00402A0D                 sub     ecx, edx // ecx中的值减去edx,将所得结果存回ecx
    .text:00402A0F                 add     ecx, 1 // ecx自加1
    .text:00402A12                 setb    al //将al寄存器置1
    .text:00402A15                 neg     eax // 求eax的补码,反码+1,符号位不变
    .text:00402A17                 or      eax, ecx //eax中的值与ecx求或,将结果存于eax中
    .text:00402A19                 push    eax             ; Size  // eax压栈 作为其后函数的参数使用
    .text:00402A1A                 call    edi ; __imp_malloc // 调malloc函数申请eax中大小的内存空间
    .text:00402A1C                 mov     edi, eax // 返回值赋给edi
    .text:00402A1E                 add     esp, 4 // 出栈4个字单元
    .text:00402A21                 mov     [ebx+8], edi //将edi中的返回值赋给ebx+8
    .text:00402A24                 test    edi, edi //测试返回值是否为0
    .text:00402A26                 jz      short loc_402A73 //如果返回值为0,ZF标志位置1,则说明申请内存空间失败,则跳转到loc_402A73子程序处,反之继续执行下去
    .text:00402A28                 push    esi             ; lpName // 一连串的压栈操作,很大概率说明有函数调用要出现了。此处压栈形参列表最后一个参数lpName
    .text:00402A29                 push    [ebp+dwMaximumSizeLow] ; dwMaximumSizeLow //此处压栈倒数第二个参数
    .text:00402A2C                 push    0               ; dwMaximumSizeHigh //此处压栈倒数第三个参数,其值为0
    .text:00402A2E                 push    4               ; flProtect //此处压栈倒数第四个参数,其值为4
    .text:00402A30                 push [ebp+lpFileMappingAttributes] ; lpFileMappingAttributes //此处压栈倒数第5个参数
    .text:00402A33                 push 0FFFFFFFFh ; hFile //此处压栈倒数第六个,也就是第一个参数,其值为-1.
    text:00402A35                  call ds:CreateFileMappingA //进入函数调用
    .text:00402A3B                 mov [ebx], eax //将返回值保存在[ebx]
    .text:00402A3D                 test eax, eax //测试返回值是否为0
    .text:00402A3F                 jz short loc_402A73 //若为0,则跳转到子函数loc_402A73处执行,反之,继续执行下去
    .text:00402A41                 push 0 ; dwNumberOfBytesToMap //后面一连串的压栈操作,与上面类似在函数调用前的压栈操作以保存传入的参数。
    .text:00402A43                 push 0 ; dwFileOffsetLow
    .text:00402A45                 push 0 ; dwFileOffsetHigh
    .text:00402A47                 push 0F001Fh ; dwDesiredAccess
    .text:00402A4C                 push eax ; hFileMappingObject
    .text:00402A4D                 call ds:MapViewOfFile//函数调用
    .text:00402A53                 mov [ebx+4], eax
    loc_402A73做的事情如以下代码所示:
    .text:00402A73 loc_402A73:                             ; CODE XREF: sub_4029E0+46↑j // 代码交叉引用在上面
    .text:00402A73                                         ; sub_4029E0+5F↑j //代码交叉引用在上面
    .text:00402A73                 push    ebx             ; Memory  //将ebx压栈
    .text:00402A74                 call    ds:__imp_free //释放申请的内存
    .text:00402A7A                 add     esp, 4 // 出栈4个字单元以平衡栈


    因此,大概可推测该函数代码片段意思,还可通过修改其值,再编译回exe文件,再通过其客户端接收程序,进一步验证一下。

    ====================》20180710

    GCC编译器堆栈保护技术:

    在进入函数时程序会启动随机生成的canary,也就是%fs:40处的8字节,将它放在当前栈帧上,退出时再与原来的canary进行比较,若不相同则说明栈溢出,类似如下所示(AT&T格式):



    关于AT&T与Intel汇编语法的不同点实例如下所示:


    GCC使用的是AT&T汇编语法。

    其他具体详情参考网页

    汇编寻址方式

    逆向基础地址

    Github地址

    展开全文
  • PL/SQL存储函数,存储过程

    千次阅读 2016-10-30 17:04:05
    存储过程和存储函数 存储过程和存储函数跟我们知道的表、视图、索引、序列、同义词等一样是我们数据中的对象。 笔记教程见:https://github.com/caojx-git/learn/blob/master/notes/oracle/PLSQL.sql 1.1什么是...
  • 常用激活函数(激励函数)理解与总结

    万次阅读 多人点赞 2018-05-13 23:07:19
    学习神经网络的时候我们总是听到激活函数这个词,而且很多资料都会提到常用的激活函数,比如Sigmoid函数、tanh函数、Relu函数。那么我们就来详细了解下激活函数方方面面的知识。本文的内容包括几个部分: 什么是...
  • 优先队列 (结构体自定义比较)(重载函数

    千次阅读 多人点赞 2018-12-23 14:10:37
    之前一直在 用 sort 的结构体自定义函数,感觉到 STL 强大,今天刷题遇见优先队列 的题 ,要求跟 以前一样,数据量大,要求对某个信息排序,并且 做相应的 操作,如果用 普通的结构体来模拟 ,但是这个sort 要每次...
  • )代码窗口:包含对象列表框、过程列表框、边界标识条、视图按钮、代码编辑区、过程分界线。     8 )立即窗口:一个重要用途是用来调试代码,想显示立即窗口,可以在视图选项卡中选择或者用快捷键 “Ctrl+G...
  • C/C++计算器(利用栈表达式求值,支持函数运算

    千次阅读 热门讨论 2017-12-13 16:22:06
    其实现思想和数据结构书上基本一致,不同的增加的函数计算,并可以扩充,利用两个栈:一个操作数栈和一个运算符栈。计算器C/C++实现代码: (开发环境:Dev-Cpp编译器)1.Express.h 代码:#include #include #...
  • 本文将为大家介绍使用函数计算部署深度学习 AI 推理的最佳实践, 其中包括使用 FUN 工具一键部署安装第三方依赖、一键部署、本地调试以及压测评估, 全方位展现函数计算的开发敏捷特性、自动弹性伸缩能力、免运维和...
  • Socket通信过程函数详解

    千次阅读 2011-06-13 19:12:00
     Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。  Socket接口设计者最先是将接口放在...
  • 在博弈树的极大极小过程中是对每一个树节点(称它为全局搜索吧)都要计算其估值,然后在这些估值中做出选择。如果搜索比较大的话,全局搜索的方式效率会非常低,因为有一些节点根本不需要搜索。那么怎么加快搜索的速度...
  • 注:对于(2),函数被赋值为函数定义的字符串,并不会对函数体中的JS代码做特殊处理(如运算等),只是将函数体JS代码的扫描结果(字符串)保存在活动对象(变量对象)的与此函数名对应的属性上,在函数执行时再做进一步...
  • 函数的重载与函数模板

    千次阅读 2017-03-16 17:24:13
    函数重载:有时候想要实现几个功能相近,但只是处理的参数类型不同的函数,在C语言里我们要把这些函数起上不同的名字,而在C++里我们可以把这些函数都给上一个函数名只需要把不同的参数给入函数就可以了,这样大大的...
  • 我们讨论的自下而上分析法是一种“移进-规约”法。大意是:用一个寄存符号的先进后出的栈,把输入符号一个一个地移进到栈里...给出输入串(a,(a,a))的算符优先分析过程。 1、扩展文法 S’ →#S# 2、求FIRSTV...
  • 分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构 Linux中创建进程一共有三个函数 fork,创建子进程 vfork,与fork类似,但是父子进程共享地址空间,而且...
  • Python--函数

    千次阅读 2016-08-29 08:54:31
    函数是对程序逻辑进行结构化或过程化的一种编程方法。其实,说简单一点,就是我们将完成某项功能的运算封装在一个单独的结构内。这样,将代码隔离成易于管理的小块,在实现大的功能时,再调用这些小块即可。很明显,...
  • 函数 函数就是一段封装好的,可以重复使用的代码,它使得我们的程序更加模块化,不需要编写大量重复的代码。 函数可以提前保存起来,并给它起一个独一无二的名字,只要知道它的名字就能使用这段代码。函数还可以接收...
  • 运算符重载 对于面向对象的程序设计来说,运算符重载可以完成两个对象之间的复杂操作...为了重载运算符,首先要定义运算符重载函数,它通常是类的非静态成员函数或者友元函数,运算符的操作数通常也应为对象。 定...
  • 一般情况下,假设当前已对前i个物品进行了某种特定的选择,且背包中已装入物品的重量是w,获得的价值是v,计算该结点的目标函数上界的一个简单方法是,将背包中剩余容量全部装入第i+1个物品,并可以将背包装满,于是...
  • 聚合函数

    2012-03-13 20:38:45
     聚合函数对一组行中的某个列执行计算执行计算并返回单一的值。聚合函数忽略空值。聚合函数经常与 SELECT 语句的 GROUP BY 子句一同使用,所以有的时候也把其称之为分组函数。 分组函数的介绍 分组函数作用于一组...
  • c语言函数详解

    千次阅读 2017-03-12 09:54:02
    程序片段(01):函数.c+call.c+测试.cpp  内容概要:函数 ///函数.c #include #include //01.函数: // 1.函数的作用:重用代码,重用功能 // 表象:代码的封装,代码的重用 // 实质:功能的封装,功能的重用 int main01...
  • 什么是函数式编程? 函数式编程是一种面向函数函数组合的编程方式。  什么是函数?从数学的角度,函数即Function,是从集合A到集合B的一种映射关系。如果集合A中的每一个元素都对应到集合B中的某一个元素,那么...
  • 什么是激活函数? 激活函数的用途(为什么需要激活函数)? 有哪些激活函数,都有什么性质和特点? 应用中如何选择合适的激活函数? 什么是激活函数? 首先要了解神经网络的基本模型。 单一神经元模型如下图所示。 ...
  • 函数式编程常用术语

    千次阅读 2017-02-11 20:56:50
    近年来函数式编程这种概念渐渐流行起来,尤其是在React/Vuejs这两个前端框架的推动下,函数式编程就像股新思潮一般瞬间席卷整个技术圈。虽然博主接触到的前端技术并不算深入,可这并不妨碍我们通过类似概念的延伸来...
  • 一种实现Win32窗口过程函数(Window Procedure)的新方法基于Thunk实现的类成员消息处理函数JERKII.SHANG (JERKII@HOTMAIL.COM)MAR.10th - 31st, 2006Windows是一个消息驱动的操作系统,在系统中发生的所有消息均...
  • 在上一节的线性回归的例子中,我们通过一定的矩阵运算获得了每张图像的最终得分(如下图),可以看到,这些得分有些是比较好的预测,有些是比较差的预测,那么,具体如何定义“好”与“差”呢?这就需要引入“损失...
  • Python 函数

    千次阅读 2013-03-01 01:38:58
    Python 第四部分 函数 目录 第15章 函数基础... 3 函数作用... 3 Def语句是实时执行的... 3 Python 中的多态... 4 第二个例子,寻找序列的交集... 4 什么时候python将会创建函数?. 5 检查传入函数的...
  • SQL Server~函数

    千次阅读 2019-02-22 16:58:19
    聚合函数对一组值进行计算并返回单一的值,通常聚合函数会与SELECT语句的GROUP BY子句一同使用,在与GROUP BY子句使用时,聚合函数会为每一个组产生一个单一值,而不会为整个表产生一个单一值。常用的聚合函数及说明...
  • JDK8函数式接口

    千次阅读 2019-05-12 11:54:34
    Supplier函数式接口 Consumer函数式接口 Function函数式接口 Predicate函数式接口

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 142,021
精华内容 56,808
关键字:

优先函数的计算过程