逆向练习3

每日逆向的理解和积累。

汇编阅读练习

练习1-1

1.汇编代码及解释如下:

  • main函数

  • .data段上定义的num变量:

2.对应c语言源码如下:

3.该程序并未开启canary保护机制。在该程序中主要注意源码中红框框起来的num变量在汇编层面的定义以及赋值变化。可与练习2中的5-1进行比较,注意static变量和auto变量的区别。

练习1-2

1.汇编代码及解释如下:

  • main函数

2.对应c语言源码如下:

3.补充:该程序并未开启canary保护机制。

练习2

1.汇编代码及解释如下:

  • main函数

  • deleteCharacters函数

2.对应c语言源码如下:

3.补充1:该程序开启了canary保护,故验证了之前的猜测-涉及到字符串处理的程序也会开启保护。本题中比较有意思的是判断字符串中各个字符是否是要删除的字符这一操作在汇编层面上的实现。

4.补充2:一般涉及到字符串处理的函数,其中对字符串的引用可能会用字符指针变量,所以如果存放值为地址值的变量,可猜测其为指针变量。

5.补充3:64位汇编参数传递——当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9; 当参数为7个以上时, 前6个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和32位汇编一样。

参数个数大于 7 个的时候
H(a, b, c, d, e, f, g, h);
a->%rdi, b->%rsi, c->%rdx, d->%rcx, e->%r8, f->%r9
h->8(%esp)
g->(%esp)
call H

6.补充4:x86_64寄存器如下图。

寄存器特性表如下图。

特性要点如下——

常用寄存器有16个,分为x86通用寄存器以及r8-r15寄存器。
通用寄存器中,函数执行前后必须保持原始的寄存器有3个:是rbx、rbp、rsp。rx寄存器中,最后4个必须保持原值:r12、r13、r14、r15。 保持原值的意义是为了让当前函数有可信任的寄存器,减小在函数调用过程中的保存&恢复操作。除了rbp、rsp用于特定用途外,其余5个寄存器可随意使用。
通用寄存器中,不必假设保存值可随意使用的寄存器有5个:是rax、rcx、rdx、rdi、rsi。其中rax用于第一个返回寄存器(当 然也可以用于其它用途),rdx用于第二个返回寄存器(在调用函数时也用于第三个参数寄存器)。rcx用于第四个参数。rdi用于第一个参数。rsi用于第二个函数参数。
r8、r9分配用于第5、第6个参数

练习3

1.汇编代码及解释如下:

  • main函数

  • sieve函数

  • isPrime函数

  • isPrimeNaive函数

  • isPrimeSieve函数

2.对应c语言源码如下:

#include<stdio.h>
#include<math.h>
#define MAX 1000
int prime[MAX];
int isPrimeNaive(int n)
{
    if(n <= 1)
        return 0;
    for(int i = 2; i < n; i++)
        if(n % i == 0)
            return 0;
    return 1;
}

int isPrime(int n)
{
    if(n<= 1)
        return 0;
    if(n == 2)
        return 1;
    if(n%2 == 0)
        return 0;
    int limit = (int)sqrt((double)n);
    for(int i = 3; i <= limit; i=i+2)
    {
        if(n % i == 0)
            return 0;
    }
    return 1;
}

void sieve()
{
    prime[0] = 0;
    prime[1] = 0;
    for(int i = 2; i < MAX; i++)
        prime[i] = 1;
    int limit = (int)sqrt((double)MAX);
    for(int i = 2; i <= limit; i++)
    {
        if(prime[i])
            for(int j = i*i; j <= MAX; j+=i)
                prime[j] = 0;
    }
}

int isPrimeSieve(int n)
{
    if(prime[n])
        return 1;
    else
        return 0;
}

int main()
{
    sieve();
    printf("N=%d %d\n", 1, isPrime(1));
    printf("N=%d %d\n", 2, isPrime(2));
    printf("N=%d %d\n", 3, isPrime(3));
    printf("N=%d %d\n", 4, isPrime(4));
    printf("N=%d %d\n", 7, isPrime(7));
    printf("N=%d %d\n", 9, isPrime(9));
    printf("N=%d %d\n", 13, isPrime(13));
    printf("N=%d %d\n", 17, isPrime(17));
    printf("N=%d %d\n", 100, isPrime(100));
    printf("N=%d %d\n", 23, isPrime(23));
    printf("N=%d %d\n", 1, isPrime(1));
    return 0;
}

3.补充1:该程序没有开启canary保护。

4.补充2:数据段.data.bss的地址是向上增长的(即向高地址增长),与栈地址相反。.data.bss的区别在于——

全局的未初始化变量存在于.bss段中,具体体现为一个占位符;
全局的已初始化变量存于.data段中;
函数内的自动变量都在栈上分配空间;
.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);
而.data却需要占用,其内容由程序初始化。

练习4

1.汇编代码及解释如下:

  • main函数

2.对应c语言源码如下:

3.补充:该程序并未开启canary保护机制。

练习5

1.汇编代码及解释如下:

  • main函数

2.对应c语言源码如下:

main.c

#include "test.h"  
#include <stdio.h>

int main()
{
    int i=10;
    int j=20;
    if(i LAG j)
        printf("%d 大于 %d \n",i,j);
    else if(i EQ j)
        printf("%d 等于 %d \n",i,j);
    else if(i SMA j)
        printf("%d 小于 %d \n",i,j);
    else
        printf("没有值。\n");
    return 0;
}

test.h

#define LAG >
#define SMA <
#define EQ ==

3.补充:该程序并未开启canary保护机制。