From 6f153688ffe0b7ba8eae893b8d10c265bb351aab Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Wed, 8 Jul 2020 14:39:00 -0400 Subject: [PATCH] Add labels to shuffled assembly source code for post-processing --- data/shellcode/block_api.x86.graphml | 655 ++++++++++-------- lib/msf/core/payload/windows/block_api.rb | 1 + lib/msf/core/payload/windows/x64/block_api.rb | 1 + lib/rex/payloads/shuffle.rb | 72 +- 4 files changed, 401 insertions(+), 328 deletions(-) diff --git a/data/shellcode/block_api.x86.graphml b/data/shellcode/block_api.x86.graphml index f9b0949b67..4779c0c569 100644 --- a/data/shellcode/block_api.x86.graphml +++ b/data/shellcode/block_api.x86.graphml @@ -26,14 +26,14 @@ 0x1003 instruction - 31c0 - xor eax, eax + 31d2 + xor edx, edx 0x1005 instruction - 648b5030 - mov edx, dword ptr fs:[eax + 0x30] + 648b5230 + mov edx, dword ptr fs:[edx + 0x30] 0x1009 @@ -49,7 +49,6 @@ - @@ -90,37 +89,30 @@ 0x1018 instruction + 31c0 + xor eax, eax + + + 0x101a + instruction ac lodsb al, byte ptr [esi] - - 0x1019 - instruction - 3c61 - cmp al, 0x61 - 0x101b instruction - 7c02 - jl 0x101f + 3c61 + cmp al, 0x61 - - - - - - 0x101d - block - - 0x101d - block - + 0x101d instruction - 2c20 - sub al, 0x20 + 7c02 + jl 0x1021 + + + @@ -132,167 +124,172 @@ 0x101f instruction + 2c20 + sub al, 0x20 + + + + + 0x1021 + block + + 0x1021 + block + + 0x1021 + instruction c1cf0d ror edi, 0xd - - 0x1022 + + 0x1024 instruction 01c7 add edi, eax - - 0x1024 - instruction - e2f2 - loop 0x1018 - - - - - - - 0x1026 - block - - 0x1026 - block - + 0x1026 instruction - 52 - push edx - - - 0x1027 - instruction - 57 - push edi - - - 0x1028 - instruction - 8b5210 - mov edx, dword ptr [edx + 0x10] - - - 0x102b - instruction - 8b4a3c - mov ecx, dword ptr [edx + 0x3c] - - - 0x102e - instruction - 8b4c1178 - mov ecx, dword ptr [ecx + edx + 0x78] - - - 0x1032 - instruction - e348 - jecxz 0x107c - - - - - - - - - - - - 0x1034 - block - - 0x1034 - block - - 0x1034 - instruction - 01d1 - add ecx, edx - - - 0x1036 - instruction - 51 - push ecx - - - 0x1037 - instruction - 8b5920 - mov ebx, dword ptr [ecx + 0x20] - - - 0x103a - instruction - 01d3 - add ebx, edx - - - 0x103c - instruction - 8b4918 - mov ecx, dword ptr [ecx + 0x18] - - - - - - - - - - - 0x103f - block - - 0x103f - block - - 0x103f - instruction - e33a - jecxz 0x107b - - - - - 0x1041 - block - - 0x1041 - block - - 0x1041 - instruction 49 dec ecx - - 0x1042 + + 0x1027 instruction - 8b348b - mov esi, dword ptr [ebx + ecx*4] + 75ef + jne 0x1018 - + + + + + + + 0x1029 + block + + 0x1029 + block + + 0x1029 + instruction + 52 + push edx + + + 0x102a + instruction + 57 + push edi + + + 0x102b + instruction + 8b5210 + mov edx, dword ptr [edx + 0x10] + + + 0x102e + instruction + 8b423c + mov eax, dword ptr [edx + 0x3c] + + + 0x1031 + instruction + 01d0 + add eax, edx + + + 0x1033 + instruction + 8b4078 + mov eax, dword ptr [eax + 0x78] + + + 0x1036 + instruction + 85c0 + test eax, eax + + + 0x1038 + instruction + 744c + je 0x1086 + + + + + + + + + + + + + + 0x103a + block + + 0x103a + block + + 0x103a + instruction + 01d0 + add eax, edx + + + 0x103c + instruction + 50 + push eax + + + 0x103d + instruction + 8b4818 + mov ecx, dword ptr [eax + 0x18] + + + 0x1040 + instruction + 8b5820 + mov ebx, dword ptr [eax + 0x20] + + + 0x1043 + instruction + 01d3 + add ebx, edx + + + + + + + + + 0x1045 + block + + 0x1045 + block + 0x1045 instruction - 01d6 - add esi, edx + 85c9 + test ecx, ecx - + 0x1047 instruction - 31ff - xor edi, edi + 743c + je 0x1085 - - - + @@ -304,271 +301,315 @@ 0x1049 instruction - ac - lodsb al, byte ptr [esi] + 49 + dec ecx 0x104a instruction - c1cf0d - ror edi, 0xd + 8b348b + mov esi, dword ptr [ebx + ecx*4] 0x104d instruction - 01c7 - add edi, eax + 01d6 + add esi, edx 0x104f instruction + 31ff + xor edi, edi + + + + + + + + 0x1051 + block + + 0x1051 + block + + 0x1051 + instruction + 31c0 + xor eax, eax + + + 0x1053 + instruction + ac + lodsb al, byte ptr [esi] + + + 0x1054 + instruction + c1cf0d + ror edi, 0xd + + + 0x1057 + instruction + 01c7 + add edi, eax + + + 0x1059 + instruction 38e0 cmp al, ah - - 0x1051 + + 0x105b instruction - 75f6 - jne 0x1049 + 75f4 + jne 0x1051 - - - - - + + + + + + + + - - 0x1053 + + 0x105d block - 0x1053 + 0x105d block - - 0x1053 + + 0x105d instruction 037df8 add edi, dword ptr [ebp - 8] - - 0x1056 + + 0x1060 instruction 3b7d24 cmp edi, dword ptr [ebp + 0x24] - - 0x1059 + + 0x1063 instruction - 75e4 - jne 0x103f + 75e0 + jne 0x1045 - - + + - - 0x105b + + 0x1065 block - 0x105b + 0x1065 block - - 0x105b + + 0x1065 instruction 58 pop eax - - 0x105c + + 0x1066 instruction 8b5824 mov ebx, dword ptr [eax + 0x24] - - 0x105f + + 0x1069 instruction 01d3 add ebx, edx - - 0x1061 + + 0x106b instruction 668b0c4b mov cx, word ptr [ebx + ecx*2] - - 0x1065 + + 0x106f instruction 8b581c mov ebx, dword ptr [eax + 0x1c] - - 0x1068 + + 0x1072 instruction 01d3 add ebx, edx - - 0x106a + + 0x1074 instruction 8b048b mov eax, dword ptr [ebx + ecx*4] - - 0x106d + + 0x1077 instruction 01d0 add eax, edx - - 0x106f + + 0x1079 instruction 89442424 mov dword ptr [esp + 0x24], eax - - 0x1073 + + 0x107d instruction 5b pop ebx - - 0x1074 + + 0x107e instruction 5b pop ebx - - 0x1075 + + 0x107f instruction 61 popal - - 0x1076 + + 0x1080 instruction 59 pop ecx - - 0x1077 + + 0x1081 instruction 5a pop edx - - 0x1078 + + 0x1082 instruction 51 push ecx - - 0x1079 + + 0x1083 instruction ffe0 jmp eax - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - 0x107b + + 0x1085 block - 0x107b + 0x1085 block - - 0x107b + + 0x1085 + instruction + 58 + pop eax + + + + + 0x1086 + block + + 0x1086 + block + + 0x1086 instruction 5f pop edi - - - - 0x107c - block - - 0x107c - block - - 0x107c - instruction - 5f - pop edi - - - 0x107d + + 0x1087 instruction 5a pop edx - - 0x107e + + 0x1088 instruction 8b12 mov edx, dword ptr [edx] - - 0x1080 + + 0x108a instruction - eb8d + eb83 jmp 0x100f - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/lib/msf/core/payload/windows/block_api.rb b/lib/msf/core/payload/windows/block_api.rb index ab108c6b9f..fa7a74a4f8 100644 --- a/lib/msf/core/payload/windows/block_api.rb +++ b/lib/msf/core/payload/windows/block_api.rb @@ -15,6 +15,7 @@ module Payload::Windows::BlockApi def asm_block_api(opts={}) Rex::Payloads::Shuffle.from_graphml_file( File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'), + arch: ARCH_X86, name: 'api_call' ) end diff --git a/lib/msf/core/payload/windows/x64/block_api.rb b/lib/msf/core/payload/windows/x64/block_api.rb index ca5f36e67a..ec58e0e997 100644 --- a/lib/msf/core/payload/windows/x64/block_api.rb +++ b/lib/msf/core/payload/windows/x64/block_api.rb @@ -15,6 +15,7 @@ module Payload::Windows::BlockApi_x64 def asm_block_api(opts={}) Rex::Payloads::Shuffle.from_graphml_file( File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x64.graphml'), + arch: ARCH_X64, name: 'api_call' ) end diff --git a/lib/rex/payloads/shuffle.rb b/lib/rex/payloads/shuffle.rb index 3bbff16f64..8c9e9bae69 100644 --- a/lib/rex/payloads/shuffle.rb +++ b/lib/rex/payloads/shuffle.rb @@ -8,38 +8,67 @@ require 'rex/parser/graphml' ## module Rex module Payloads - module Shuffle + class Shuffle + + FLOW_INSTRUCTIONS = {} + FLOW_INSTRUCTIONS[ARCH_X86] = %w{ call jae jb jbe jc jcxz je jecxz jg jge jl jle jmp jna jnae jnb jnbe jnc jne jng jnge jnl jnle jno jnp jns jnz jo jp jpe jpo js jz }.freeze + FLOW_INSTRUCTIONS[ARCH_X64] = (FLOW_INSTRUCTIONS[ARCH_X86] + %w{ jrcxz }).freeze # - # Shuffle instructions from a GraphML data file. + # Shuffle instructions from a GraphML data file and return the assembly source. If an architecture is specified + # and supported, labels will be added for control flow instructions such as jumps and calls. Labels are necessary + # if any post processing is performed on the source (such as for obfuscation). # # @param file_path [String] The file path to load the GraphML data from. # @param name [String] An optional symbol name to apply to the assembly source. - def self.from_graphml_file(file_path, name: nil) + def self.from_graphml_file(file_path, arch: nil, name: nil) graphml = Rex::Parser::GraphML.from_file(file_path) - instructions = self.shuffle_instructions(graphml) - instructions = (["#{name}:"] + instructions.map { |instruction| ' ' + instruction}) unless name.nil? - instructions.join("\n") + "\n" - end - - # - # Load constraint information from GraphML data and then use it to shuffle the instructions. Constraints are - # expected to be identified as edges between instruction nodes that define ordering precedence. Each instruction - # node must specify it's instruction source and binary representation (encoded in hex). - # - # @param graphml [Rex::Parser::GraphML::Element::GraphML] The graph to load the instruction data from. - # @return [Array] The array of assembly instructions. - def self.shuffle_instructions(graphml) - # build an array of all of the graphs representing basic blocks, sorted by their address blocks = graphml.graphs.filter { |graph| graph.attributes['type'] == 'block' }.sort_by { |graph| graph.attributes['address'] } - blocks.map { |block| self.process_block(block) }.flatten + blocks.map! { |block| { node: block, instructions: self.process_block(block) } } + + label_prefix = Rex::Text.rand_text_alpha_lower(4) + labeler = lambda { |address| "loc_#{label_prefix}#{ address.to_s(16).rjust(4, '0') }" } + + source_lines = [] + labeled = [] + label_refs = [] + blocks.each do |block| + source_lines << labeler.call(block[:node].attributes['address']) + ':' + labeled << block[:node].attributes['address'] + # by default use the raw binary instruction to avoid syntax compatibility issues with metasm + instructions = block[:instructions].map { |node| 'db ' + node.attributes['instruction.hex'].strip.chars.each_slice(2).map { |hex| '0x' + hex.join }.join(', ') } + unless arch.nil? + raise ArgumentError, 'Unsupported architecture' if FLOW_INSTRUCTIONS[arch].nil? + + # if a supported architecture was specified, use the original source and apply the necessary labels + block[:instructions].each_with_index do |node, index| + next unless match = /^(?\S+)\s+(?
0x[a-f0-9]+)$/.match(node.attributes['instruction.source']) + next unless FLOW_INSTRUCTIONS[arch].include? match[:mnemonic] + + address = Integer(match[:address]) + instructions[index] = "#{match[:mnemonic]} #{labeler.call(address)}" + label_refs << address + end + end + + source_lines += instructions + end + + unless label_refs.all? { |address| labeled.include? address } + # raise this here so it's closer to the source of the problem :( + raise StandardError, 'Missing label reference' + end + + + source_lines = ([name + ':'] + source_lines.map { |source_line| ' ' + source_line}) unless name.nil? + source_lines.join("\n") + "\n" end private # - # Process the specified graph element which represents a single basic block in assembly. This graph element contains - # nodes representing each of its instructions. + # Process the specified graph element which represents a single basic block in assembly. This graph element + # contains nodes representing each of its instructions. # def self.process_block(block) path = [] @@ -63,8 +92,9 @@ module Rex end end - path.map { |node| 'db ' + node.attributes['instruction.hex'].strip.chars.each_slice(2).map { |hex| '0x' + hex.join }.join(', ') } + path end + end end end