├── .gitignore ├── EVBE ├── EVBContainer.py ├── EVBDataParser.py ├── EVBFile.py ├── EVBFileSystem.py ├── EVBRegistry.py └── __init__.py ├── LICENSE ├── README.md ├── aplib.py └── evbe.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.pyc 3 | *.zip -------------------------------------------------------------------------------- /EVBE/EVBContainer.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | """ 3 | Copyright (C) 2013 Roman Bondarenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | from EVBE.EVBDataParser import DataParser 24 | from EVBE.EVBFileSystem import FileSystem 25 | 26 | __author__ = 'Roman Bondarenko' 27 | __contact__ = 'roman@reu.org.ua' 28 | 29 | 30 | HEADER_STRUCT = [ 31 | ('I', ''), # some value 32 | ('I', ''), # some value (maybe size or address) 33 | ('I', ''), # some value 34 | ('I', ''), # some value 35 | ('I', ''), # some value 36 | ('I', ''), # some value 37 | ('I', ''), # some value 38 | ('I', ''), # some value (maybe size) 39 | ('I', ''), # some value 40 | ('I', ''), # some value (some size) 41 | ('B', 'compress_files'), # Compress Files 42 | ('B', 'delete_on_exit'), # Delete Extracted On Exit 43 | ('H', ''), # some value 44 | ('I', ''), # some value 45 | ('I', ''), # some value 46 | ('I', ''), # some value 47 | ('I', ''), # some value 48 | ('I', ''), # some value 49 | ('Q', ''), # some value 50 | ('B', 'files_virtualization'), # Enable Files Virtualization 51 | ('B', 'registry_virtualization'), # Enable Registry Virtualization 52 | ('B', 'share_virtual_system'), # Share virtual system to child processes 53 | ('B', 'using_temp_files'), # Map executable files using temporary file 54 | ('B', 'allow_running'), # Allow running of virtual executable files 55 | ('B', ''), # 56 | ('B', ''), # 57 | ('B', ''), # 58 | ('B', ''), # 59 | ('B', ''), # 60 | ('B', ''), # 61 | ('B', ''), # 62 | ('I', ''), # 63 | ('4s', 'signature'), # EVB signature ('EVB\x00') 64 | ('I', ''), # some value 65 | ('Q', 'env_value'), # environment value 66 | ('I', ''), # some value 67 | ('I', ''), # some value 68 | ('I', ''), # some value 69 | ('I', ''), # some value 70 | ('I', 'data_size'), # size of data 71 | ('I', ''), # some value 72 | ('I', ''), # some value 73 | ('I', ''), # some value 74 | ('I', ''), # some size 75 | ('I', ''), # some value 76 | ('I', 'registry_size'), # size of registry data 77 | ('I', ''), # some value 78 | ] 79 | 80 | 81 | class Container(object): 82 | class Header(object): 83 | signature = None 84 | files_virtualization = 0 85 | compress_files = 0 86 | delete_on_exit = 0 87 | registry_virtualization = 0 88 | share_virtual_system = 0 89 | using_temp_files = 0 90 | allow_running = 0 91 | data_size = 0 92 | registry_size = 0 93 | 94 | def __init__(self, data, offset=0): 95 | self.data = data 96 | self.offset = offset 97 | self.header = self.Header 98 | 99 | def read_header(self): 100 | if len(self.data) < 0x98: 101 | print('Header is too small') 102 | return False 103 | 104 | DataParser(self.header, self.data, HEADER_STRUCT, self.offset) 105 | 106 | if self.header.signature != 'EVB\x00': 107 | print('Signature not found') 108 | return False 109 | 110 | self.offset += 0x98 111 | return True 112 | 113 | def read_data(self): 114 | if self.header.files_virtualization: 115 | fs = FileSystem(self.data, self.header.data_size, self.offset) 116 | fs.enum() 117 | return fs.files 118 | return [] 119 | 120 | def info(self): 121 | s = ''' 122 | Enable Files Virtualization: %s 123 | Compress Files: %s 124 | Delete Extracted On Exit: %s 125 | Enable Registry Virtualization: %s 126 | Share virtual system to child processes: %s 127 | Map executable files using temporary file: %s 128 | Allow running of virtual executable files: %s 129 | 130 | Size of file data: %d 131 | Size of registry data %d 132 | ''' 133 | u = lambda i: 'Disabled' if i == 0 else 'Unknown value: %d' % i 134 | f = lambda i: 'Enabled' if i == 1 else u(i) 135 | print(s % 136 | (f(self.header.files_virtualization), f(self.header.compress_files), f(self.header.delete_on_exit), 137 | f(self.header.registry_virtualization), f(self.header.share_virtual_system), 138 | f(self.header.using_temp_files), f(self.header.allow_running), 139 | self.header.data_size, self.header.registry_size)) -------------------------------------------------------------------------------- /EVBE/EVBDataParser.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | """ 3 | Copyright (C) 2013 Roman Bondarenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | from struct import unpack_from 24 | 25 | __author__ = 'Roman Bondarenko' 26 | __contact__ = 'roman@reu.org.ua' 27 | 28 | 29 | class DataParser(object): 30 | def __init__(self, obj, data, fmt, offset=0, str_size=-1): 31 | format_ = '<' + ''.join([f for f, _ in fmt]) 32 | if str_size >= 0: 33 | format_ = format_ % str_size 34 | unpacked = unpack_from(format_, data, offset) 35 | for i in range(len(fmt)): 36 | name = fmt[i][1] 37 | if name == '': 38 | name = 'some_value_%d' % i 39 | setattr(obj, name, unpacked[i]) -------------------------------------------------------------------------------- /EVBE/EVBFile.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | """ 3 | Copyright (C) 2013 Roman Bondarenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | import os 24 | try: 25 | from pefile import PE 26 | except ImportError: 27 | raise ImportError('You need install pefile library http://code.google.com/p/pefile') 28 | 29 | __author__ = 'Roman Bondarenko' 30 | __contact__ = 'roman@reu.org.ua' 31 | 32 | 33 | class EVBFile(object): 34 | def __init__(self, name): 35 | self.name = name 36 | self.data = None 37 | self.offset = 0x48 38 | 39 | # TODO: Add generic search for container 40 | def read(self): 41 | try: 42 | pe = PE(self.name, fast_load=True) 43 | except: 44 | print('File %s invalid' % self.name) 45 | return False 46 | 47 | if not pe.is_exe(): 48 | print('This file is not exe') 49 | pe.close() 50 | return False 51 | 52 | section = None 53 | for s in pe.sections: 54 | if s.Name == '.enigma1': 55 | section = s 56 | break 57 | 58 | if section is None: 59 | print('This file is not Enigma Virtual Box container') 60 | pe.close() 61 | return False 62 | 63 | self.data = pe.get_data(section.VirtualAddress, section.SizeOfRawData) 64 | 65 | pe.close() 66 | 67 | return True -------------------------------------------------------------------------------- /EVBE/EVBFileSystem.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | """ 3 | Copyright (C) 2013 Roman Bondarenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | import os 24 | from EVBE.EVBDataParser import DataParser 25 | 26 | __author__ = 'Roman Bondarenko' 27 | __contact__ = 'roman@reu.org.ua' 28 | 29 | from struct import unpack_from 30 | 31 | NODE = [ 32 | ('I', 'size'), 33 | ('I', ''), 34 | ('I', ''), 35 | ('I', 'objects_count'), 36 | ] 37 | 38 | NAMED_NODE = [ 39 | ('%ds', 'name'), 40 | ('H', ''), 41 | ('B', 'type'), 42 | ] 43 | 44 | OPTIONAL = [ 45 | ('H', ''), 46 | ('I', 'original_size'), 47 | ('I', ''), 48 | ('Q', ''), 49 | ('Q', ''), 50 | ('Q', ''), 51 | ('B', ''), 52 | ('H', ''), 53 | ('I', ''), 54 | ('I', 'stored_size'), 55 | ('I', ''), 56 | ] 57 | 58 | 59 | class FileSystem(object): 60 | """ 61 | Parse virtual file system 62 | """ 63 | class Node(object): 64 | """ 65 | Directory or file 66 | """ 67 | size = 0 68 | objects_count = 0 69 | name = None 70 | type = 0 71 | stored_size = 0 72 | original_size = 0 73 | name_exist = 0 74 | 75 | def __init__(self, data, offset): 76 | DataParser(self, data, NODE, offset) 77 | i = 0 78 | while unpack_from('> 7) & 1 45 | self.tag <<= 1 46 | return bit 47 | 48 | def getgamma(self): 49 | result = 1 50 | while True: 51 | result = (result << 1) + self.getbit() 52 | if self.getbit() == 0: 53 | break 54 | return result 55 | 56 | def depack(self): 57 | offs = 0 58 | len_ = 0 59 | R0 = 0 60 | done = 0 61 | LWM = 0 62 | 63 | destination = self.source[self.offset] 64 | self.offset += 1 65 | 66 | while done == 0: 67 | if self.getbit() != 0: 68 | if self.getbit() != 0: 69 | if self.getbit() != 0: 70 | offs = 0 71 | for i in range(4): 72 | offs = (offs << 1) + self.getbit() 73 | if offs != 0: 74 | destination += destination[len(destination) - offs] 75 | else: 76 | destination += '\x00' 77 | LWM = 0 78 | else: 79 | offs = ord(self.source[self.offset]) 80 | self.offset += 1 81 | len_ = 2 + (offs & 1) 82 | offs >>= 1 83 | if offs != 0: 84 | for i in range(len_): 85 | destination += destination[len(destination) - offs] 86 | else: 87 | done = 1 88 | R0 = offs 89 | LWM = 1 90 | else: 91 | offs = self.getgamma() 92 | if LWM == 0 and offs == 2: 93 | offs = R0 94 | len_ = self.getgamma() 95 | for i in range(len_): 96 | destination += destination[len(destination) - offs] 97 | else: 98 | if LWM == 0: 99 | offs -= 3 100 | else: 101 | offs -= 2 102 | offs <<= 8 103 | offs += ord(self.source[self.offset]) 104 | self.offset += 1 105 | len_ = self.getgamma() 106 | if offs >= 32000: 107 | len_ += 1 108 | if offs >= 1280: 109 | len_ += 1 110 | if offs < 128: 111 | len_ += 2 112 | for i in range(len_): 113 | destination += destination[len(destination) - offs] 114 | R0 = offs 115 | LWM = 1 116 | else: 117 | destination += self.source[self.offset] 118 | self.offset += 1 119 | LWM = 0 120 | return destination 121 | -------------------------------------------------------------------------------- /evbe.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | """ 3 | Copyright (C) 2013 Roman Bondarenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | import os 24 | import sys 25 | from argparse import ArgumentParser 26 | from EVBE.EVBFile import EVBFile 27 | from EVBE.EVBContainer import Container 28 | from aplib import aPLib 29 | 30 | __version__ = '1.0.9' 31 | __author__ = 'Roman Bondarenko' 32 | __contact__ = 'roman@reu.org.ua' 33 | 34 | if __name__ == '__main__': 35 | parser = ArgumentParser(description='Enigma Virtual Box Extractor v%s, by Roman Bondarenko' % __version__) 36 | parser.add_argument('file', help='input file') 37 | parser.add_argument('-e', action='store_true', help='extract data') 38 | parser.add_argument('-o', dest='output_directory', 39 | help='path to folder into which the data will be retrieved (default: _data)') 40 | args = parser.parse_args() 41 | 42 | file_ = EVBFile(args.file) 43 | if not file_.read(): 44 | sys.exit(2) 45 | 46 | container = Container(file_.data, file_.offset) 47 | if container.read_header(): 48 | if not args.e: 49 | container.info() 50 | else: 51 | offset = 0 52 | output = args.output_directory 53 | if output is None: 54 | output = args.file[:-4] + '_data' 55 | if not os.path.exists(output): 56 | os.mkdir(output) 57 | for fs in container.read_data(): 58 | name = os.path.join(output, fs['name']) 59 | if not 'offset' in fs.keys(): 60 | if not os.path.exists(name): 61 | os.mkdir(name) 62 | else: 63 | with open(name, 'wb') as f: 64 | offset, size = fs['offset'], fs['size'] 65 | try: 66 | data = file_.data[offset:offset + size] 67 | offset += size 68 | if container.header.compress_files == 1: 69 | a = aPLib(data) 70 | data = a.depack() 71 | except Exception as e: 72 | print(e) 73 | else: 74 | f.write(data) --------------------------------------------------------------------------------