基础知识


虚拟内存

Windows 的内存可以被分为两个层面:物理内存和虚拟内存

  • 物理内存:相对复杂,需要进入ring0才能看到
  • 虚拟内存: 用户模式下,调试器看到的地址都是虚拟内存

1.内存管理器只是分给进程一片“假地址”,只有当进程实际的内存操作时,内存管理器才会把“假地址”和物理地址联系起来

2.操作系统原理中也有“虚拟内存”的概念,那是指实际的物理内存不够时,有时操作系统会把“部分硬盘空间”当作当作内存来使用从而使程序得到装载运行的现象

几个地址

  • 文件偏移地址(File Offset):数据在PE文件中的地址
  • 装载基址(Image Base):PE装入内存的基址(EXE一般是0x00400000, DLL则一般为0x10000000)
  • 虚拟内存地址(Virtual Address, VA):PE文件中的指令装入内存后的地址
  • 相对虚拟地址(Relative Virtual Address, RVA):内存地址相对映射基址的偏移量

VA = Image Base + RVA

  • 文件偏移地址计算方法:

          文件偏移地址 =  虚拟内存地址(VA) - 装载地址(Image Base) - 节偏移
                      = RVA - 节偏移
    

    其中,节偏移 = Voffset - Roffset

小例子-crack

书中给了一个段代码示例:

#include <stdio.h>

#define PASSWORD "123456"

int verify_password(char *password)
{
    int authenticated;
    authenticated = strcmp(password, PASSWORD);
    return authenticated;
}

int main(void)
{
    int valid_flag = 0;
    char password[1024];
    while(1){
        printf("please input password:  ");
        scanf("%s", password);
        valid_flag = verify_password(password);
        if(valid_flag)
            printf("incorrect password!\n\n");
        else{
            printf("Congratulation! You have passed verification!\n");
            break;
        }
    }
    return 0;
}

从源码可以看到,只有密码输入为123456时程序才会执行结束。编译一下:

gcc -m32 -o crack crack.c

得到的程序运行发现与预期一致:

Alt

载入IDA查看程序流程:

Alt

可以发现,这个流程还是很简洁的,到判断代码处:

.text:00401479                 mov     [esp+41Ch], eax
.text:00401480                 cmp     dword ptr [esp+41Ch], 0
.text:00401488                 jz      short loc_401498
.text:0040148A                 mov     dword ptr [esp], offset aIncorrectPassw ; "incorrect password!\n"
.text:00401491                 call    _puts
.text:00401496                 jmp     short loc_40144D
.text:00401498 ; ---------------------------------------------------------------------------
.text:00401498
.text:00401498 loc_401498:                             ; CODE XREF: _main+57j
.text:00401498                 mov     dword ptr [esp], offset aCongratulation ; "Congratulation! You have passed verific"...
.text:0040149F                 call    _puts
.text:004014A4                 nop
.text:004014A5                 mov     eax, 0
.text:004014AA                 leave
.text:004014AB                 retn

发现在地址0x00401488(VA)处有个跳转,在OD中下个断点运行到此处,输入任意串:

Alt

因为这里我们需要让跳转实现,所以将je改为jne(可以发现对应机器码为75):

Alt Alt

接着运行就可看到想要的结果了:

Alt

当然这里还可以修改z标志寄存器来达到目的,要达到长期有效的目的还可通过OD或者IDA将改写的内容写入exe文件。此处通过计算文件偏移地址的方法在诸如winhex的编辑器中修改数据达到永久修改的目的。

首先,通过LordPE得到相关值:

Alt

这里带入公式可以计算得到文件偏移地址为0x888:

0x00401488 - 0x00400000 - (0x00001000 - 0x00000400) = 0x88

或者直接通过lordPE计算:

Alt

得到文件偏移地址之后,就可以在winhex里面修改关键数据了:

Alt

之后运行程序,发现只有密码不正确时才会输出对应的结果:

Alt