新手简单入门逆向-消除对汇编的恐惧

最近这几天一直在看一本电子书,看雪的0day安全:软件漏洞分析技术

内容灵感来自:第一篇,第一章节最后一节1.4小实验

#include <stdio.h>
#include <string.h>
#define PASSWORD "1234567"
// 验证输入的密码是否正确
int verify_password(char* password) {
    int authenticated;
    authenticated = strcmp(password, PASSWORD);
    return authenticated;
}
int main() {
    int valid_flag = 0;
    char password[1024];

    while (1) {
        printf("please input password: ");
        scanf_s("%s", password);

        valid_flag = verify_password(password);

        if (valid_flag) {
            printf("incorrect password!\n\n");
        }
        else {
            printf("Congratulation! You have passed the verification!\n");
            break;
        }
    }

    return 0;
}

这是书中给到的源码

编译前要关闭vs的内存地址随机化

环境:vs 2022 release 32位程序

IDA反编译后伪代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int v4; // eax
  char password[1024]; // [esp+0h] [ebp-400h] BYREF

  printf("please input password: ");
  scanf_s("%s", password);
  v3 = strcmp(password, "1234567");
  if ( v3 )
    v3 = v3 < 0 ? -1 : 1;
  if ( v3 )
  {
    do
    {
      printf("incorrect password!\n\n");
      printf("please input password: ");
      scanf_s("%s", password);
      v4 = strcmp(password, "1234567");
      if ( v4 )
        v4 = v4 < 0 ? -1 : 1;
    }
    while ( v4 );
  }
  printf("Congratulation! You have passed the verification!\n");
  return 0;
}

看到这里进行了两次判断,第一次输入v3进行判断,第二次输入v4进行判断

下面介绍一下伪代码中的迷惑点

在v3和v4的strcmp函数下面会有这样的代码

  if ( v3 )
    v3 = v3 < 0 ? -1 : 1;

这个其实就是strcmp函数的返回值,可以不用管它

strcmp函数介绍
功能:从左至右逐个对字符串 s1 和 s2 中的字符按 ASCII 值大小进行比较 ,直到出现不同字符或遇 '\0' 为止。
返回值:
若 s1 等于 s2 ,返回 0。
若 s1 小于 s2 ,返回负数。
若 s1 大于 s2 ,返回正数。

伪代码的逻辑大致如下:

  1. 输入password,通过strcmp函数来判断。然后将返回的结果返还给v3

  2. if 判断v3的值,如果不是等于0(那就是密码输入错误了)就会执行if内的内容,如果等于零直接执行 printf("Congratulation! You have passed the verification!\n"); 那很不错了(๑•̀ㅂ•́)و✧

  3. 假如输入错误,进入到if体内,先给我们打印出密码输入错误,然后重新提示我们输入密码。

  4. 这次重新输入password,使用strcmp来比较,将返回结果赋给v4。这里有意思的是,最后没有if判断v4是否等于零,直接用do while来判断v4是否等于零。如果v4等于零,那么直接跳出循环并打印Congratulation! You have passed the verification!,如果v4不等于0(负数也包含在内)那么就会一直在do while循环中徘徊直到我们输入的password正确。

流程图分析1

由于流程图太多太长,这里就不一一展示了,只挑重点的说

loc_4010E7 这个就是我们第一个if,也就是判断我们v3的if。

这里再介绍两个汇编指令:

test:指令用于对两个操作数进行按位逻辑与运算,但不保存运算结果,仅根据运算结果设置标志寄存器
​
jz条件跳转指令,全称为 “Jump if Zero”,即当零标志位(ZF)为 1 时,程序会跳转到指定的目标地址执行;若零标志位(ZF)为 0,则顺序执行下一条指令。

这个是来判断我们v3的值是否为0,如果我们v3的值为0的话就会执行jz跳转指令,直接跳到密码正确那里。

test eax,eax 来判断eax的值是否为0,如果为0则执行jz跳转,不为零则向右侧执行

破解点1

在test eax,eax指令这里按下空格

记住这个内存地址 4010E7

然后我们将程序拖入到x32dbg中

Ctrl+G 打开查询框,输入我们内存地址4010E7,然后跳转到那里

将test eax,eax的值修改为xor eax,eax 这样一来ZF标志位肯定为1了

xor eax, eax 是将 eax 寄存器的值与自身进行按位异或运算。由于 eax 中的每一位都与自身相同,根据按位异或运算规则,每一位的运算结果都为 0。
由于 xor eax, eax 的运算结果为 0,根据 ZF 的设置规则,零标志位(ZF)会被置为 1。

所以下面的那条jz指令就会自动变成了je指令

je指令
​
je 是 “Jump if Equal” 的缩写,也就是当零标志位(ZF)的值为 1 时,程序就会跳转到指定的目标地址继续执行;若零标志位(ZF)为 0,程序则会按顺序执行下一条指令。

这样,我们就算破解完成了

然后导出看下成果

正确密码是1234567,但是这里输入1234就提示正确 (๑•̀ㅂ•́)و✧

流程图分析2

这个跟上一个类似,但是这个是do while来判断是否为0的,上面那个是if

jnz指令
jnz 是 “Jump if Not Zero” 的缩写,即当零标志位(ZF)的值为 0 时,程序会跳转到指定的目标地址继续执行;若零标志位(ZF)为 1,则程序会顺序执行下一条指令。

这里可能有点乱,我来梳理一下下

jnz指令,是0标志位ZF为0的时候 (也就是v4不是0的时候)发生跳转
jz指令,是0标志位ZF为1的时候(也就是v3是0的时候)发生跳转

我这里发懒了,直接nop掉了

原样

后者

直接给你跳转nop掉,让你啥也不是,然后保存补丁

依旧可以,这里为什么第一遍会错呢?

这个程序至少有两次判断,第一个判断是通过判断v3,第二个是判断v4的。我们改的是v4那个while判断所以第一遍输入v3的内容时候自然会提示密码错误,但是第二遍输入的时候就提示密码正确了

正确密码依旧是:1234567

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值