6.21 筆記法實例 (pe.py)
import getopt
try:
import pefile
HAVE_PEFILE = True
except ImportError:
HAVE_PEFILE = False
try:
import magic
HAVE_MAGIC = True
except ImportError:
HAVE_MAGIC = False
from viper.common.out import *
from viper.common.objects import File
from viper.common.abstracts import Module
from viper.core.session import __session__
class PE(Module):
cmd = 'pe'
description = 'Extract information from PE32 headers'
def __init__(self):
self.pe = None
def __get_filetype(self, data):
if not HAVE_MAGIC:
return None
try:
ms = magic.open(magic.MAGIC_NONE)
ms.load()
file_type = ms.buffer(data)
except:
try:
file_type = magic.from_buffer(data)
except Exception:
return None
return file_type
def imports(self):
if not self.pe:
return
if hasattr(self.pe, 'DIRECTORY_ENTRY_IMPORT'):
for entry in self.pe.DIRECTORY_ENTRY_IMPORT:
try:
print("[*] DLL: {0}".format(entry.dll))
for symbol in entry.imports:
print("\t{0}: {1}".format(hex(symbol.address), symbol.name))
except:
continue
def exports(self):
if not self.pe:
return
print("[*] Exports:")
if hasattr(self.pe, 'DIRECTORY_ENTRY_EXPORT'):
for symbol in self.pe.DIRECTORY_ENTRY_EXPORT.symbols:
print("{0}: {1} ({2})".format(hex(self.pe.OPTIONAL_HEADER.ImageBase + symbol.address), symbol.name, symbol.ordinal))
def resources(self):
if not self.pe:
return
def usage():
print("usage: pe resources [-d=folder]")
def help():
usage()
print("")
print("Options:")
print("\t--help (-h)\tShow this help message")
print("\t--dump (-d)\tDestination directory to store resource files in")
print("")
try:
opts, argv = getopt.getopt(self.args[1:], 'hd:', ['help', 'dump='])
except getopt.GetoptError as e:
print(e)
usage()
return
dump_to = None
for opt, value in opts:
if opt in ('-h', '--help'):
help()
return
elif opt in ('-d', '--dump'):
dump_to = value
resources = []
if hasattr(self.pe, 'DIRECTORY_ENTRY_RESOURCE'):
for resource_type in self.pe.DIRECTORY_ENTRY_RESOURCE.entries:
try:
resource = {}
if resource_type.name is not None:
name = str(resource_type.name)
else:
name = str(pefile.RESOURCE_TYPE.get(resource_type.struct.Id))
if name == None:
name = str(resource_type.struct.Id)
if hasattr(resource_type, 'directory'):
for resource_id in resource_type.directory.entries:
if hasattr(resource_id, 'directory'):
for resource_lang in resource_id.directory.entries:
data = self.pe.get_data(resource_lang.data.struct.OffsetToData, resource_lang.data.struct.Size)
filetype = self.__get_filetype(data)
language = pefile.LANG.get(resource_lang.data.lang, None)
sublanguage = pefile.get_sublang_name_for_lang(resource_lang.data.lang, resource_lang.data.sublang)
offset = ('%-8s' % hex(resource_lang.data.struct.OffsetToData)).strip()
size = ('%-8s' % hex(resource_lang.data.struct.Size)).strip()
resource = [name, offset, size, filetype, language, sublanguage]
if dump_to:
resource_path = os.path.join(dump_to, '{0}_{1}_{2}'.format(__session__.file.md5, offset, name))
resource.append(resource_path)
with open(resource_path, 'wb') as resource_handle:
resource_handle.write(data)
resources.append(resource)
except Exception as e:
print_error(e)
continue
headers = ['Name', 'Offset', 'Size', 'File Type', 'Language', 'Sublanguage']
if dump_to:
headers.append('Dumped To')
print table(headers, resources)
def help(self):
print_error("Choose an option!")
def run(self):
if not __session__.is_set():
print_error("No session opened")
return
if not HAVE_PEFILE:
print_error("Missing dependency, install pefile (`pip install pefile`)")
return
try:
self.pe = pefile.PE(__session__.file.path)
except pefile.PEFormatError as e:
print_error("Unable to parse PE file: {0}".format(e))
return
if len(self.args) == 0:
self.help()
return
if self.args[0] == 'imports':
self.imports()
elif self.args[0] == 'exports':
self.exports()
elif self.args[0] == 'resources':
self.resources()
用 HAVE_PEFILE 及 HAVE_MAGIC 兩 flag
表示 pefile, magic 兩 module 是否被成功 import
class PE 繼承自 class Module
instance 初始化時,先將 self.pe = None (尚不知用意
PE 內有 function 如下:
__get_filetype()
imports()
exports()
resources()
help()
run()
1. __get_filetype()
先檢查有無 import magic 套件
若無,則 return None
此處作者用了兩種方法取得 file_type
1. 先用 magic-python module 來做。
https://github.com/mammadori/magic-python
2. 若第一種方法行不通,則用 python-magic 來做。
https://github.com/ahupp/python-magic
與 objects.py 相比,這裡少了用 subprocess 呼叫 file 指令來做。
2. imports()
先檢查 self.pe 是否存在
若無,則跳出 imports()
self.pe 在 run function 中 line 155 給予
用 hasattr 判斷 DIRECTORY_ENTRY_IMPORT 是否為 self.pe instance 內的 attribute
若是,則用迴圈一一取出 self.pe.DIRECTORY_ENTRY_IMPORT as entry
接著印出 entry.dll
再用迴圈一一取出 entry.imports as symbol
印出 hex(symbol.address) 以及 symbol.name
3. exports()
先檢查 self.pe 是否存在
若無,則跳出 exports()
印出 [*] Exports:
接著用 hasattr 判斷 DIRECTORY_ENTRY_IMPORT 是否為 self.pe instance 內的 attribute
若是,則用迴圈一一取出 self.pe.DIRECTORY_ENTRY_EXPORT.symbols as symbol
接著印出
1. hex(self.pe.OPTIONAL_HEADER.ImageBase + symbol.address)
2. symbol.name
3. symbol.ordinal
4. resources()
先檢查 self.pe 是否存在
若無,則跳出 resources
function usage
印出 usage: pe resources [-d=folder]
顯數 resources 底下還有 -d 參數可用
function help
呼叫 usage 先印一次簡單的用法。
再更細部印出 resources 底下的參數、用法。
接著用 getopt 抓出 resources 後面的參數
self.args[0] could be resources, imports or exports
line 87, dump_to 先設為 None
接著處理 opts
若參數是 -h or --help 則呼叫 help()
若參數是 -d or --dump
則將 value (也就是儲存 resources files 的目標資料夾) assign 給 dump_to
接著建立一名為 resources 的空 list。
line 98, 接著用 hasattr 判斷 DIRECTORY_ENTRY_IMPORT 是否為 self.pe instance 內的 attribute
若是,則用迴圈一一取出 self.pe.DIRECTORY_ENTRY_RESOURCE.entries as resource_type
迴圈內,先建立一名為 resource 的空字典
若 resource_type.name 不是 None
則 name = str(resource_type.name)
若是 None
則 name = str(pefile.RESOURCE_TYPE.get(resource_type.struct.Id))
接著檢查 name 是否 == None
若是,則 name = str(resource_type.struct.Id)
接著又用 hasattr 檢查 directory 是否為 resource_type 的 attribute
若是,則用迴圈一一取出 resource_type.directory.entries as resource_lang
迴圈內
1. 用 self.pe.get_data() 將結果給予 data
2. 用 self.__get_filetype(data) 將結果給予 filetype
3. 用 pefile.LANG.get() 將結果給予 language
4. 用 pefile.get_sublang_name_for_lang() 將結果給予 sublanguage
5. 讓 ('%-8s' % hex(resource_lang.data.struct.OffsetToData)).strip() 處理後結果給予 offset
6. 讓 ('%-8s' % hex(resource_lang.data.struct.Size)).strip() 處理後結果給予 size
最後將 name, offset, size, filetype, language, sublanguage 丟入 resource list中。
若 dump_to 不是空
則用 os.path.join 將 __session__.file.md5, offset, name 合併為 resource_path
再將 resource_path append 到 resource list 中
接著開檔將資料寫入檔案
寫完後,再將 resource append 到 resources list 中。
line 136, 設定 headers list
若 dump_to 非為空
則 headers.append('Dumped To')
line 138, 用 pretty table 印出 resources
看到這裡,感覺 line 191, 的 resource 字典沒被用到?
5. help()
印出 Choose an option!
6. run()
首先檢查 session 是否 open
若否則印出 No session opened
接著離開 run
接著檢查是否有成功 import pefile
若無,則印出 Missing dependency, install pefile (`pip install pefile`)
試
用 pefile.PE(__session__.file.path) 將結果給 self.pe
若失敗
則印出 Unable to parse PE file
並離開 run
檢查參數長度是否為零
若是,則呼叫 help()
並離開 run
若 self.args[0] == 'imports'
呼叫 self.imports()
若為 'exports'
呼叫 self.exports()
若為 'resources'
呼叫 self.resources()
class Demo:
def __init__(self, i):
self.i = i
self.x = "xxx"
self.y = "yyy"
self.z = "zzz"
def __str__(self):
return str(self.i)
def hello(self):
print("hello " + self.__str__())
d = Demo(22)
print(hasattr(d, "t"))
print(hasattr(d, "u"))
print(hasattr(d, "v"))
print(hasattr(d, "w"))
print(hasattr(d, "x"))
print(hasattr(d, "y"))
print(hasattr(d, "z"))
Last updated
Was this helpful?