Advertisement
opexxx

malware.py

Nov 25th, 2013
647
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 155.80 KB | None | 0 0
  1. # Volatility
  2. #
  3. # Authors:
  4. # Michael Hale Ligh <michael.ligh@mnin.org>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or (at
  9. # your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful, but
  12. # WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. # General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. #
  20.  
  21. import os, sys, string, struct, subprocess, binascii, time, re
  22. from operator import itemgetter
  23. from bisect import bisect_right
  24. import volatility.debug as debug
  25. import volatility.obj as obj
  26. import volatility.plugins.procdump as procdump
  27. import volatility.win32.tasks as tasks
  28. import volatility.win32.modules as modules
  29. import volatility.commands as commands
  30. import volatility.utils as utils
  31. import volatility.plugins.ssdt as ssdt
  32. import volatility.plugins.filescan as filescan
  33. import volatility.scan as scan
  34. import volatility.plugins.modscan as modscan
  35. import volatility.plugins.taskmods as taskmods
  36. import volatility.plugins.overlays.windows.windows as windows
  37.  
  38. #--------------------------------------------------------------------------------
  39. # dependents
  40. #--------------------------------------------------------------------------------
  41. try:
  42.     import yara
  43. except ImportError:
  44.     print "YARA is not installed, see http://code.google.com/p/yara-project/"
  45.  
  46. try:
  47.     import distorm3
  48. except ImportError:
  49.     print 'distorm3 is not installed, see http://code.google.com/p/distorm/'
  50.  
  51. #--------------------------------------------------------------------------------
  52. # memory protection flags from nt!MmProtectToValue
  53. #--------------------------------------------------------------------------------
  54. PROTECT_FLAGS = [
  55.     'PAGE_NOACCESS',
  56.     'PAGE_READONLY',
  57.     'PAGE_EXECUTE',
  58.     'PAGE_EXECUTE_READ',
  59.     'PAGE_READWRITE',
  60.     'PAGE_WRITECOPY',
  61.     'PAGE_EXECUTE_READWRITE',
  62.     'PAGE_EXECUTE_WRITECOPY',
  63.     'PAGE_NOACCESS',
  64.     'PAGE_NOCACHE | PAGE_READONLY',
  65.     'PAGE_NOCACHE | PAGE_EXECUTE',
  66.     'PAGE_NOCACHE | PAGE_EXECUTE_READ',
  67.     'PAGE_NOCACHE | PAGE_READWRITE',
  68.     'PAGE_NOCACHE | PAGE_WRITECOPY',
  69.     'PAGE_NOCACHE | PAGE_EXECUTE_READWRITE',
  70.     'PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY',
  71.     'PAGE_NOACCESS',
  72.     'PAGE_GUARD | PAGE_READONLY',
  73.     'PAGE_GUARD | PAGE_EXECUTE',
  74.     'PAGE_GUARD | PAGE_EXECUTE_READ',
  75.     'PAGE_GUARD | PAGE_READWRITE',
  76.     'PAGE_GUARD | PAGE_WRITECOPY',
  77.     'PAGE_GUARD | PAGE_EXECUTE_READWRITE',
  78.     'PAGE_GUARD | PAGE_EXECUTE_WRITECOPY',
  79.     'PAGE_NOACCESS',
  80.     'PAGE_WRITECOMBINE | PAGE_READONLY',
  81.     'PAGE_WRITECOMBINE | PAGE_EXECUTE',
  82.     'PAGE_WRITECOMBINE | PAGE_EXECUTE_READ',
  83.     'PAGE_WRITECOMBINE | PAGE_READWRITE',
  84.     'PAGE_WRITECOMBINE | PAGE_WRITECOPY',
  85.     'PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE',
  86.     'PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY',
  87. ]
  88.  
  89. #--------------------------------------------------------------------------------
  90. # Special data types not in the public symbol files
  91. #--------------------------------------------------------------------------------
  92.  
  93. malware_types = {
  94.      #
  95.      # Types for the svcscan plugin (XP only)
  96.      #
  97.     '_SERVICE_LIST_ENTRY' : [ 0x8, {
  98.         'Blink' : [ 0x0, ['pointer', ['_SERVICE_RECORD_LEGACY']]],
  99.         'Flink' : [ 0x4, ['pointer', ['_SERVICE_RECORD_LEGACY']]],
  100.     } ],
  101.     '_SERVICE_RECORD_LEGACY' : [ 0x70, {
  102.         'ServiceList' : [ 0x0, ['_SERVICE_LIST_ENTRY']],
  103.         'ServiceName' : [ 0x8, ['pointer', ['unsigned short']]],
  104.         'DisplayName' : [ 0xc, ['pointer', ['unsigned short']]],
  105.         'Order' : [ 0x10, ['int']],
  106.         'TagSignature' : [ 0x18, ['int']],
  107.         'Binary1' : [ 0x24, ['pointer', ['unsigned short']]],
  108.         'Binary2' : [ 0x24, ['pointer', ['_SERVICE_BINARY_LEGACY']]],
  109.         'Type' : [ 0x28, ['int']],
  110.         'State' : [ 0x2c, ['int']],
  111.     } ],
  112.     '_SERVICE_BINARY_LEGACY' : [ 0x14, {
  113.         'ServicePath' : [ 0x8, ['pointer', ['unsigned short']]],
  114.         'ProcessId' : [ 0xc, ['int']],
  115.     } ],
  116.      #
  117.      # Types for the svcscan plugin (Vista and 7)
  118.      #
  119.     '_SERVICE_HEADER': [ None, {
  120.         # Signature of "serH"
  121.         'Tag': [ 0x0, ['array', 4, ['unsigned char']]],
  122.         # Pointer to the main record
  123.         'Ser': [ 0xC, ['pointer', ['_SERVICE_RECORD']]],
  124.     } ],
  125.     '_SERVICE_RECORD': [ None, {
  126.         # Previous entry in the singly linked list
  127.         'PrevEntry': [ 0x0, ['pointer', ['_SERVICE_RECORD']]],
  128.         # The service's name
  129.         'ServiceName': [ 0x4, ['pointer', ['unsigned short']]],
  130.         # The service's display name
  131.         'DisplayName': [ 0x8, ['pointer', ['unsigned short']]],
  132.         # The unique order number of the service
  133.         'Order': [ 0xC, ['unsigned int']],
  134.         # Union which is LPWSTR for drivers or points to another struct for processes
  135.         'Binary2': [ 0x1C, ['pointer', ['_SERVICE_BINARY']]],
  136.         'Binary1': [ 0x1C, ['pointer', ['unsigned char']]],
  137.         # The service type (kernel driver, own process, shared process, etc)
  138.         'Type': [ 0x20, ['unsigned int']],
  139.         # The service state (running, stopped, paused, etc)
  140.         'State': [ 0x24, ['unsigned int']],
  141.     } ],
  142.     '_SERVICE_BINARY': [ None, {
  143.         # The load path of the service binary, includes command line arguments
  144.         'ServicePath': [ 0x8, ['pointer', ['unsigned short']]],
  145.         # Process ID if the service is active
  146.         'ProcessId': [ 0xC, ['unsigned int']],
  147.     } ],
  148.     #
  149.     # Types for the csrpslist plugin
  150.     #
  151.     '_CSR_PROCESS_POINTER' : [ None, {
  152.         'CsrRootProcess' : [ 0x0, ['pointer', ['pointer', ['_CSR_PROCESS']]]],
  153.     } ],
  154.     '_CSR_PROCESS' : [ 0x60, {
  155.         'ClientId' : [ 0x0, ['_CLIENT_ID']],
  156.         'ListLink' : [ 0x8, ['_LIST_ENTRY']],
  157.         'ThreadList' : [ 0x10, ['_LIST_ENTRY']],
  158.         'NtSession' : [ 0x18, ['pointer', ['_CSR_NT_SESSION']]],
  159.         'ClientPort' : [ 0x1c, ['pointer', ['void']]],
  160.         'ClientViewBase' : [ 0x20, ['pointer', ['unsigned char']]],
  161.         'ClientViewBounds' : [ 0x24, ['pointer', ['unsigned char']]],
  162.         'ProcessHandle' : [ 0x28, ['pointer', ['void']]],
  163.         'SequenceNumber' : [ 0x2c, ['unsigned long']],
  164.         'Flags' : [ 0x30, ['unsigned long']],
  165.         'DebugFlags' : [ 0x34, ['unsigned long']],
  166.         'ReferenceCount' : [ 0x38, ['unsigned long']],
  167.         'ProcessGroupId' : [ 0x3c, ['unsigned long']],
  168.         'ProcessGroupSequence' : [ 0x40, ['unsigned long']],
  169.         'LastMessageSequence' : [ 0x44, ['unsigned long']],
  170.         'NumOutstandingMessages' : [ 0x48, ['unsigned long']],
  171.         'ShutdownLevel' : [ 0x4c, ['unsigned long']],
  172.         'ShutdownFlags' : [ 0x50, ['unsigned long']],
  173.         'Luid' : [ 0x54, ['_LUID']],
  174.         'ServerDllPerProcessData' : [ 0x5c, ['array', 1, ['pointer', ['void']]]],
  175.     } ],
  176.     '_CSR_THREAD' : [ 0x38, {
  177.         'CreateTime' : [ 0x0, ['_LARGE_INTEGER']],
  178.         'Link' : [ 0x8, ['_LIST_ENTRY']],
  179.         'HashLinks' : [ 0x10, ['_LIST_ENTRY']],
  180.         'ClientId' : [ 0x18, ['_CLIENT_ID']],
  181.         'Process' : [ 0x20, ['pointer', ['_CSR_PROCESS']]],
  182.         'ThreadHandle' : [ 0x24, ['pointer', ['void']]],
  183.         'Flags' : [ 0x28, ['unsigned long']],
  184.         'ReferenceCount' : [ 0x2c, ['unsigned long']],
  185.         'ImpersonateCount' : [ 0x30, ['unsigned long']],
  186.     } ],
  187.     '_CSR_NT_SESSION' : [ 0x18, {
  188.         'SessionLink' : [ 0x0, ['_LIST_ENTRY']],
  189.         'SessionId' : [ 0x8, ['unsigned long']],
  190.         'ReferenceCount' : [ 0xc, ['unsigned long']],
  191.         'RootDirectory' : [ 0x10, ['_STRING']],
  192.     } ],
  193.     #
  194.     # Types for pe file header parsing
  195.     #
  196.     '_IMAGE_EXPORT_DIRECTORY': [ 0x28, {
  197.         'Base': [ 0x10, ['unsigned int']],
  198.         'NumberOfFunctions': [ 0x14, ['unsigned int']],
  199.         'NumberOfNames': [ 0x18, ['unsigned int']],
  200.         'AddressOfFunctions': [ 0x1C, ['unsigned int']],
  201.         'AddressOfNames': [ 0x20, ['unsigned int']],
  202.         'AddressOfNameOrdinals': [ 0x24, ['unsigned int']],
  203.     } ],
  204.     '_IMAGE_IMPORT_DESCRIPTOR': [ 0x14, {
  205.         'OriginalFirstThunk': [ 0x0, ['unsigned int']],
  206.         'TimeDateStamp': [ 0x4, ['unsigned int']],
  207.         'ForwarderChain': [ 0x8, ['unsigned int']],
  208.         'Name': [ 0xC, ['unsigned int']],
  209.         'FirstThunk': [ 0x10, ['pointer', ['unsigned int']]],
  210.     } ],
  211.     '_IMAGE_THUNK_DATA' : [ 0x4, {
  212.         'AddressOfData' : [ 0x0, ['unsigned int']],
  213.     } ],
  214.     '_IMAGE_IMPORT_BY_NAME' : [ None, {
  215.         'Hint' : [ 0x0, ['unsigned short']],
  216.         'Name' : [ 0x2, ['array', 20, ['unsigned char']]],
  217.     } ],
  218.     '_IMAGE_SECTION_HEADER' : [ None, {
  219.         'Name' : [ 0x0, ['String', dict(length = 8)]],
  220.     }],
  221.     #
  222.     # Types for callbacks (Thanks to Frank Boldewin for some clues)
  223.     #
  224.     '_NOTIFICATION_PACKET' : [ 0x10, {
  225.         'ListEntry' : [ 0x0, ['_LIST_ENTRY']],
  226.         'DriverObject' : [ 0x8, ['pointer', ['_DRIVER_OBJECT']]],
  227.         'NotificationRoutine' : [ 0xC, ['unsigned int']],
  228.     } ],
  229.     '_KBUGCHECK_CALLBACK_RECORD' : [ 0x20, {
  230.         'Entry' : [ 0x0, ['_LIST_ENTRY']],
  231.         'CallbackRoutine' : [ 0x8, ['unsigned int']],
  232.         'Buffer' : [ 0xC, ['pointer', ['void']]],
  233.         'Length' : [ 0x10, ['unsigned int']],
  234.         'Component' : [ 0x14, ['pointer', ['unsigned char']]],
  235.         'Checksum' : [ 0x18, ['pointer', ['unsigned int']]],
  236.         'State' : [ 0x1C, ['unsigned char']],
  237.     } ],
  238.     '_KBUGCHECK_REASON_CALLBACK_RECORD' : [ 0x1C, {
  239.         'Entry' : [ 0x0, ['_LIST_ENTRY']],
  240.         'CallbackRoutine' : [ 0x8, ['unsigned int']],
  241.         'Component' : [ 0xC, ['pointer', ['unsigned char']]],
  242.         'Checksum' : [ 0x10, ['pointer', ['unsigned int']]],
  243.         'Reason' : [ 0x14, ['unsigned int']],
  244.         'State' : [ 0x18, ['unsigned char']],
  245.     } ],
  246.     '_SHUTDOWN_PACKET' : [ 0xC, {
  247.         'Entry' : [ 0x0, ['_LIST_ENTRY']],
  248.         'DeviceObject' : [ 0x8, ['pointer', ['_DEVICE_OBJECT']]],
  249.     } ],
  250.     '_EX_CALLBACK_ROUTINE_BLOCK' : [ 0x8, {
  251.         'RundownProtect' : [ 0x0, ['unsigned int']], \
  252.         'Function' : [ 0x4, ['unsigned int']], \
  253.         'Context' : [ 0x8, ['unsigned int']], \
  254.     } ],
  255.     '_GENERIC_CALLBACK' : [ 0xC, {
  256.         'Callback' : [ 0x4, ['pointer', ['void']]],
  257.         'Associated' : [ 0x8, ['pointer', ['void']]],
  258.     } ],
  259.     '_REGISTRY_CALLBACK_LEGACY' : [ 0x38, {
  260.         'CreateTime' : [ 0x0, ['WinTimeStamp', {}]],
  261.     } ],
  262.     '_REGISTRY_CALLBACK' : [ None, {
  263.         'ListEntry' : [ 0x0, ['_LIST_ENTRY']],
  264.         'Function' : [ 0x1C, ['pointer', ['void']]],
  265.     } ],
  266.     '_DBGPRINT_CALLBACK' : [ 0x14, {
  267.         'Function' : [ 0x8, ['pointer', ['void']]],
  268.     } ],
  269.     '_SEG_DESCRIPTOR' : [ 0x8, {
  270.         'size1': [ 0x0, ['BitField', dict(start_bit = 0, end_bit = 16)]],
  271.         'baseaddress1': [ 0x2, ['BitField', dict(start_bit = 0, end_bit = 16)]],
  272.         'baseaddress2': [ 0x4, ['BitField', dict(start_bit = 0, end_bit = 8)]],
  273.         'type': [ 0x4, ['BitField', dict(start_bit = 8, end_bit = 12)]],
  274.         'sFlag': [ 0x4, ['BitField', dict(start_bit = 12, end_bit = 13)]],
  275.         'dpl': [ 0x4, ['BitField', dict(start_bit = 13, end_bit = 15)]],
  276.         'pFlag': [ 0x4, ['BitField', dict(start_bit = 15, end_bit = 16)]],
  277.         'size2': [ 0x6, ['BitField', dict(start_bit = 0, end_bit = 4)]],
  278.         'notused': [ 0x6, ['BitField', dict(start_bit = 4, end_bit = 5)]],
  279.         'lFlag': [ 0x6, ['BitField', dict(start_bit = 5, end_bit = 6)]],
  280.         'DB': [ 0x6, ['BitField', dict(start_bit = 6, end_bit = 7)]],
  281.         'gFlag': [ 0x6, ['BitField', dict(start_bit = 7, end_bit = 8)]],
  282.         'baseaddress3': [ 0x6, ['BitField', dict(start_bit = 8, end_bit = 16)]],
  283.     } ],
  284.     '_CALL_GATE_DESCRIPTOR' : [ 0x8, {
  285.         'offset1': [ 0x0, ['BitField', dict(start_bit = 0, end_bit = 16)]],
  286.         'selector': [ 0x2, ['BitField', dict(start_bit = 0, end_bit = 16)]],
  287.         'argcount': [ 0x4, ['BitField', dict(start_bit = 0, end_bit = 5)]],
  288.         'zeroes': [ 0x4, ['BitField', dict(start_bit = 5, end_bit = 8)]],
  289.         'type': [ 0x4, ['BitField', dict(start_bit = 8, end_bit = 12)]],
  290.         'sFlag': [ 0x4, ['BitField', dict(start_bit = 12, end_bit = 13)]],
  291.         'dpl': [ 0x4, ['BitField', dict(start_bit = 13, end_bit = 15)]],
  292.         'pFlag': [ 0x4, ['BitField', dict(start_bit = 15, end_bit = 16)]],
  293.         'offset2': [ 0x6, ['BitField', dict(start_bit = 0, end_bit = 16)]],
  294.     } ],
  295. }
  296.  
  297. #--------------------------------------------------------------------------------
  298. # Symbol classes used with API hooks etc
  299. #--------------------------------------------------------------------------------
  300.  
  301. class Symbol:
  302.     def __init__(self, owner_mod=None, ordinal=None, funcaddr=None, name=None, forwarder=None, dll=None):
  303.         self.owner_mod = owner_mod
  304.         self.ordinal = ordinal
  305.         self.funcaddr = funcaddr & 0xFFFFFFFF
  306.         self.name = name
  307.         self.forwarder = forwarder
  308.  
  309.     def get_name(self):
  310.         """Returns the function name, its ordinal value, or UNKNOWN"""
  311.         if self.name != None and self.name != "":
  312.             return self.name
  313.         elif self.ordinal != None:
  314.             return hex(self.ordinal)
  315.         else:
  316.             return 'UNKNOWN'
  317.  
  318.     def get_instruction(self, instruction_number = 0):
  319.         """Returns the Nth instruction at an API's start address"""                    
  320.  
  321.         data = self.owner_mod.obj_vm.zread(self.funcaddr, (instruction_number + 1) * 12)
  322.         c = 0
  323.  
  324.         for op in distorm3.Decompose(self.funcaddr, data, distorm3.Decode32Bits):
  325.             if not op.valid:
  326.                 break
  327.             if c == instruction_number:
  328.                 return op
  329.             c += 1            
  330.  
  331. class ExportedSymbol(Symbol):
  332.     pass
  333.  
  334. class ImportedSymbol(Symbol):
  335.     pass
  336.  
  337. #--------------------------------------------------------------------------------
  338. # Extensions of Volatility objects - stable on all OS
  339. #--------------------------------------------------------------------------------
  340.  
  341. class _IMAGE_IMPORT_DESCRIPTOR(obj.CType):
  342.     """Handles IID entries for imported functions"""
  343.  
  344.     def __init__(self, theType = None, offset = None, vm = None, parent = None, *args, **kwargs):
  345.         self.sectoffset = offset
  346.         obj.CType.__init__(self, theType = theType, offset = offset, vm = vm, parent = parent, *args, **kwargs)
  347.  
  348.     def is_list_end(self):
  349.         """Returns True if we've reached the list end (a struct with all zeroes)"""
  350.         data = self.obj_vm.zread(self.obj_offset, self.obj_vm.profile.get_obj_size('_IMAGE_IMPORT_DESCRIPTOR'))
  351.         if data.count(chr(0)) == len(data):
  352.             return True
  353.         return False
  354.  
  355.     def get_name(self):
  356.         """Returns the name of the DLL for this IID"""
  357.         ###This try block is for http://code.google.com/p/volatility/issues/detail?id=57
  358.         try:
  359.             va = self.obj_parent.DllBase + self.Name
  360.         except:
  361.             return ''
  362.  
  363.         if self.obj_vm.is_valid_address(va):
  364.             return read_asciiz(self.obj_vm, va)
  365.         else:
  366.             return ''
  367.  
  368.     def get_table(self, thunk_va, img_size):
  369.         """Generator for ITD structures"""
  370.         i = 0
  371.         thunk_size = self.obj_vm.profile.get_obj_size('_IMAGE_THUNK_DATA')
  372.         while True:
  373.             thunk = obj.Object('_IMAGE_THUNK_DATA', offset = thunk_va + (i*thunk_size), vm = self.obj_vm)
  374.             if not thunk or not thunk.AddressOfData:
  375.                 break
  376.             yield thunk
  377.             i += 1
  378.  
  379.     def get_imports(self):
  380.         """Generator for enumerating imported functions"""
  381.  
  382.         mod_base = self.obj_parent.DllBase
  383.  
  384.         dll = self.get_name()
  385.  
  386.         if not is_valid_dos_filename(dll):
  387.             dll = '*invalid*'
  388.  
  389.         try:
  390.             # The lookup table, contains function names
  391.             ilt = list(self.get_table(mod_base + self.OriginalFirstThunk))
  392.             # The address table, contains bound addresses
  393.             iat = list(self.get_table(mod_base + self.FirstThunk))
  394.         except:
  395.             raise StopIteration('Invalid')
  396.  
  397.         # We can use either ilt or iat here, doesn't really matter
  398.         table = ilt
  399.  
  400.         for i in xrange(len(table)):
  401.  
  402.             #print i, table[i], table[i].AddressOfData
  403.  
  404.             imp_ord = imp_name = None
  405.  
  406.             if table[i].AddressOfData & IMAGE_ORDINAL_FLAG:
  407.                 imp_ord = table[i].AddressOfData & 0xffff
  408.             else:
  409.                 imp_name = read_asciiz(self.obj_vm, mod_base + table[i].AddressOfData + 2)
  410.                 if not is_valid_function_name(imp_name):
  411.                     imp_name = "*invalid*"
  412.  
  413.             imp_address = self.FirstThunk + mod_base + i * 4
  414.             imp_bound = obj.Object('unsigned int', offset = imp_address, vm = self.obj_vm)
  415.  
  416.             if imp_bound:
  417.                 imp = ImportedSymbol(
  418.                     owner_mod = self.obj_parent,
  419.                     ordinal = imp_ord,
  420.                     funcaddr = imp_bound,
  421.                     name = imp_name,
  422.                     forwarder = None)
  423.    
  424.                 yield dll, imp
  425.  
  426. class _IMAGE_EXPORT_DIRECTORY(obj.CType):
  427.     """Handles IED entries for exported functions"""
  428.  
  429.     def __init__(self, theType = None, offset = None, vm = None, parent = None, *args, **kwargs):
  430.         self.sectoffset = offset
  431.         obj.CType.__init__(self, theType = theType, offset = offset, vm = vm, parent = parent, *args, **kwargs)
  432.  
  433.     def get_forwarder(self, mod_base, func_rva):
  434.         """If we are dealing with forwarded exports, return the name of the target function"""
  435.  
  436.         return None
  437.  
  438.         exp_addr = self.obj_parent.VirtualAddress
  439.         exp_size = self.obj_parent.Size
  440.         if func_rva >= exp_addr and func_rva < exp_addr + exp_size:
  441.             return self.read_asciiz(mod_base + func_rva)
  442.         else:
  443.             return None
  444.  
  445.     def get_exports(self):
  446.         """Generator for exported functions"""
  447.  
  448.         mod_base = self.obj_parent.DllBase
  449.  
  450.         seen_ordinals = []
  451.  
  452.         address_of_functions = obj.Object('Array', vm = self.obj_vm, \
  453.             offset = mod_base + self.AddressOfFunctions, targetType = 'unsigned int', count = self.NumberOfFunctions)
  454.         address_of_names = obj.Object('Array', vm = self.obj_vm, \
  455.             offset = mod_base + self.AddressOfNames, targetType = 'unsigned int', count = self.NumberOfNames)
  456.         address_of_name_ordinals = obj.Object('Array', vm = self.obj_vm, \
  457.             offset = mod_base + self.AddressOfNameOrdinals, targetType = 'unsigned short', count = self.NumberOfNames)
  458.  
  459.         # Handle functions exported by name *and* ordinal
  460.         for i in range(self.NumberOfNames):
  461.  
  462.             name_rva = address_of_names[i]
  463.             ordinal = address_of_name_ordinals[i]
  464.  
  465.             if ordinal == None:
  466.                 continue
  467.  
  468.             if name_rva == None or name_rva == 0:
  469.                 continue
  470.  
  471.             if ordinal >= self.NumberOfFunctions:
  472.                 continue
  473.  
  474.             func_rva = address_of_functions[ordinal]
  475.  
  476.             if func_rva == None or func_rva == 0:
  477.                 continue
  478.  
  479.             forwarder = self.get_forwarder(mod_base, func_rva)
  480.             name = read_asciiz(self.obj_vm, mod_base + name_rva)
  481.             ordinal += self.Base
  482.  
  483.             seen_ordinals.append(ordinal)
  484.  
  485.             yield ExportedSymbol(owner_mod = self.obj_parent,
  486.                                 ordinal = ordinal,
  487.                                 funcaddr = mod_base + func_rva,
  488.                                 name = name,
  489.                                 forwarder = forwarder)
  490.  
  491.         # Handle functions exported by ordinal only
  492.         for i in range(self.NumberOfFunctions):
  493.             ordinal = self.Base + i
  494.             if ordinal not in seen_ordinals:
  495.                 func_rva = address_of_functions[i]
  496.                 if func_rva == None or func_rva == 0:
  497.                     continue
  498.  
  499.                 forwarder = self.get_forwarder(mod_base, func_rva)
  500.                 seen_ordinals.append(ordinal)
  501.  
  502.                 yield ExportedSymbol(owner_mod = self.obj_parent,
  503.                                     ordinal = ordinal,
  504.                                     funcaddr = mod_base + func_rva,
  505.                                     name = None,
  506.                                     forwarder = forwarder)
  507.  
  508. class _LDR_DATA_TABLE_ENTRY(obj.CType):
  509.     """Extension for PE files to help enumerate IAT and EAT"""
  510.  
  511.     def __init__(self, theType = None, offset = None, vm = None, parent = None, *args, **kwargs):
  512.         self.sectoffset = offset
  513.         obj.CType.__init__(self, theType = theType, offset = offset, vm = vm, parent = parent, *args, **kwargs)
  514.  
  515.     def get_nt_header(self):
  516.         try:
  517.             dos_header = obj.Object("_IMAGE_DOS_HEADER", offset = self.DllBase, vm = self.obj_vm)
  518.             return dos_header.get_nt_header()
  519.         except ValueError:
  520.             return None
  521.  
  522.     def getprocaddress(self, name_to_find):
  523.         """Return the virtual address of a given function exported by a DLL"""
  524.         for exp in self.exports():
  525.             if exp.get_name() == name_to_find:
  526.                 return exp.funcaddr
  527.         return None
  528.  
  529.     def imports(self, addr_space = None):
  530.  
  531.         if addr_space != None:
  532.             self.obj_vm = addr_space
  533.  
  534.         nt_header = self.get_nt_header()
  535.         if nt_header == None:
  536.             raise StopIteration
  537.  
  538.         imp_addr = nt_header.OptionalHeader.DataDirectory[1].VirtualAddress
  539.         imp_size = nt_header.OptionalHeader.DataDirectory[1].Size
  540.  
  541.         if imp_addr > nt_header.OptionalHeader.SizeOfImage or imp_size <= 0:
  542.             raise StopIteration
  543.  
  544.         obj_size  = self.obj_vm.profile.get_obj_size('_IMAGE_IMPORT_DESCRIPTOR')
  545.         i = 0
  546.         imps = dict()
  547.  
  548.         while True:
  549.             desc = obj.Object('_IMAGE_IMPORT_DESCRIPTOR', vm = self.obj_vm, \
  550.                                 offset = self.DllBase + imp_addr + (i * obj_size), \
  551.                                 parent = self)
  552.  
  553.             if not desc or desc.is_list_end():
  554.                 break
  555.  
  556.             try:
  557.                 for dll, imp in desc.get_imports():
  558.                     if imps.has_key(dll):
  559.                         imps[dll].append(imp)
  560.                     else:
  561.                         imps[dll] = [imp]
  562.             except obj.InvalidOffsetError:
  563.                 pass
  564.  
  565.             i += 1
  566.        
  567.         for dll, symbols in imps.items():
  568.             yield dll, symbols
  569.  
  570.     def exports(self, addr_space = None):
  571.  
  572.         if addr_space != None:
  573.             self.newattr('obj_vm', addr_space)
  574.  
  575.         nt_header = self.get_nt_header()
  576.         if nt_header == None:
  577.             raise StopIteration
  578.    
  579.         exp_addr = nt_header.OptionalHeader.DataDirectory[0].VirtualAddress
  580.         exp_size = nt_header.OptionalHeader.DataDirectory[0].Size
  581.  
  582.         if exp_addr > nt_header.OptionalHeader.SizeOfImage or exp_size <= 0:
  583.             raise StopIteration
  584.  
  585.         exp_dir = obj.Object('_IMAGE_EXPORT_DIRECTORY', vm = self.obj_vm, offset = self.DllBase + exp_addr, parent = self)
  586.  
  587.         if exp_dir != None:
  588.             if exp_dir.AddressOfFunctions > nt_header.OptionalHeader.SizeOfImage or \
  589.                 exp_dir.AddressOfNameOrdinals > nt_header.OptionalHeader.SizeOfImage or \
  590.                 exp_dir.AddressOfNames > nt_header.OptionalHeader.SizeOfImage or \
  591.                 exp_dir.NumberOfFunctions > 0x7FFF or exp_dir.NumberOfNames > 0x7FFF:
  592.                     raise StopIteration
  593.             try:
  594.                 for exp in exp_dir.get_exports():
  595.                     yield exp
  596.             except obj.InvalidOffsetError:
  597.                 pass
  598.  
  599. class _EPROCESS(windows._EPROCESS):
  600.     def list_modules(self):
  601.         if self.UniqueProcessId and self.Peb.Ldr.InLoadOrderModuleList:
  602.             for l in self.Peb.Ldr.InLoadOrderModuleList.list_of_type("_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks"):
  603.                 yield l
  604.  
  605. class _IMAGE_DOS_HEADER(obj.CType):
  606.     """DOS header"""
  607.  
  608.     def get_nt_header(self):
  609.         """Get the NT header"""
  610.  
  611.         if self.e_magic != 0x5a4d:
  612.             raise ValueError('e_magic {0:04X} is not a valid DOS signature.'.format(self.e_magic))
  613.  
  614.         nt_header = obj.Object("_IMAGE_NT_HEADERS",
  615.                           offset = self.e_lfanew + self.obj_offset,
  616.                           vm = self.obj_vm)
  617.  
  618.         if nt_header.Signature != 0x4550:
  619.             raise ValueError('NT header signature {0:04X} is not a valid'.format(nt_header.Signature))
  620.  
  621.         return nt_header
  622.  
  623. class _IMAGE_NT_HEADERS(obj.CType):
  624.     """PE header"""
  625.  
  626.     def get_sections(self, unsafe):
  627.         """Get the PE sections"""
  628.         sect_size = self.obj_vm.profile.get_obj_size("_IMAGE_SECTION_HEADER")
  629.         start_addr = self.FileHeader.SizeOfOptionalHeader + self.OptionalHeader.obj_offset
  630.  
  631.         for i in range(self.FileHeader.NumberOfSections):
  632.             s_addr = start_addr + (i * sect_size)
  633.             sect = obj.Object("_IMAGE_SECTION_HEADER", offset = s_addr, vm = self.obj_vm, parent = self)
  634.             if not unsafe:
  635.                 sect.sanity_check_section()
  636.             yield sect
  637.    
  638. class _IMAGE_SECTION_HEADER(obj.CType):
  639.     """PE section"""
  640.  
  641.     def sanity_check_section(self):
  642.         """Sanity checks address boundaries"""
  643.         # Note: all addresses here are RVAs
  644.         image_size = self.obj_parent.OptionalHeader.SizeOfImage
  645.         if self.VirtualAddress > image_size:
  646.             raise ValueError('VirtualAddress {0:08x} is past the end of image.'.format(self.VirtualAddress))
  647.         if self.Misc.VirtualSize > image_size:
  648.             raise ValueError('VirtualSize {0:08x} is larger than image size.'.format(self.Misc.VirtualSize))
  649.         if self.SizeOfRawData > image_size:
  650.             raise ValueError('SizeOfRawData {0:08x} is larger than image size.'.format(self.SizeOfRawData))
  651.  
  652. #--------------------------------------------------------------------------------
  653. # general lib area
  654. #--------------------------------------------------------------------------------
  655.  
  656. #### This is from pefile
  657. allowed_filename = string.lowercase + string.uppercase + string.digits + "!#$%&'()-@^_`{}~+,.;=[]" + ''.join( [chr(i) for i in range(128, 256)] )
  658. def is_valid_dos_filename(s):
  659.     if s is None or s == '' or not isinstance(s, str):
  660.         return False
  661.     for c in s:
  662.         if c not in allowed_filename:
  663.             return False
  664.     return True
  665.  
  666. #### This is from pefile
  667. allowed_function_name = string.lowercase + string.uppercase + string.digits + '_?@$()'
  668. def is_valid_function_name(s):
  669.     if s is None or s == '' or not isinstance(s, str):
  670.         return False
  671.     for c in s:
  672.         if c not in allowed_function_name:
  673.             return False
  674.     return True
  675.  
  676. #### This is from pefile (no 64-bit support currently)
  677. IMAGE_ORDINAL_FLAG = 0x80000000L
  678.  
  679. def read_asciiz(addr_space, string_address, max=255):
  680.     """Read a NULL-terminated ASCII string in the object's address space"""
  681.     if not addr_space.is_valid_address(string_address):
  682.         return None
  683.     try:
  684.         str = addr_space.zread(string_address, max)    
  685.         return str[:str.index('\x00')]
  686.     except:
  687.         return None
  688.  
  689. def wctomb(ptr, addr_space):
  690.     """Read a variable length Unicode buffer and return it as ASCII"""
  691.     if not addr_space.is_valid_address(ptr):
  692.         return None
  693.     try:
  694.         ret = addr_space.read(ptr, 128)
  695.     except:
  696.         return None
  697.     length = ret.find("\x00\x00")
  698.     ret = ret[0:length]
  699.     return ret.replace('\x00', '')
  700.  
  701. def can_exec(f):
  702.     """Returns true if a memory segment is executable"""
  703.     try:
  704.         flags = PROTECT_FLAGS[f]
  705.     except IndexError:
  706.         return False
  707.     else:
  708.         return flags.find("EXECUTE") != -1
  709.  
  710. def hd(src, start=0, length=16):
  711.     """Hexdump formula seen at http://code.activestate.com/recipes/142812-hex-dumper"""
  712.     FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])
  713.     result=[]
  714.     for i in xrange(0, len(src), length):
  715.         s = src[i:i+length]
  716.         hexa = ' '.join(["%02x"%ord(x) for x in s])
  717.         printable = s.translate(FILTER)
  718.         result.append("0x%08x   %-*s   %s\n" % (i+start, length*3, hexa, printable))
  719.     return ''.join(result)
  720.  
  721. def find_space(addr_space, procs, mod_base):
  722.     """Search for an address space (usually looking for a GUI process)"""
  723.     if addr_space.is_valid_address(mod_base):
  724.         return addr_space
  725.     for proc in procs:
  726.         ps_ad = proc.get_process_address_space()
  727.         if ps_ad != None:
  728.             if ps_ad.is_valid_address(mod_base):
  729.                 return ps_ad
  730.     return None
  731.  
  732. def get_disasm_text(data, start, count = 10, stoponret=False):
  733.     """Return a string representation of disassembled instructions"""
  734.     ret = ""
  735.     n = 0
  736.     if sys.modules.has_key('distorm3'):
  737.         iterable = distorm3.DecodeGenerator(start, data, distorm3.Decode32Bits)
  738.         for (offset, size, instruction, hexdump) in iterable:
  739.             if n == count:
  740.                 break
  741.             ret += "%.8x: %-32s %s\n" % (offset, hexdump, instruction)
  742.             # Optionally stop when reaching the end of a function
  743.             if stoponret and instruction.startswith("RET"):
  744.                 break
  745.             n += 1
  746.     return ret
  747.  
  748. def get_yara_text(data, hits, start):
  749.     """Format YARA hits for viewing. Note: YARA 1.4a matches are different than 1.4"""
  750.     ret = ""
  751.     if not hits:
  752.         return ret
  753.     for hit in hits:
  754.         ret += "YARA rule: {0}\n".format(hit.rule)
  755.         if hit.meta.has_key('description'):
  756.             ret += "Description: {0}\n".format(hit.meta['description'])
  757.  
  758.         # Account for the API change from Yara Python 1.4 to 1.4.a
  759.         if isinstance(hit.strings, list):
  760.             hit_strings = [(key, val) for (key, _, val) in hit.strings]
  761.         elif isinstance(hit.strings, dict):
  762.             hit_strings = [(key, val) for (key, val) in hit.strings.items()]
  763.         else:
  764.             return ret
  765.  
  766.         for (key, val) in hit_strings:
  767.             makehex = False
  768.             for c in val:
  769.                 if c not in string.printable:
  770.                     makehex = True
  771.                     break
  772.             if makehex:
  773.                 val = binascii.hexlify(val)
  774.             # Get a temporary buffer of the first 128 characters
  775.             buffer = data[key:key+128]
  776.             if hit.meta.has_key('null_string'):
  777.                 if buffer.find("\x00") != -1:
  778.                     val = buffer[0:buffer.find("\x00")]
  779.             ret += "Hit: {0}\n".format(val)
  780.             ret += "{0}\n".format(hd(buffer, start+key))
  781.     return ret
  782.  
  783. def get_flags(flags, val):
  784.     """ text representation of flags from a dictionary """
  785.     if flags.has_key(val):
  786.         return flags[val]
  787.     else:
  788.         return [f for f in flags if (flags[f] | val == val)]
  789.  
  790. def get_sorted_mods(modlist):
  791.     """Return a tuple of modules (LDR_MODULEs) info"""
  792.     mods = {}
  793.     for mod in modlist:
  794.         # Try using the base name first, otherwise strip the file name from the full path
  795.         if not mod.BaseDllName:
  796.             name = os.path.basename(str(mod.FullDllName))
  797.         else:
  798.             name = str(mod.BaseDllName)
  799.         mods[mod.DllBase] = (mod, name.lower())
  800.     mod_addrs = sorted(mods.keys())
  801.     return mods, mod_addrs
  802.  
  803. def get_mem_size(p, start):
  804.     """Given a starting address in process memory, return the base and size of the region"""
  805.     for vad in p.VadRoot.traverse():
  806.         if vad == None:
  807.             continue
  808.  
  809.         # Find the start and end range
  810.         startvpn = vad.StartingVpn << 12
  811.         endvpn = ((vad.EndingVpn + 1) << 12) - 1
  812.  
  813.         if startvpn <= start and endvpn > start:
  814.             return (startvpn, (endvpn-startvpn))
  815.  
  816.     return None
  817.  
  818. def find_module(modlist, mod_addrs, addr):
  819.     """Modified version of BDG's binary search for modules. Note that modlist and mod_addrs
  820.    must be sorted in order of the module base address."""
  821.     pos = bisect_right(mod_addrs, addr) - 1
  822.     if pos == -1: return None
  823.     mod,name = modlist[mod_addrs[pos]]
  824.  
  825.     if (addr >= mod.DllBase and addr <  mod.DllBase + mod.SizeOfImage):
  826.         return mod
  827.     else:
  828.         return None
  829.  
  830. def find_module_by_name(mods, names):
  831.     """Lookup a module by its name"""
  832.     for mod in mods:
  833.         if str(mod.BaseDllName).lower() in names:
  834.             return mod
  835.     return None
  836.  
  837. def find_module_by_name_ex(mods, names):
  838.     """Lookup a module by its name"""
  839.     ret = []
  840.     for mod in mods:
  841.         for name in names:
  842.             if str(mod.BaseDllName).lower() == name.lower():
  843.                 ret.append(mod)
  844.     return ret
  845.  
  846. def get_vad_data(ps_ad, start, end):
  847.     """Returns the data for a given VAD range. Optimizations by Ryan Smith @ hustlelabs.com"""
  848.     range_data = ""
  849.     num_pages = (end - start + 1) >> 12
  850.     ps_ad_is_valid_address = ps_ad.is_valid_address
  851.     ps_ad_read = ps_ad.read
  852.     blank_page = '\x00' * 0x1000
  853.     pages_one = [(ps_ad_read(start + index * 0x1000, 0x1000) if ps_ad_is_valid_address(start + index * 0x1000) else blank_page) for index in xrange(num_pages)]
  854.     if None in pages_one:
  855.         pages_one = [a_page if a_page != None else blank_page for a_page in pages_one]
  856.     return ''.join(pages_one)
  857.  
  858. def get_malware_space(config, astype=None):
  859.     """Returns an address space with malware types"""
  860.     addr_space = utils.load_as(config, astype) if astype else utils.load_as(config)
  861.     for cls in [_IMAGE_IMPORT_DESCRIPTOR, _IMAGE_EXPORT_DIRECTORY, _LDR_DATA_TABLE_ENTRY, _PSP_CID_TABLE, _EPROCESS, _IMAGE_DOS_HEADER, _IMAGE_NT_HEADERS, _IMAGE_SECTION_HEADER]:
  862.         addr_space.profile.object_classes[cls.__name__] = cls
  863.     addr_space.profile.add_types(malware_types)
  864.     return addr_space
  865.  
  866. def get_obj_name(space, object):
  867.     """ Return the name from OBJECT_HEADER """
  868.  
  869.     object_obj = obj.Object("_OBJECT_HEADER", \
  870.         offset = object.obj_offset - \
  871.         space.profile.get_obj_offset('_OBJECT_HEADER', 'Body'),
  872.         vm = space)
  873.  
  874.     object_obj.kas = space
  875.     return object_obj.get_object_name() or "(unnamed)"
  876.  
  877. #--------------------------------------------------------------------------------
  878. # malfind plug-in
  879. #--------------------------------------------------------------------------------
  880. class Malfind(procdump.ProcExeDump):
  881.     "[MALWARE] Find hidden and injected code"
  882.  
  883.     def __init__(self, config, *args):
  884.         procdump.ProcExeDump.__init__(self, config, *args)
  885.         config.add_option('YARA-RULES', short_option='Y', default=None,
  886.                           help='Use YARA rules in addition to finding injected code')
  887.         config.add_option('YARA-RULES-ONLY', short_option='y', default=None,
  888.                           help='Use YARA rules only')
  889.         config.add_option("KERNEL", short_option='K', default=False,
  890.                           action='store_true', help='Scan kernel modules')
  891.         config.add_option("SCAN", short_option = 'S', default=False,
  892.                           action = 'store_true', help = 'Use PSScan')
  893.  
  894.     def rebuild(self, addr_space, start):
  895.         """Use the ProcExeDump's get_image to rebuild a PE in memory"""
  896.         pedata = ''
  897.         if not addr_space.is_valid_address(start):
  898.             return pedata
  899.         for offset, code in self.get_image(sys.stdout, addr_space, start):
  900.             # Pad out pedata long enough to contain the new code
  901.             pedata += "\x00" * (len(code) + offset - len(pedata))
  902.             # Swap out whatever's in pedata so far for the new code
  903.             pedata = pedata[:offset] + code + pedata[offset + len(code):]
  904.         return pedata
  905.  
  906.     def get_vads(self, proc):
  907.         """Generator for VADs in a process"""
  908.  
  909.         ps_ad = proc.get_process_address_space()
  910.         if ps_ad != None:
  911.             for vad in proc.VadRoot.traverse():
  912.                 if vad == None:
  913.                     continue
  914.    
  915.                 # Find the start and end range
  916.                 start = vad.StartingVpn << 12
  917.                 end = ((vad.EndingVpn + 1) << 12) - 1
  918.    
  919.                 if start > 0xFFFFFFFF or end > (0xFFFFFFFF << 12):
  920.                     continue
  921.                 data = get_vad_data(ps_ad, start, end)
  922.                 if data != "":
  923.                     yield (ps_ad, start, end, vad.Tag, vad.Flags.Protection >> 24, data)
  924.  
  925.     def calculate(self):
  926.         rules = None
  927.         config = self._config
  928.        
  929.         if config.DUMP_DIR == None:
  930.             debug.error("Please specify a dump directory (--dump-dir)")
  931.         if not os.path.isdir(config.DUMP_DIR):
  932.             debug.error(config.DUMP_DIR + " is not a directory")
  933.         if config.YARA_RULES != None or config.YARA_RULES_ONLY != None:
  934.             if sys.modules.has_key('yara'):
  935.                 stuff = config.YARA_RULES if config.YARA_RULES else config.YARA_RULES_ONLY
  936.                 # Compile the Yara rules from a specified file on disk
  937.                 if os.path.isfile(stuff):
  938.                     rules = yara.compile(stuff)
  939.                 else:
  940.                     # Compile the Yara rules from a string passed on command line
  941.                     # If the input starts with '{' or '/' then its a byte string or regex,
  942.                     # in which case we don't use quotes to wrap it.
  943.                     if stuff.startswith("{") or stuff.startswith("/"):
  944.                         s = stuff
  945.                     else:
  946.                         s = '"' + stuff + '"'
  947.                     rules  = yara.compile(sources={
  948.                     'namespace1' : 'rule z1 {strings: $a = ' + s + ' condition: $a}',
  949.                     })
  950.         # We require YARA signatures for kernel mode
  951.         elif config.KERNEL:
  952.             debug.error("You must use YARA rules for kernel mode!")
  953.  
  954.         addr_space = get_malware_space(config)
  955.  
  956.         if config.KERNEL:
  957.             mods  = list(modules.lsmod(addr_space))
  958.             procs = list(tasks.pslist(addr_space))
  959.             for mod in mods:
  960.                 space = find_space(addr_space, procs, mod.DllBase)
  961.                 if space != None:
  962.                     data = space.zread(mod.DllBase, mod.SizeOfImage)
  963.                     hits = rules.match(data=data)
  964.                     if len(hits):
  965.                         yield mod.BaseDllName, None, mod.DllBase, (mod.DllBase + mod.SizeOfImage), None, None, '-', hits, data
  966.         else:
  967.             if self._config.OFFSET != None:
  968.                 procs = [self.virtual_process_from_physical_offset(addr_space, self._config.OFFSET)]
  969.             elif self._config.SCAN:
  970.                 scan_procs = list(filescan.PSScan(self._config).calculate())
  971.                 procs = []
  972.                 for task in scan_procs:
  973.                     procs.append(self.virtual_process_from_physical_offset(addr_space, task.obj_offset))
  974.             else:
  975.                 procs = list(self.filter_tasks(tasks.pslist(addr_space)))
  976.  
  977.             for proc in procs:
  978.  
  979.                 # get the DLLs so we know what to whitelist
  980.                 if proc.Peb.Ldr.InLoadOrderModuleList:
  981.                     dll_bases = [l.DllBase for l in proc.Peb.Ldr.InLoadOrderModuleList.list_of_type("_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks")]
  982.                 else:
  983.                     dll_bases = []
  984.  
  985.                 image_name = proc.ImageFileName
  986.  
  987.                 if self._config.OFFSET:
  988.                     offset = proc.obj_offset
  989.                 else:        
  990.                     offset = addr_space.vtop(proc.obj_offset)
  991.  
  992.                 for ps_ad, start, end, tag, prx, data in self.get_vads(proc):
  993.    
  994.                     # Ignore blocks of all zero
  995.                     if data.count(chr(0)) == len(data):
  996.                         continue
  997.    
  998.                     suspicious = False
  999.                     hits = []
  1000.    
  1001.                     # Rebuild the data as PE if necessary
  1002.                     if data[0:2] == 'MZ':
  1003.                         try:
  1004.                             pedata = self.rebuild(ps_ad, start)
  1005.                         except:
  1006.                             pass
  1007.                         else:
  1008.                             data = pedata
  1009.                         # flag exeuctable PE's *not* in the DLLs list. Note: sometimes the
  1010.                         # DLL list is empty because the PEB areas are paged. in this case, we
  1011.                         # would end up flagging all DLLs in the process, so check for that too.
  1012.                         if dll_bases and start not in dll_bases and can_exec(prx):
  1013.                             suspicious = True
  1014.    
  1015.                     # Scan the memory with YARA
  1016.                     if rules != None:
  1017.                         hits = rules.match(data=data)
  1018.    
  1019.                     # Keep executable short vads
  1020.                     if (tag == "VadS") and can_exec(prx):
  1021.                         suspicious = True
  1022.                    
  1023.                     if suspicious:
  1024.                         if self._config.YARA_RULES_ONLY != None: continue
  1025.                     else:
  1026.                         if len(hits) == 0: continue
  1027.    
  1028.                     # Save the data to a file
  1029.                     fname = "{0}.{1:x}.{2:08x}-{3:08x}.dmp".format(image_name, offset, start, end)
  1030.                     dump_path = os.path.join(config.DUMP_DIR, fname)
  1031.    
  1032.                     f = open(dump_path, 'wb')
  1033.                     f.write(data)
  1034.                     f.close()
  1035.    
  1036.                     yield image_name, proc.UniqueProcessId, start, end, tag, prx, dump_path, hits, data
  1037.  
  1038.     def render_text(self, outfd, data):
  1039.         outfd.write("{0:<20} {1:<6} {2:10} {3:10} {4:<8} {5:<6} {6}\n".format(
  1040.             'Name', 'Pid', 'Start', 'End', 'Tag', 'Hits', 'Protect'))
  1041.  
  1042.         for (name,pid,start,end,tag,prx,fname,hits,chunk) in data:
  1043.             outfd.write("{0:<20} {1:<6} {2:#010x} {3:<#010x} {4:<8} {5:<6} {6}\n".format(
  1044.                 name,
  1045.                 pid or '',
  1046.                 start,
  1047.                 end,
  1048.                 tag or '',
  1049.                 len(hits),
  1050.                 PROTECT_FLAGS[prx] if prx else ''))
  1051.  
  1052.             outfd.write("Dumped to: {0}\n".format(fname))
  1053.             # Don't hexdump if we have YARA hits, bc we'll print individual hexdumps for each hit
  1054.             if len(hits) == 0:
  1055.                 outfd.write("{0}\n".format(hd(chunk[0:128],start)))
  1056.             # Disasm if the code is marked as executable
  1057.             if prx and can_exec(prx) and chunk[0:2] != 'MZ':
  1058.                 outfd.write("Disassembly:\n{0}\n".format(get_disasm_text(chunk, start)))
  1059.             outfd.write("{0}\n".format(get_yara_text(chunk, hits, start)))
  1060.  
  1061. #--------------------------------------------------------------------------------
  1062. # svcscan plug-in
  1063. #--------------------------------------------------------------------------------
  1064.  
  1065. SERVICE_TYPES = dict(
  1066.   SERVICE_KERNEL_DRIVER = 0x01,
  1067.   SERVICE_FILE_SYSTEM_DRIVER = 0x02,
  1068.   SERVICE_WIN32_OWN_PROCESS = 0x10,
  1069.   SERVICE_WIN32_SHARE_PROCESS = 0x20,
  1070.   SERVICE_INTERACTIVE_PROCESS = 0x100,
  1071. )
  1072.  
  1073. SERVICE_STATES = dict(
  1074.   SERVICE_STOPPED = 0x01,
  1075.   SERVICE_START_PENDING = 0x02,
  1076.   SERVICE_STOP_PENDING = 0x3,
  1077.   SERVICE_RUNNING = 0x4,
  1078.   SERVICE_CONTINUE_PENDING = 0x5,
  1079.   SERVICE_PAUSE_PENDING = 0x6,
  1080.   SERVICE_PAUSED = 0x7,
  1081. )
  1082.  
  1083. class SvcScan(Malfind):
  1084.     "[MALWARE] Scan for Windows services"
  1085.  
  1086.     def __init__(self, config, *args):
  1087.         Malfind.__init__(self, config, *args)
  1088.         config.remove_option("DUMP-DIR")
  1089.         config.remove_option("PID")
  1090.         config.remove_option("UNSAFE")
  1091.         config.remove_option("SCAN")
  1092.         config.remove_option("KERNEL")
  1093.         config.remove_option("YARA-RULES")
  1094.         config.remove_option("YARA-RULES-ONLY")
  1095.         config.remove_option("OFFSET")
  1096.  
  1097.     def get_services(self, addr_space):
  1098.         """Generator that yields information on services found in the SCM process's memory"""
  1099.  
  1100.         TagOffset = addr_space.profile.get_obj_offset('_SERVICE_RECORD_LEGACY', 'TagSignature')
  1101.  
  1102.         procs = [p for p in tasks.pslist(addr_space) if str(p.ImageFileName) == "services.exe"]
  1103.  
  1104.         rules = yara.compile(sources={
  1105.                     'namespace1' : 'rule legacy {strings: $a = "sErv" condition: $a}',
  1106.                     'namespace2' : 'rule recent {strings: $a = "serH" condition: $a}',
  1107.                 })
  1108.  
  1109.         # Stores the service record struct with the highest Order
  1110.         highest_rec = None
  1111.  
  1112.         # Cycle through the process memory segments
  1113.         for proc in procs:
  1114.             for info in self.get_vads(proc):
  1115.                 ps_ad = info[0]
  1116.                 start = info[1]
  1117.                 data  = info[5]
  1118.                 # Find all the service record signatures
  1119.                 for hit in rules.match(data=data):
  1120.                     for (offset, _, _) in hit.strings:
  1121.                         if hit.rule == "legacy":
  1122.                             rec = obj.Object("_SERVICE_RECORD_LEGACY", start + offset - TagOffset, ps_ad)
  1123.                             # Do a sanity check on the order
  1124.                             if rec and rec.Order > 0 and rec.Order < 0xFFFF:
  1125.                                 yield rec
  1126.                         elif hit.rule == "recent":
  1127.                             rec = obj.Object('_SERVICE_HEADER', start + offset, ps_ad)
  1128.                             # Just a few sanity checks....
  1129.                             if not rec or \
  1130.                                 not ps_ad.is_valid_address(rec.Ser) or \
  1131.                                 rec.Ser.Order > 0xFFFF or \
  1132.                                 rec.Ser.State not in SERVICE_STATES.values():
  1133.                                 continue
  1134.                             # Save the record with the highest Order
  1135.                             if not highest_rec or highest_rec.Order < rec.Ser.Order:
  1136.                                 highest_rec = rec.Ser
  1137.  
  1138.         # Walk the list starting at the highest Order on down to 0
  1139.         if highest_rec:
  1140.             rec = highest_rec
  1141.             while rec and rec.PrevEntry:
  1142.                 yield rec
  1143.                 rec = rec.PrevEntry
  1144.  
  1145.     def calculate(self):
  1146.         addr_space = get_malware_space(self._config)
  1147.  
  1148.         for info in self.get_services(addr_space):
  1149.             yield info
  1150.  
  1151.     def render_text(self, outfd, data):
  1152.  
  1153.         outfd.write("{0:<12} {1:<8} {2:<8} {3:<16} {4:<40} {5:<30} {6:<20} {7}\n".format(
  1154.             "Record", "Order", "Pid", "Name", "DisplayName", "Type", "State", "Path"))
  1155.  
  1156.         for rec in data:
  1157.  
  1158.             Type  = '|'.join(get_flags(SERVICE_TYPES, rec.Type))
  1159.             State = '|'.join(get_flags(SERVICE_STATES, rec.State))
  1160.  
  1161.             if 'SERVICE_KERNEL_DRIVER' in Type or 'SERVICE_FILE_SYSTEM_DRIVER' in Type:
  1162.                 Binary = wctomb(rec.Binary1, rec.obj_vm)
  1163.                 ProcId = '--------'
  1164.             else:
  1165.                 Binary = wctomb(rec.Binary2.ServicePath, rec.obj_vm)
  1166.                 ProcId = rec.Binary2.ProcessId
  1167.  
  1168.             if Binary == None:
  1169.                 Binary = '--------'
  1170.  
  1171.             outfd.write("{0:<#12x} {1:<#8x} {2:<8} {3:16} {4:40} {5:<30} {6:<20} {7}\n".format(
  1172.                 rec.v(),
  1173.                 rec.Order,
  1174.                 ProcId,
  1175.                 repr(wctomb(rec.ServiceName, rec.obj_vm)),
  1176.                 repr(wctomb(rec.DisplayName, rec.obj_vm)),
  1177.                 Type,
  1178.                 State,
  1179.                 Binary,
  1180.                 ))
  1181.  
  1182. #--------------------------------------------------------------------------------
  1183. # ldr_modules plug-in
  1184. #--------------------------------------------------------------------------------
  1185. class LdrModules(taskmods.DllList):
  1186.     "[MALWARE] Detect unlinked DLLs"
  1187.  
  1188.     def __init__(self, config, *args):
  1189.         taskmods.DllList.__init__(self, config, *args)
  1190.  
  1191.     def list_load_modules(self, task):
  1192.         """Generator to yield the DLLs in load order"""
  1193.         if task.UniqueProcessId and task.Peb.Ldr.InLoadOrderModuleList:
  1194.             for l in task.Peb.Ldr.InLoadOrderModuleList.list_of_type(
  1195.                 "_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks"):
  1196.                 yield l
  1197.  
  1198.     def list_init_modules(self, task):
  1199.         """Generator to yield the DLLs in initialization order"""
  1200.         if task.UniqueProcessId and task.Peb.Ldr.InInitializationOrderModuleList:
  1201.             for l in task.Peb.Ldr.InInitializationOrderModuleList.list_of_type(
  1202.                 "_LDR_DATA_TABLE_ENTRY", "InInitializationOrderLinks"):
  1203.                 yield l
  1204.  
  1205.     def list_mem_modules(self, task):
  1206.         """Generator to yield the DLLs in memory order"""
  1207.         if task.UniqueProcessId and task.Peb.Ldr.InMemoryOrderModuleList:
  1208.             for l in task.Peb.Ldr.InMemoryOrderModuleList.list_of_type(
  1209.                 "_LDR_DATA_TABLE_ENTRY", "InMemoryOrderLinks"):
  1210.                 yield l
  1211.  
  1212.     def list_mapped_files(self, p, pe_only=True, get_data=False):
  1213.         """Returns a dictionary of mapped files in a process. The keys are base addresses
  1214.        of the mapped files and the value is a tuple containing the file name and data"""
  1215.         mapped_files = {}
  1216.         ps_ad = p.get_process_address_space()
  1217.         ps_ad_read = ps_ad.read
  1218.         # Enumerate the VAD entries
  1219.         for vad in p.VadRoot.traverse():
  1220.             if vad == None:
  1221.                 continue
  1222.             # We're only looking for VADs with mapped files
  1223.             try:
  1224.                 FO = vad.get_file_object()
  1225.             except AttributeError:
  1226.                 continue
  1227.             # Get the start and end address
  1228.             start = vad.StartingVpn << 12
  1229.             end = ((vad.EndingVpn + 1) << 12) - 1
  1230.             if start > 0x7FFFFFFF:
  1231.                 continue
  1232.             # Return only PE files or all files?
  1233.             if not pe_only or ps_ad_read(start, 2) == 'MZ':
  1234.                 # Return the file data also?
  1235.                 if get_data:
  1236.                     mapped_files[start] = (FO.FileName, get_vad_data(ps_ad, start, end))
  1237.                 else:
  1238.                     mapped_files[start] = (FO.FileName, None)
  1239.         return mapped_files
  1240.  
  1241.     def calculate(self):
  1242.         addr_space = get_malware_space(self._config)
  1243.  
  1244.         if self._config.OFFSET != None:
  1245.             procs = [self.virtual_process_from_physical_offset(addr_space, self._config.OFFSET)]
  1246.         else:
  1247.             procs = self.filter_tasks(tasks.pslist(addr_space))
  1248.  
  1249.         for p in procs:
  1250.             # Build a dictionary for all three PEB lists where the
  1251.             # keys are base address and dll name are the values
  1252.             inloadorder = dict((mod.DllBase.v(), mod) for mod in list(self.list_load_modules(p)))
  1253.             ininitorder = dict((mod.DllBase.v(), mod) for mod in list(self.list_init_modules(p)))
  1254.             inmemorder  = dict((mod.DllBase.v(), mod) for mod in list(self.list_mem_modules(p)))
  1255.             # For each of the memory mapped files, check if its unlinked
  1256.             mapped_files = self.list_mapped_files(p)
  1257.             for map in mapped_files.keys():
  1258.                 try:
  1259.                     load_path = inloadorder[map]
  1260.                 except KeyError:
  1261.                     load_path = None
  1262.                 try:
  1263.                     init_path = ininitorder[map]
  1264.                 except KeyError:
  1265.                     init_path = None
  1266.                 try:
  1267.                     mem_path = inmemorder[map]
  1268.                 except KeyError:
  1269.                     mem_path = None
  1270.                 yield p, map, mapped_files[map][0], load_path, init_path, mem_path
  1271.  
  1272.     def render_text(self, outfd, data):
  1273.         outfd.write("{0:<8} {1:<20} {2:<12} {3:<8} {4:<8} {5:<8} {6}\n".format('Pid', 'Process', 'Base', 'InLoad', 'InInit', 'InMem', 'MappedPath'))
  1274.         for p, base, mapped_path, load_path, init_path, mem_path in data:
  1275.             inload = True if load_path != None else False
  1276.             ininit = True if init_path != None else False
  1277.             inmem = True if mem_path != None else False
  1278.             outfd.write("{0:<8} {1:<20} {2:<#12x} {3:<8} {4:<8} {5:<8} {6}\n".format(p.UniqueProcessId, p.ImageFileName, base, inload, ininit, inmem, mapped_path))
  1279.             if self._config.verbose:
  1280.                 if load_path != None:
  1281.                     outfd.write("  Load Path: {0} : {1}\n".format(load_path.FullDllName, load_path.BaseDllName))
  1282.                 if init_path != None:
  1283.                     outfd.write("  Init Path: {0} : {1}\n".format(init_path.FullDllName, init_path.BaseDllName))
  1284.                 if mem_path != None:
  1285.                     outfd.write("  Mem Path:  {0} : {1}\n".format(mem_path.FullDllName, mem_path.BaseDllName))  
  1286.  
  1287. #--------------------------------------------------------------------------------
  1288. # impscan plug-in
  1289. #--------------------------------------------------------------------------------
  1290. class ImpScan(Malfind):
  1291.     "[MALWARE] Scan a module for imports (API calls)"
  1292.  
  1293.     def __init__(self, config, *args):
  1294.         Malfind.__init__(self, config, *args)
  1295.         config.add_option('ADDR', short_option = 'a', default=None,
  1296.                           help='Address to begin scanning (hex)',
  1297.                           action='store', type='int')
  1298.         config.add_option('SIZE', short_option = 's', default=None,
  1299.                           help='Size of memory to scan from base address',
  1300.                           action='store', type='int')
  1301.         config.remove_option("OFFSET")
  1302.         config.remove_option("YARA-RULES")
  1303.         config.remove_option("YARA-RULES-ONLY")
  1304.         config.remove_option("KERNEL")
  1305.  
  1306.     def automate_ida(self, ofname, names, funcs, hooks):
  1307.         """This function creates IDC labels and executes IDA. Note: idal must be in your PATH."""
  1308.  
  1309.         # Build a full path to the IDC file (required by idag.exe on Windows)
  1310.         idcfile = os.path.join(os.getcwd(), ofname) + ".idc"
  1311.  
  1312.         print "Dumping IDC file to {0}".format(idcfile)
  1313.         f = open(idcfile, "w")
  1314.         if f == None:
  1315.             return
  1316.  
  1317.         # This is the IDC header
  1318.         f.write("#include <idc.idc>\nstatic main(void) {\n")
  1319.  
  1320.         # This is one line of IDC for each function
  1321.         if funcs != None:
  1322.             for addr in funcs:
  1323.                 f.write("   MakeFunction(0x{0:08X}, BADADDR);\n".format(addr))
  1324.  
  1325.         # Write the IAT labels
  1326.         if names != None:
  1327.             for addr,func_name in names.iteritems():
  1328.                 f.write("   MakeDword(0x{0:08X});\n".format(addr))
  1329.                 f.write("   MakeName(0x{0:08X}, \"{1}\");\n".format(addr, func_name))
  1330.  
  1331.         # Write the hook labels (if any)
  1332.         if hooks != None:
  1333.             for addr,func_name in hooks.iteritems():
  1334.                 f.write("   MakeName(0x{0:08X}, \"Hook{1}\");\n".format(addr, func_name))
  1335.                 f.write("   MakeFunction(0x{0:08X}, BADADDR);\n".format(addr))
  1336.  
  1337.         # This is the IDC trailer
  1338.         f.write("Exit(0);}")
  1339.         f.close()
  1340.  
  1341.         # Set the tvision environment variable so no interaction is needed
  1342.         os.environ['TVHEADLESS'] = '1'
  1343.  
  1344.         # If we're on Windows, use idag.exe, otherwise use idal
  1345.         if sys.platform == "win32":
  1346.             ida = "idag"
  1347.         else:
  1348.             ida = "idal"
  1349.  
  1350.         try:
  1351.             proc = subprocess.Popen([ida, '-c', '-A', "-S{0}".format(idcfile), ofname], stdout=subprocess.PIPE)
  1352.         except OSError, e:
  1353.             print "Cannot launch IDA: " + str(e)
  1354.             print "Please make sure idal or idag.exe is in your PATH"
  1355.         else:  
  1356.             print proc.communicate()[0]
  1357.  
  1358.     def revert_and_split(self, mod_name, func_name):
  1359.         """This function reverts forwarded imports to the original name"""
  1360.         forwarded_imports = {
  1361.             "ntdll.dll!RtlGetLastWin32Error" : "kernel32.dll!GetLastError",
  1362.             "ntdll.dll!RtlSetLastWin32Error" : "kernel32.dll!SetLastError",
  1363.             "ntdll.dll!RtlRestoreLastWin32Error" : "kernel32.dll!SetLastError",
  1364.             "ntdll.dll!RtlAllocateHeap" : "kernel32.dll!HeapAlloc",
  1365.             "ntdll.dll!RtlReAllocateHeap" : "kernel32.dll!HeapReAlloc",
  1366.             "ntdll.dll!RtlFreeHeap" : "kernel32.dll!HeapFree",
  1367.             "ntdll.dll!RtlEnterCriticalSection" : "kernel32.dll!EnterCriticalSection",
  1368.             "ntdll.dll!RtlLeaveCriticalSection" : "kernel32.dll!LeaveCriticalSection",
  1369.             "ntdll.dll!RtlDeleteCriticalSection" : "kernel32.dll!DeleteCriticalSection",
  1370.             "ntdll.dll!RtlZeroMemory" : "kernel32.dll!ZeroMemory",
  1371.             "ntdll.dll!RtlSizeHeap" : "kernel32.dll!HeapSize",
  1372.             "ntdll.dll!RtlUnwind" : "kernel32.dll!RtlUnwind"
  1373.         }
  1374.         fullname = "%s!%s" % (mod_name, func_name)
  1375.         if forwarded_imports.has_key(fullname):
  1376.             mod_name  = forwarded_imports[fullname].split('!')[0]
  1377.             func_name = forwarded_imports[fullname].split('!')[1]
  1378.         return mod_name, func_name
  1379.  
  1380.     def call_scan(self, ps_ad, data, base):
  1381.         """Disassemble a block of data and yield possible calls to imported functions. We're
  1382.        looking for instructions such as these:
  1383.  
  1384.        CALL [0x1000400]
  1385.        JMP  [0x1000400]
  1386.  
  1387.        The 0x1000400 address is an entry in the IAT. It stores a DWORD which is the location
  1388.        of the API function being called.
  1389.        """
  1390.         ps_ad_read = ps_ad.read
  1391.         data_len = len(data)
  1392.  
  1393.         for op in distorm3.Decompose(base, data, distorm3.Decode32Bits):
  1394.  
  1395.             if op.valid and ((op.flowControl == 'FC_CALL' and op.mnemonic == "CALL") or (op.flowControl == 'FC_UNC_BRANCH' and op.mnemonic == "JMP")) and op.operands[0].type == 'AbsoluteMemoryAddress':
  1396.                
  1397.                 # Chop it to 32 bits, this will need changing for 64 bit support
  1398.                 const = (op.operands[0].disp) & 0xFFFFFFFF
  1399.  
  1400.                 # The memory location must be inside the range or its an error
  1401.                 if (const < base) or (const > base + data_len):
  1402.                     continue
  1403.  
  1404.                 try:
  1405.                     call_dest = struct.unpack("=I", ps_ad_read(const, 4))[0]
  1406.                 except:
  1407.                     continue
  1408.  
  1409.                 yield op.address, const, call_dest
  1410.  
  1411.     def vicinity_scan(self, ps_ad, imports, exports, base, end_addr, forward=True):
  1412.         """For whatever reason, the generic call scanner above may not pick up all calls to
  1413.        imported functions. However, the IAT is usually grouped together. This function scans
  1414.        forward or backward from the lowest and highest known IAT entry to find other entries
  1415.        which are near by."""
  1416.         keys = imports.keys()
  1417.         if not keys:
  1418.             return
  1419.         keys.sort()
  1420.         # Scan forward from the lowest IAT entry or backward from the highest IAT entry
  1421.         starting_point = keys[0] if forward else keys[len(keys)-1]
  1422.         stopcount = 5
  1423.         i = 0
  1424.         while (stopcount > 0) and (i < 0x2000):
  1425.             if forward:
  1426.                 const = starting_point + (i*4)
  1427.             else:
  1428.                 const = starting_point - (i*4)
  1429.             call_dest = struct.unpack("=I", ps_ad.read(const, 4))[0]
  1430.             if (call_dest < base) or (call_dest > end_addr):
  1431.                 if self.check_import(imports, exports, call_dest, const):
  1432.                     stopcount = 5
  1433.                 else:
  1434.                     stopcount -= 1
  1435.             else:
  1436.                 stopcount -= 1
  1437.             i += 1
  1438.  
  1439.     def check_import(self, imports, exports, call_dest, const):
  1440.         """Returns true if a given destination leads to an exported function"""
  1441.         # Find out if the destination leads to an API address
  1442.         if not exports.has_key(call_dest):
  1443.             return False
  1444.         # Revert any forwarded exports
  1445.         (mod_name, func_name) = exports[call_dest]
  1446.         (mod_name, func_name) = self.revert_and_split(mod_name, func_name)
  1447.         # Obviously some APIs are called from multiple locations in a binary
  1448.         # Here, we skip the duplicate calls (we only need 1 to build the IAT)
  1449.         if not imports.has_key(const):
  1450.             imports[const] = (mod_name, func_name, call_dest)
  1451.             return True
  1452.         return False
  1453.  
  1454.     def find_functions(self, data, addr):
  1455.         """Scan a given data buffer for function prologues"""
  1456.         instruction_sets = [
  1457.             "\x55\x89\xE5", # PUSH EBP; MOV EBP, ESP
  1458.             "\x55\x8B\xEC", # PUSH EBP; MOV EBP, ESP
  1459.             "\x8B\xFF\x55\x8B\xEC", # MOV EDI, EDI; PUSH EBP; MOV EBP, ESP
  1460.             ]
  1461.         i = 0
  1462.         found = []
  1463.         for sets in instruction_sets:
  1464.             while data.find(sets) != -1:
  1465.                 offset = data.find(sets)
  1466.                 address = (addr + offset + i) & 0xffffffff
  1467.                 if address not in found:
  1468.                     found.append(address)
  1469.                 length = offset + len(sets)
  1470.                 i += length
  1471.                 data = data[length:]
  1472.         return found
  1473.  
  1474.     def get_all_imports(self, addr_space, data, base, exports):
  1475.         """Returns a dictionary where the keys are IAT entry addresses and the values
  1476.        are the names of the imported functions being called"""
  1477.         imports = {}
  1478.         for (_, const, call_dest) in self.call_scan(addr_space, data, base):
  1479.             self.check_import(imports, exports, call_dest, const)
  1480.         # Do the forward and reverse vicinity scans
  1481.         end_addr = base + len(data)
  1482.         self.vicinity_scan(addr_space, imports, exports, base, end_addr, True)
  1483.         self.vicinity_scan(addr_space, imports, exports, base, end_addr, False)
  1484.        
  1485.         return imports
  1486.  
  1487.     def rebase(self, addr_space, base, data):
  1488.         """Rebases a PE image by modifying the ImageBase field"""
  1489.         dos_header = obj.Object("_IMAGE_DOS_HEADER", offset = base, vm = addr_space)
  1490.         nt_header = dos_header.get_nt_header()
  1491.         optional_header_offset = nt_header.OptionalHeader.obj_offset - base
  1492.         img_base_offset = addr_space.profile.get_obj_offset('_IMAGE_OPTIONAL_HEADER', 'ImageBase')
  1493.         s = optional_header_offset + img_base_offset
  1494.         return data[0:s] + struct.pack("=I", base) + data[s+4:]
  1495.  
  1496.     def get_kernel_exports(self, addr_space, procs, mods):
  1497.         exports = dict()
  1498.         for mod in mods:
  1499.             space = find_space(addr_space, procs, mod.DllBase)
  1500.             if space == None:
  1501.                 continue
  1502.             for exp in mod.exports(space):
  1503.                 exports[exp.funcaddr] = (mod.BaseDllName, exp.get_name())
  1504.         return exports
  1505.  
  1506.     def get_process_exports(self, addr_space, mods):
  1507.         exports = dict()
  1508.         for mod in mods:
  1509.             for exp in mod.exports():
  1510.                 exports[exp.funcaddr] = (mod.BaseDllName, exp.get_name())
  1511.         return exports
  1512.  
  1513.     def calculate(self):
  1514.         t0 = time.time()
  1515.         config = self._config
  1516.    
  1517.         if config.DUMP_DIR == None:
  1518.             debug.error("Please specify a dump directory (--dump-dir)")
  1519.         if not os.path.isdir(config.DUMP_DIR):
  1520.             debug.error(config.DUMP_DIR + " is not a directory")
  1521.  
  1522.         addr_space = get_malware_space(config)
  1523.  
  1524.         apis  = dict()
  1525.         procs = list(tasks.pslist(addr_space))
  1526.  
  1527.         # If the user didn't specify a process with --pid, they must want kernel mode...
  1528.         if not config.PID:
  1529.             if config.ADDR == None:
  1530.                 debug.error("Please specify a module address in hex (--addr)")
  1531.  
  1532.             base = config.ADDR
  1533.  
  1534.             # Get a list of loaded kernel modules
  1535.             drivers = list(modules.lsmod(addr_space))
  1536.             mods, mod_addrs = get_sorted_mods(drivers)
  1537.  
  1538.             # If the base maps to a loaded module, get the size from the LDR_MODULE.
  1539.             # Otherwise the user MUST provide the size or we wont' know when to stop.
  1540.             try:
  1541.                 size = [mods[addr][0].SizeOfImage for addr in mod_addrs if mods[addr][0].DllBase.v() == base][0]
  1542.             except:
  1543.                 if config.SIZE == None:
  1544.                     debug.error("You must supply a size (--size)")
  1545.                 else:
  1546.                     size = config.SIZE
  1547.  
  1548.             # Find a space for the address
  1549.             space = find_space(addr_space, procs, base)
  1550.             if space == None:
  1551.                 debug.error("The supplied address is not valid")
  1552.  
  1553.             # Read the data at the supplied address. If rebuild doesn't work, just zread.
  1554.             data = self.rebuild(space, base)
  1555.            
  1556.             if not data:
  1557.                 data = space.zread(base, size)
  1558.  
  1559.             apis = self.get_kernel_exports(addr_space, procs, drivers)
  1560.        
  1561.             ofname = os.path.join(config.DUMP_DIR, "{0}.bin".format(base))
  1562.             addr_space = space
  1563.         else:
  1564.             # Currently we only support scanning one process at a time
  1565.             p = self.filter_tasks(tasks.pslist(addr_space))[0]
  1566.  
  1567.             # Discontinue if the process address space isn't valid
  1568.             ps_ad = p.get_process_address_space()
  1569.             if ps_ad == None:
  1570.                 debug.error("The process address space is invalid")
  1571.  
  1572.             if not p.Peb:
  1573.                 debug.error("The PEB is not accessible")
  1574.  
  1575.             # Get the loaded DLLs into a list
  1576.             mods = list(p.list_modules())
  1577.  
  1578.             # Now figure out the range of memory to scan for imports
  1579.             # This may be specified by the user with --ADDR and --SIZE or
  1580.             # alternately those opts can be left off, in which case we'll
  1581.             # scan the main module (*.exe) for imports
  1582.             if config.ADDR != None:
  1583.                 ret = get_mem_size(p, config.ADDR)
  1584.                 if not isinstance(ret, tuple):
  1585.                     debug.error("Cannot find the desired memory range")
  1586.                 base, size = ret
  1587.             # If the user didn't specify an address, they must want to analyze the main EXE
  1588.             else:
  1589.                 if len(mods) == 0:
  1590.                     debug.error("Cannot read DLL list")
  1591.                 base = mods[0].DllBase
  1592.                 size = mods[0].SizeOfImage
  1593.  
  1594.             if not ps_ad.is_valid_address(base):
  1595.                 debug.error("The desired base address {0:#x} is invalid!".format(base))
  1596.  
  1597.             # Can't continue if the data isn't available
  1598.             data = get_vad_data(ps_ad, base, base+size)
  1599.             if len(data) == 0:
  1600.                 debug.error("Cannot acquire the data!")
  1601.  
  1602.             apis = self.get_process_exports(ps_ad, mods)
  1603.  
  1604.             ofname = os.path.join(config.DUMP_DIR, "{0}-{1:x}-{2:x}.bin".format(p.UniqueProcessId, base, base+size))
  1605.             addr_space = ps_ad
  1606.  
  1607.         imports = self.get_all_imports(addr_space, data, base, apis)
  1608.        
  1609.         # Build the data for IDC statements
  1610.         funcs = self.find_functions(data, base)
  1611.  
  1612.         # Build a dictionary of imports for IDC statements
  1613.         names = dict((const, vals[1]) for (const, vals) in imports.items())
  1614.  
  1615.         for const, vals in imports.items():
  1616.             (mod_name, func_name, call_dest) = vals
  1617.             print "%x" % const, mod_name, func_name, "%x" % call_dest
  1618.  
  1619.         # If there's a PE at the base address, fix its PE header
  1620.         if data[0:2] == 'MZ':
  1621.             data = self.rebuild(addr_space, base)
  1622.             data = self.rebase(addr_space, base, data)
  1623.            
  1624.         f = open(ofname, "wb")
  1625.         f.write(data)
  1626.         f.close()
  1627.         self.automate_ida(ofname, names, funcs, None)
  1628.        
  1629.         print 'Finished after', time.time() - t0, 'seconds'
  1630.  
  1631.     def render_text(self, outfd, data):
  1632.         pass
  1633.  
  1634. #--------------------------------------------------------------------------------
  1635. # apihooks plug-in
  1636. #--------------------------------------------------------------------------------
  1637.  
  1638. # The values of each dictionary item is a list of tuples which are regexes
  1639. # in the format (process, srd_mod, dst_mod, function). If you specify
  1640. # (".*", ".*", ".*", ".*") then you essentially whitelist all possible hooks
  1641. # of the given type.
  1642.  
  1643. ignore_rules = dict(
  1644.     # Usermode IAT hook rules
  1645.     iat = [
  1646.         # Ignore hooks that point inside C runtime libraries
  1647.         (".*", ".*", "(msvcr|msvcp).+\.dll", ".*"),
  1648.         # Ignore hooks of WMI that point inside advapi32.dll
  1649.         (".*", "wmi.dll", "advapi32.dll", ".*"),
  1650.         # Ignore hooks of winsock that point inside ws2 and mswsock
  1651.         (".*", "WSOCK32.dll", "(WS2_32|MSWSOCK)\.dll", ".*"),
  1652.         # Ignore hooks of SCHANNEL* that point inside secur32.dll
  1653.         (".*", "schannel.dll", "secur32.dll", ".*"),
  1654.         # Ignore hooks that point inside known modules
  1655.         (".*", ".*", "(kernel32|ntdll|shimeng|kernelbase|shlwapi)", ".*"),
  1656.         # Handle some known forwarded imports
  1657.         (".*", ".*", ".*", "((Enter|Delete|Leave)CriticalSection|(Get|Set)LastError|Heap(ReAlloc|Free|Size|Alloc)|Rtl(Unwind|MoveMemory))"),
  1658.         ],
  1659.     # Usermode EAT hook rules
  1660.     eat = [
  1661.         # These modules have so many hooks its really not useful to check
  1662.         (".*", "(msvcp|msvcr|mfc|wbemcomn|fastprox)", ".*", ".*"),
  1663.         ],
  1664.     # Usermode Inline hook rules
  1665.     inline = [
  1666.         # Ignore hooks in the pywin32 service process
  1667.         ("pythonservice", ".*", ".*", ".*"),
  1668.         # Many legit hooks land inside these modules
  1669.         (".*", ".*", "(msvcr|wbemcomn|ntdll|kernel32|ole32|shlwapi|user32|gdi32|ws2_32|shell32)", ".*"),
  1670.         # Ignore hooks of the msvcrt DLLs
  1671.         (".*", "(msvc(p|r)\d{2}|mfc\d{2})\.dll", ".*", ".*"),
  1672.         # Ignore hooks of MD5Final, MD5Init, MD5Update that point inside advapi32
  1673.         (".*", ".*", "advapi32.dll", "MD5.+"),
  1674.         # Ignore hooks of common firefox components
  1675.         ("firefox\.exe", ".*", "(xul|mozcrt|nspr4)", ".*"),
  1676.         # Ignore hooks created by Parallels VM software
  1677.         (".*", "user32.dll", "prl_hook.dll", ".*"),
  1678.         # Ignore DLL registration functions
  1679.         (".*", ".*", ".*", "(DllCanUnloadNow|DllRegisterServer|DllUnregisterServer)"),
  1680.         ],
  1681.     # Kernel mode IAT hook rules
  1682.     iatk = [
  1683.         (".*", ".*", "(win32k\.sys|hal\.dll|dump_wmilib\.sys|ntkrnlpa\.exe|ntoskrnl\.exe)", ".*"),
  1684.         # Ignore hooks of the SCSI module which point inside the dump_scsiport module
  1685.         (".*", "scsiport\.sys", "dump_scsiport\.sys", ".*"),
  1686.         ],
  1687.     # Kernel mode EAT hook rules
  1688.     eatk = [
  1689.         ],
  1690.     # Kernel mode Inline hook rules
  1691.     inlinek = [
  1692.         # Ignore kernel hooks that point inside these modules
  1693.         (".*", ".*", "(hal.dll|ndis.sys|ntkrnlpa.exe|ntoskrnl.exe)", ".*"),
  1694.         ],
  1695.     )
  1696.  
  1697. class ApiHooks(procdump.ProcExeDump):
  1698.     "[MALWARE] Find API hooks"
  1699.  
  1700.     def __init__(self, config, *args):
  1701.         procdump.ProcExeDump.__init__(self, config, *args)
  1702.         config.remove_option("DUMP-DIR")
  1703.         config.remove_option("UNSAFE")
  1704.         config.add_option("KERNEL", short_option='K', default=False,
  1705.                           action='store_true', help='Scan kernel modules')
  1706.  
  1707.         # Whitelist to ignore API hooks  
  1708.         self.compiled_rules = self.compile()
  1709.         # Container for driver objects
  1710.         self.driver_objects = list()
  1711.  
  1712.     def compile(self):
  1713.         """By precompiling the regular expression rules, we save a lot of time"""
  1714.         compiled_rules = dict()
  1715.         for key, rules in ignore_rules.items():
  1716.             for rule in rules:
  1717.                 ruleset = ((re.compile(rule[0], re.I), re.compile(rule[1], re.I), re.compile(rule[2], re.I), re.compile(rule[3], re.I)))
  1718.                 if compiled_rules.has_key(key):
  1719.                     compiled_rules[key].append(ruleset)
  1720.                 else:
  1721.                     compiled_rules[key] = [ruleset]
  1722.         return compiled_rules
  1723.  
  1724.     def ignore(self, rule_key, process, src_mod, dst_mod, function):
  1725.         """Check if an API hook should be ignored or not, based on the precompiled regexes"""
  1726.         for rule in self.compiled_rules[rule_key]:
  1727.             if rule[0].search(process) != None and rule[1].search(src_mod) != None and rule[2].search(dst_mod) != None and rule[3].search(function) != None:
  1728.                 return True
  1729.         return False
  1730.  
  1731.     def get_section_from_mod(self, addr_space, mod, addr):
  1732.         """Return the name of the PE section in which a given VA resides"""
  1733.         dos_header = obj.Object("_IMAGE_DOS_HEADER", offset = mod.DllBase, vm = addr_space)
  1734.         nt_header = dos_header.get_nt_header()  
  1735.  
  1736.         for sect in nt_header.get_sections(False):
  1737.             start_va = mod.DllBase + sect.VirtualAddress
  1738.             if addr > start_va and addr < sect.Misc.VirtualSize + start_va:
  1739.                 return str(sect.Name)
  1740.         return ''
  1741.  
  1742.     def get_ucp_hooks(self, mods, mod_addrs, mod, src_mod_name, space):
  1743.         """Scan for calls to unknown code pages"""
  1744.  
  1745.         # Abort if we can't get the NT header
  1746.         dos_header = obj.Object("_IMAGE_DOS_HEADER", offset = mod.DllBase, vm = space)
  1747.         nt_header = dos_header.get_nt_header()  
  1748.         if nt_header == None:
  1749.             raise StopIteration
  1750.  
  1751.         # Parse the PE sections for this driver
  1752.         for sec in nt_header.get_sections(False):
  1753.  
  1754.             # Only check executable sections
  1755.             if sec.Characteristics & 0x20000000:
  1756.  
  1757.                 # Calculate the virtual address of this PE section in memory
  1758.                 sec_va = mod.DllBase + sec.VirtualAddress
  1759.  
  1760.                 # Extract the section's data and make sure we get it all
  1761.                 data = space.zread(sec_va, sec.Misc.VirtualSize)
  1762.  
  1763.                 if len(data) != sec.Misc.VirtualSize:
  1764.                     continue
  1765.  
  1766.                 # Disassemble instructions in the section
  1767.                 for op in distorm3.Decompose(sec_va, data, distorm3.Decode32Bits):
  1768.                     if op.valid and ((op.flowControl == 'FC_CALL' and op.mnemonic == "CALL") or (op.flowControl == 'FC_UNC_BRANCH' and op.mnemonic == "JMP")) and op.operands[0].type == 'AbsoluteMemoryAddress':
  1769.                         # This is ADDR, which is the IAT location
  1770.                         const = op.operands[0].disp & 0xFFFFFFFF
  1771.  
  1772.                         # Abort if ADDR is not a valid address
  1773.                         if not space.is_valid_address(const):
  1774.                             continue
  1775.  
  1776.                         # This is what [ADDR] points to - the absolute call destination
  1777.                         call_dest = struct.unpack("=I", space.read(const, 4))[0]
  1778.  
  1779.                         # Abort if [ADDR] is not a valid address
  1780.                         if not space.is_valid_address(call_dest):
  1781.                             continue
  1782.  
  1783.                         # If ADDR or [ADDR] point to an unknown code page, then we've likely found a hook
  1784.                         if find_module(mods, mod_addrs, const) == None or \
  1785.                            find_module(mods, mod_addrs, call_dest) == None:
  1786.  
  1787.                             # Disassemble the instruction which calls the hook
  1788.                             s = "{0} [{1:#x}]".format(op.mnemonic, call_dest)
  1789.  
  1790.                             # Disassemble the data at the call destination (the rootkit code)
  1791.                             call_data = space.zread(call_dest, 32)
  1792.                             dest_disasm = get_disasm_text(call_data, call_dest)
  1793.  
  1794.                             yield (None, 'ucpcall', None, src_mod_name, '', op.address, call_dest, None, s)
  1795.  
  1796.     def get_hooks(self, proc, addr_space, mods, mod_addrs, mod, src_mod_name):
  1797.         """Find IAT, EAT and Inline hooks"""
  1798.        
  1799.         # Declare this variable so we don't constantly look it up
  1800.         addr_space_is_valid_address = addr_space.is_valid_address
  1801.        
  1802.         # If the process object is None, then we're working in kernel mode
  1803.         if proc != None:
  1804.             process_name = str(proc.ImageFileName).lower()
  1805.             eat_hook_type = 'eat'
  1806.             iat_hook_type = 'iat'
  1807.             inline_hook_type = 'inline'
  1808.         else:
  1809.             process_name = ''
  1810.             eat_hook_type = 'eatk'
  1811.             iat_hook_type = 'iatk'
  1812.             inline_hook_type = 'inlinek'    
  1813.  
  1814.         modlist = [m for k, (m, v) in mods.items()]
  1815.  
  1816.         for dll, symbols in mod.imports():
  1817.            
  1818.             # Get a list of the DLLs in the process space that match the name of the imported DLL.
  1819.             # This is important, for example, because a process might load comctl32.dll from both
  1820.             # the system32 directory and the WinSxS directory for app compatibility
  1821.             possible_mods = find_module_by_name_ex(modlist, [dll])
  1822.  
  1823.             if not possible_mods:
  1824.                 continue
  1825.  
  1826.             for imp in symbols:
  1827.                 if not addr_space_is_valid_address(imp.funcaddr):
  1828.                     continue
  1829.    
  1830.                 dst_mod = find_module(mods, mod_addrs, imp.funcaddr)
  1831.    
  1832.                 if dst_mod not in possible_mods:
  1833.    
  1834.                     try:
  1835.                         dst_mod_name = str(dst_mod.BaseDllName)
  1836.                     except:
  1837.                         dst_mod_name = "UNKNOWN"
  1838.    
  1839.                     if self.ignore(iat_hook_type, process_name, dll, dst_mod_name, imp.get_name()):
  1840.                         continue
  1841.    
  1842.                     yield (proc, iat_hook_type, src_mod_name, dll, imp.get_name(), 0, imp.funcaddr, dst_mod_name, None)
  1843.  
  1844.         # Check this whitelist before scanning, it can save some time
  1845.         if self.ignore(inline_hook_type, "", src_mod_name, "", ""):
  1846.             raise StopIteration
  1847.  
  1848.         for exp in mod.exports():
  1849.  
  1850.             if not addr_space_is_valid_address(exp.funcaddr):
  1851.                 continue
  1852.  
  1853.             # Get the module containing the function
  1854.             dst_mod = find_module(mods, mod_addrs, exp.funcaddr)
  1855.  
  1856.             # This is a check for EAT hooks
  1857.             if dst_mod != exp.owner_mod:
  1858.                 try:
  1859.                     dst_mod_name = str(dst_mod.BaseDllName)
  1860.                 except:
  1861.                     dst_mod_name = "UNKNOWN"
  1862.  
  1863.                 yield (proc, eat_hook_type, None, src_mod_name, exp.get_name() + '[' + hex(exp.funcaddr) + ']', 0, exp.funcaddr, dst_mod_name, None)
  1864.                 # No need to check for inline hooks in the case of EAT hooks
  1865.                 continue
  1866.  
  1867.             # Check for inline hooks
  1868.             ret = self.check_inline(exp.funcaddr, addr_space, dst_mod.DllBase, dst_mod.DllBase + dst_mod.SizeOfImage)
  1869.  
  1870.             if not isinstance(ret, tuple):
  1871.                 continue
  1872.  
  1873.             (dst, instr) = ret
  1874.  
  1875.             # The hook destination must exist in valid memory for the process
  1876.             if not addr_space_is_valid_address(dst):
  1877.                 continue
  1878.  
  1879.             dst_mod = find_module(mods, mod_addrs, dst)
  1880.  
  1881.             if dst_mod != exp.owner_mod:
  1882.                 try:
  1883.                     dst_mod_name = str(dst_mod.BaseDllName)
  1884.                 except:
  1885.                     dst_mod_name = "UNKNOWN"
  1886.                    
  1887.                 if self.ignore(inline_hook_type, process_name, src_mod_name, dst_mod_name, exp.get_name()):
  1888.                     continue
  1889.                    
  1890.                 #ntoskrnl exports symbols such as SePublicDefaultDacl, SeTokenObjectType that
  1891.                 #are not functions. in some cases, the symbols are DWORDs that store addresses
  1892.                 #like 823C45E8 (where E8 is a CALL) and it looks like redirection according to the
  1893.                 #disasm. we could create a whitelist entry with the names of the symbols, but there
  1894.                 # may be other symbols in the future, so instead we check the section where the
  1895.                 #exported symbol exists - if its .text the export is probably a function. if
  1896.                 #its .data or PAGEDATA the export is probably a variable...
  1897.                
  1898.                 if proc == None and "data" in self.get_section_from_mod(addr_space, mod, exp.funcaddr).lower():
  1899.                     continue
  1900.                    
  1901.                 yield (proc, inline_hook_type, None, src_mod_name, exp.get_name() + '[' + hex(exp.funcaddr) + ']', exp.funcaddr, dst, dst_mod_name, instr)
  1902.  
  1903.     def check_inline(self, va, ps_ad, mem_start=None, mem_end=None):
  1904.         """Check for inline hooks with static disassembly. The mem_start and mem_end
  1905.        parameters are placeholders for different versions of ApiHooks which use other
  1906.        techniques to detect hooks - they are not used in this version"""
  1907.  
  1908.         try:
  1909.             bytes = ps_ad.zread(va, 24)
  1910.         except:
  1911.             return None
  1912.  
  1913.         STOP = lambda x: x < mem_start or x > mem_start + mem_end
  1914.  
  1915.         # If the API is in kernel memory, then the hook destination should also
  1916.         # be in kernel memory (and same with user mode) or maybe there's a disasm
  1917.         # error or other corruption happening....
  1918.         kernel_memory = True if va > 0x80000000 else False
  1919.  
  1920.         n = 0
  1921.  
  1922.         call_dest = mnemonic = push_val = None
  1923.  
  1924.         for op in distorm3.Decompose(va, bytes, distorm3.Decode32Bits):
  1925.  
  1926.             # Increasing n can lead to false positives as we scan further into
  1927.             # a function. However, it is necessary to detect some hooks. The
  1928.             # alternate option is to use emulation...
  1929.             if not op.valid or n == 3:
  1930.                 break
  1931.  
  1932.             if op.flowControl == 'FC_CALL':
  1933.  
  1934.                 # Check for CALL [ADDR]
  1935.                 if op.mnemonic == "CALL" and op.operands[0].type == 'AbsoluteMemoryAddress':
  1936.                     const = op.operands[0].disp & 0xFFFFFFFF
  1937.                     call_dest = struct.unpack("=I", ps_ad.zread(const, 4))[0]
  1938.                     mnemonic  = 'CALL [{0:#x}] =>> {1:#x}'.format(const, call_dest)
  1939.                     if STOP(call_dest): break
  1940.  
  1941.                 # Check for CALL ADDR
  1942.                 elif op.operands[0].type == 'Immediate':
  1943.                     call_dest = op.operands[0].value & 0xFFFFFFFF
  1944.                     mnemonic  = 'CALL {0:#x}'.format(call_dest)
  1945.                     if STOP(call_dest): break
  1946.  
  1947.             elif op.flowControl == 'FC_UNC_BRANCH' and op.mnemonic == "JMP" and op.size > 2:
  1948.  
  1949.                 # Check for JMP [ADDR]
  1950.                 if op.operands[0].type == 'AbsoluteMemoryAddress':
  1951.                     const = op.operands[0].disp & 0xFFFFFFFF
  1952.                     call_dest = struct.unpack("=I", ps_ad.zread(const, 4))[0]
  1953.                     mnemonic  = 'JMP [{0:#x}] =>> {1:#x}'.format(const, call_dest)
  1954.                     if STOP(call_dest): break
  1955.  
  1956.                 # Check for JMP ADDR
  1957.                 elif op.operands[0].type == 'Immediate':
  1958.                     call_dest = op.operands[0].value & 0xFFFFFFFF
  1959.                     mnemonic  = 'JMP {0:#x}'.format(call_dest)
  1960.                     if STOP(call_dest): break
  1961.  
  1962.             # Check for PUSH followed by a RET
  1963.             elif op.flowControl == 'FC_NONE':
  1964.                 if op.mnemonic == "PUSH" and op.operands[0].type == 'Immediate' and op.size == 5:
  1965.                     push_val = op.operands[0].value & 0xFFFFFFFF
  1966.                    
  1967.             elif op.flowControl == 'FC_RET':
  1968.                 if push_val:
  1969.                     call_dest = push_val
  1970.                     mnemonic  = "PUSH {0:#x}; {1}".format(call_dest, op.mnemonic)
  1971.                     if STOP(call_dest): break
  1972.  
  1973.             n += 1
  1974.  
  1975.         # Check that we're still in the correct ring that we started in
  1976.         if kernel_memory and call_dest < 0x80000000 or not \
  1977.            kernel_memory and call_dest > 0x80000000:
  1978.             return None
  1979.  
  1980.         return call_dest, mnemonic
  1981.  
  1982.     def get_syscall_hooks(self, proc, addr_space, mods, mod_addrs, mod):
  1983.         """Enumerate syscall hooks, as installed by Carberp etc."""
  1984.  
  1985.         # Enumerate exported functions by this module
  1986.         exports = list(mod.exports())
  1987.  
  1988.         # Resolve KiFastSystemCall API
  1989.         try:
  1990.             kifastsystemcall = [exp.funcaddr for exp in exports if exp.get_name() == "KiFastSystemCall"][0]
  1991.         except IndexError:
  1992.             raise StopIteration("Cannot find the address of KiFastSystemCall")
  1993.  
  1994.         addr_space_is_valid_address = addr_space.is_valid_address
  1995.  
  1996.         # Check each exported function if its an NT syscall
  1997.         for exp in exports:
  1998.  
  1999.             if not addr_space_is_valid_address(exp.funcaddr):
  2000.                 continue
  2001.  
  2002.             # Disassemble the first two instructions
  2003.             try:
  2004.                 i1 = exp.get_instruction(0)
  2005.                 i2 = exp.get_instruction(1)
  2006.             except:
  2007.                 continue
  2008.  
  2009.             # They both must be valid and have two operands  
  2010.             if not i1 or not i1.valid or len(i1.operands) != 2 or \
  2011.                not i2 or not i2.valid or len(i2.operands) != 2:
  2012.                 continue
  2013.            
  2014.             # Now check the instruction and operand types
  2015.             if i1.mnemonic == "MOV" and i1.operands[0].type == 'Register' and i1.operands[0].name == 'EAX' and i1.operands[1].type == 'Immediate' and \
  2016.                i2.mnemonic == "MOV" and i2.operands[0].type == 'Register' and i2.operands[0].name == 'EDX' and i1.operands[1].type == 'Immediate':
  2017.                 # This is a pointer to where KiFastSystemCall is stored
  2018.                 call_dest = i2.operands[1].value
  2019.                 # Cheap disassembly of the instruction
  2020.                 str2 = "MOV EDX, {0:#x}".format(call_dest)
  2021.                 # Derefrence to find the KiFastSystemCall address
  2022.                 deref_pointer = obj.Object('unsigned long', offset = call_dest, vm = addr_space)
  2023.                 if deref_pointer != kifastsystemcall:
  2024.                     yield (proc, 'syscall', None, 'ntdll.dll', exp.get_name() + '[' + hex(exp.funcaddr) + ']', call_dest, deref_pointer, "UNKNOWN", str2)
  2025.  
  2026.     def get_all_hooks(self, proc, addr_space, procs, mods, mod_addrs):
  2027.         """Dispatcher for hook detection"""
  2028.  
  2029.         for base, (mod, name) in mods.items():
  2030.             # If the process object is None, then we're dealing with kernel modules. In this case,
  2031.             # we need to find a valid space, because some modules are only accessible from the
  2032.             # context of a process with at least one GUI thread.
  2033.             if proc == None:
  2034.                 space = find_space(addr_space, procs, base)
  2035.             else:
  2036.                 space = addr_space
  2037.  
  2038.             if space == None or not space.vtop(base):
  2039.                 continue
  2040.  
  2041.             for val in self.get_hooks(proc, space, mods, mod_addrs, mod, name):
  2042.                 yield val
  2043.  
  2044.             # Check for NT syscall hooks if the module is ntdll
  2045.             if name == "ntdll.dll":
  2046.                 for val in self.get_syscall_hooks(proc, space, mods, mod_addrs, mod):
  2047.                     yield val
  2048.  
  2049.             # For the sake of time, we only check the modules in this list for calls to
  2050.             # unknown code pages. Expand it however you please.
  2051.             if name in ["tcpip.sys", "ntfs.sys", "fastfast.sys", "wanarp.sys", "ndis.sys", "atapi.sys", "ntoskrnl.exe", "ntkrnlpa.exe", "ntkrnlmp.exe"]:
  2052.                 for val in self.get_ucp_hooks(mods, mod_addrs, mod, name, space):
  2053.                     yield val
  2054.  
  2055.     def get_name_from_driver_object(self, addr):
  2056.         """Look up the name of a driver based on its DRIVER_OBJECT - this is useful in cases
  2057.        like Rustock.B which unlink and overwrite data in the LDR_MODULES list"""
  2058.         myobj = filescan.DriverScan(self._config)
  2059.         myobj.kernel_address_space = utils.load_as(self._config)
  2060.         if not self.driver_objects:
  2061.             self.driver_objects = list(myobj.calculate())
  2062.         for object_obj, driver_obj, extension_obj, object_name_info_obj in self.driver_objects:
  2063.             if addr >= driver_obj.DriverStart and addr < (driver_obj.DriverStart + driver_obj.DriverSize) & 0xFFFFFFFF:
  2064.                 return myobj.parse_string(driver_obj.DriverName)
  2065.         return 'UNKNOWN'
  2066.  
  2067.     def calculate(self):
  2068.         t0 = time.time()
  2069.  
  2070.         addr_space = get_malware_space(self._config)
  2071.  
  2072.         procs = list(tasks.pslist(addr_space))
  2073.  
  2074.         # Perform a user space scan
  2075.         if not self._config.KERNEL:
  2076.  
  2077.             # Scan all processes, unless the user specified a PID list
  2078.             for p in self.filter_tasks(procs):
  2079.  
  2080.                 ps_ad = p.get_process_address_space()
  2081.                 if ps_ad == None:
  2082.                     continue
  2083.  
  2084.                 # The PEB must be valid to get a list of loaded DLLs
  2085.                 if not p.Peb:
  2086.                     continue
  2087.  
  2088.                 mods, mod_addrs = get_sorted_mods(p.list_modules())                
  2089.  
  2090.                 for val in self.get_all_hooks(p, ps_ad, procs, mods, mod_addrs):
  2091.                     yield val
  2092.  
  2093.         # Perform a kernel space scan
  2094.         else:
  2095.             mods, mod_addrs = get_sorted_mods(modules.lsmod(addr_space))
  2096.             for val in self.get_all_hooks(None, addr_space, procs, mods, mod_addrs):
  2097.                 (proc, type, current_mod, mod, func, src, dst, hooker, instruction) = val
  2098.                 if hooker == "UNKNOWN":
  2099.                     hooker = self.get_name_from_driver_object(dst)
  2100.                 yield (proc, type, current_mod, mod, func, src, dst, hooker, instruction)
  2101.                    
  2102.         print 'Finished after', time.time() - t0, 'seconds'
  2103.  
  2104.     def render_text(self, outfd, data):
  2105.         outfd.write("{0:<32} {1:<8} {2:40} {3}\n".format('Name', 'Type', 'Target', 'Value'))
  2106.         for (proc, type, current_mod, mod, func, src, dst, hooker, instruction) in data:
  2107.             if proc:
  2108.                 name = "{0}[{1}]".format(proc.ImageFileName, proc.UniqueProcessId)
  2109.                 if current_mod:
  2110.                     name += '@' + current_mod
  2111.             else:
  2112.                 name = '-'
  2113.             value = "{0:#x}".format(src)
  2114.             if instruction:
  2115.                 value += " {0}".format(instruction)
  2116.             else:
  2117.                 value += " {0:#x}".format(dst)
  2118.             if hooker:
  2119.                 value += " ({0})".format(hooker)
  2120.  
  2121.             target = mod
  2122.             if func:
  2123.                 target += "!" + func
  2124.  
  2125.             outfd.write("{0:<32} {1:<8} {2:<40} {3}\n".format(name, type, target, value))
  2126.  
  2127. #--------------------------------------------------------------------------------
  2128. # idt plug-in
  2129. # IDT info from http://www.logix.cz/michal/doc/i386/chp09-00.htm & Rootkit Arsenal
  2130. #--------------------------------------------------------------------------------
  2131.  
  2132. idt_info = {
  2133.         0x0: ('KiTrap00', 'Divide error'),
  2134.         0x1: ('KiTrap01', 'Debug'),
  2135.         0x2: ('KiTrap02', 'Nonmaskable interrupt'),
  2136.         0x3: ('KiTrap03', 'Breakpoint'),
  2137.         0x4: ('KiTrap04', 'Overflow'),
  2138.         0x5: ('KiTrap05', 'Bounds check'),
  2139.         0x6: ('KiTrap06', 'Invalid opcode'),
  2140.         0x7: ('KiTrap07', 'Coprocessor not avail.'),
  2141.         0x8: ('KiTrap08', 'Double fault'),
  2142.         0x9: ('KiTrap09', ''),
  2143.         0xA: ('KiTrap0A', 'Invalid TSS'),
  2144.         0xB: ('KiTrap0B', 'Segment not present'),
  2145.         0xC: ('KiTrap0C', 'Stack exception'),
  2146.         0xD: ('KiTrap0D', 'General protection'),
  2147.         0xE: ('KiTrap0E', 'Page fault'),
  2148.         0xF: ('KiTrap0F', ''),
  2149.         0x10: ('KiTrap10', ''),
  2150.         0x11: ('KiTrap11', ''),
  2151.         0x12: ('KiTrap12', ''),
  2152.         0x13: ('KiTrap13', ''),
  2153.         0x2A: ('KiGetTickCount', ''),
  2154.         0x2B: ('KiCallbackReturn', ''),
  2155.         0x2C: ('KiSetLowWaitHighThread', ''),
  2156.         0x2D: ('KiDebugService', ''),
  2157.         0x2E: ('KiSystemService', ''),
  2158.     }  
  2159.  
  2160. class IDT(ApiHooks):
  2161.     "[MALWARE] Display Interrupt Descriptor Table"
  2162.  
  2163.     def __init__(self, config, *args):
  2164.         ApiHooks.__init__(self, config, *args)
  2165.         config.remove_option("PID")
  2166.         config.remove_option("KERNEL")
  2167.         config.remove_option("OFFSET")
  2168.  
  2169.     def calculate(self):
  2170.         addr_space = get_malware_space(self._config)
  2171.  
  2172.         mods, mod_addrs = get_sorted_mods(modules.lsmod(addr_space))
  2173.  
  2174.         volmagic = obj.Object('VOLATILITY_MAGIC', 0x0, addr_space)
  2175.         kpcra = volmagic.KPCR.v()
  2176.         kpcrval = obj.Object("_KPCR", offset = kpcra, vm = addr_space)
  2177.  
  2178.         entries = obj.Object("Array", offset = kpcrval.IDT, vm = addr_space, count = 256, targetType = '_KIDTENTRY')
  2179.  
  2180.         for i, entry in enumerate(entries):
  2181.  
  2182.             if idt_info.has_key(i):
  2183.                 func_name = idt_info[i][0]
  2184.             elif i >= 0x30:
  2185.                 func_name = 'KiUnexpectedInterrupt{0}'.format(i-0x30)
  2186.             else:
  2187.                 func_name = '-'
  2188.  
  2189.             details = ''
  2190.  
  2191.             if entry.ExtendedOffset == 0:
  2192.                 addr = 0
  2193.             else:
  2194.                 addr = (entry.ExtendedOffset.v() << 16) | entry.Offset.v()
  2195.  
  2196.                 mod = find_module(mods, mod_addrs, addr)
  2197.                 if mod != None:
  2198.                     secname = self.get_section_from_mod(addr_space, mod, addr)
  2199.                     details = "{0} {1}".format(mod.BaseDllName, secname)
  2200.                     ret = self.check_inline(addr, addr_space, mod.DllBase, mod.DllBase + mod.SizeOfImage)
  2201.                     if isinstance(ret, tuple):
  2202.                         details += ' => ' + ret[1]
  2203.        
  2204.             yield i, entry.Selector, func_name, addr, details
  2205.  
  2206.     def render_text(self, outfd, data):
  2207.         outfd.write("{0:<8} {1:<8} {2:<26} {3:<12} {4}\n".format("Index", "Selector", "Function", "Value", "Details"))
  2208.         for i, selector, func_name, addr, details in data:
  2209.             outfd.write("{0:<8X} {1:<8X} {2:<26} {3:<#12x} {4}\n".format(i, selector, func_name, addr, details))
  2210.  
  2211.  
  2212. #--------------------------------------------------------------------------------
  2213. # gdt plugin
  2214. #
  2215. # This plugin was built using information in Reverend Bill Blunden's book
  2216. # "The Rootkit Arsenal" and Matthew "j00ru" Jurczyk and Gynvael Coldwind's article
  2217. # "GDT and LDT in Windows kernel vulnerability exploitation" which can be found
  2218. # at http://dl.packetstormsecurity.net/papers/attack/call_gate_exploitation.pdf.
  2219. #--------------------------------------------------------------------------------
  2220.  
  2221. type = [
  2222.     "Data RO",
  2223.     "Data RO Ac",
  2224.     "Data RW",
  2225.     "Data RW Ac",
  2226.     "Data RO E",
  2227.     "Data RO EA",
  2228.     "Data RW E",
  2229.     "Data RW EA",
  2230.     "Code EO",
  2231.     "Code EO Ac",
  2232.     "Code RE",
  2233.     "Code RE Ac",
  2234.     "Code EO C",
  2235.     "Code EO CA",
  2236.     "Code RE C",
  2237.     "Code RE CA",
  2238.     "<Reserved>",
  2239.     "TSS16 Avl",
  2240.     "LDT",
  2241.     "TSS16 Busy",
  2242.     "CallGate16",
  2243.     "TaskGate",
  2244.     "Int Gate16",
  2245.     "TrapGate16",
  2246.     "<Reserved>",
  2247.     "TSS32 Avl",
  2248.     "<Reserved>",
  2249.     "TSS32 Busy",
  2250.     "CallGate32",
  2251.     "<Reserved>",
  2252.     "Int Gate32",
  2253.     "TrapGate32",
  2254. ]
  2255.  
  2256. class GDT(commands.command):
  2257.     "[MALWARE] Display Global Descriptor Table"
  2258.  
  2259.     def __init__(self, config, *args):
  2260.         commands.command.__init__(self, config, *args)
  2261.  
  2262.     def calculate(self):
  2263.         addr_space = get_malware_space(self._config)
  2264.  
  2265.         volmagic = obj.Object('VOLATILITY_MAGIC', 0x0, addr_space)
  2266.         kpcrval  = obj.Object("_KPCR", offset = volmagic.KPCR.v(), vm = addr_space)
  2267.        
  2268.         # Since the real GDT size is read from a register, we'll just assume
  2269.         # that there are 128 entries (which is normal for most OS)
  2270.         segdesc = obj.Object('Array', targetType = '_SEG_DESCRIPTOR', count = 128, offset = kpcrval.GDT, vm = addr_space)
  2271.  
  2272.         for i, sd in enumerate(segdesc):
  2273.             yield i, sd
  2274.  
  2275.     def render_text(self, outfd, data):
  2276.  
  2277.         outfd.write("{0:<6} {1:<12} {2:<12} {3:<14} {4:<6} {5:6} {6:6}\n".format(
  2278.             "Sel", "Base", "Limit", "Type", "DPL", "Gr", "Pr", "Flag"))
  2279.  
  2280.         for i, sd in data:
  2281.  
  2282.             # Do this for seg descriptors *and* call gates
  2283.             index = sd.type + 16 if sd.sFlag == 0 else sd.type
  2284.             present = "P" if sd.pFlag == 1 else "Np"
  2285.  
  2286.             # Do this only for 32-bit call gates
  2287.             if type[index] == "CallGate32":
  2288.                 cg = obj.Object('_CALL_GATE_DESCRIPTOR', sd.obj_offset, sd.obj_vm)
  2289.  
  2290.                 calladdr = cg.offset1 + (cg.offset2 << 16)
  2291.  
  2292.                 outfd.write("{0:<#6x} {1:<#12x} {2:<12} {3:<14} {4:<6} {5:<6} {6:6}\n".format(
  2293.                     i*8, calladdr, '-', type[index], sd.dpl, '-', present))
  2294.  
  2295.                 if calladdr > 0x80000000 and cg.obj_vm.is_valid_address(calladdr):
  2296.                     try:
  2297.                         disasm = get_disasm_text(cg.obj_vm.zread(calladdr, 12), calladdr, stoponret=True)
  2298.                     except:
  2299.                         disasm = None
  2300.  
  2301.                     if disasm:
  2302.                         outfd.write("\n{0}\n".format(disasm))
  2303.  
  2304.             # Do this for all other types of seg descriptors
  2305.             else:
  2306.                 granularity = "Pg" if sd.gFlag == 1 else "By"
  2307.                 baseaddress = sd.baseaddress1 + ((sd.baseaddress2 + (sd.baseaddress3 << 8)) << 16)
  2308.  
  2309.                 limit = sd.size1 + (sd.size2 << 16)
  2310.  
  2311.                 if sd.gFlag == 1:
  2312.                     limit = (limit + 1) * 0x1000
  2313.                     limit -= 1
  2314.  
  2315.                 outfd.write("{0:<#6x} {1:<#12x} {2:<#12x} {3:<14} {4:<6} {5:<6} {6:6}\n".format(
  2316.                     i*8, baseaddress, limit, type[index], sd.dpl, granularity, present))
  2317.  
  2318. #--------------------------------------------------------------------------------
  2319. # callbacks plug-in
  2320. #--------------------------------------------------------------------------------
  2321.  
  2322. class PoolScanFSCallback(scan.PoolScanner):
  2323.     """PoolScanner for File System Callbacks"""
  2324.     checks = [ ('PoolTagCheck', dict(tag = "IoFs")),
  2325.                ('CheckPoolSize', dict(condition = lambda x: x == 0x18)),
  2326.                ('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
  2327.                #('CheckPoolIndex', dict(value = 4)),
  2328.                ]
  2329.  
  2330. class PoolScanShutdownCallback(scan.PoolScanner):
  2331.     """PoolScanner for Shutdown Callbacks"""
  2332.     checks = [ ('PoolTagCheck', dict(tag = "IoSh")),
  2333.                ('CheckPoolSize', dict(condition = lambda x: x == 0x18)),
  2334.                ('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
  2335.                ('CheckPoolIndex', dict(value = 0)),
  2336.                ]
  2337.  
  2338. class PoolScanGenericCallback(scan.PoolScanner):
  2339.     """PoolScanner for Generic Callbacks"""
  2340.     checks = [ ('PoolTagCheck', dict(tag = "Cbrb")),
  2341.                ('CheckPoolSize', dict(condition = lambda x: x == 0x18)),
  2342.                ('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
  2343.                # This is a good constraint for all images except Frank's rustock-c.vmem
  2344.                #('CheckPoolIndex', dict(value = 1)),
  2345.                ]
  2346.  
  2347. class PoolScanDbgPrintCallback(scan.PoolScanner):
  2348.     """PoolScanner for DebugPrint Callbacks on Vista and 7"""
  2349.     checks = [ ('PoolTagCheck', dict(tag = "DbCb")),
  2350.                ('CheckPoolSize', dict(condition = lambda x: x == 0x20)),
  2351.                ('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
  2352.                #('CheckPoolIndex', dict(value = 0)),
  2353.                ]    
  2354.  
  2355. class PoolScanRegistryCallback(scan.PoolScanner):
  2356.     """PoolScanner for DebugPrint Callbacks on Vista and 7"""
  2357.     checks = [ ('PoolTagCheck', dict(tag = "CMcb")),
  2358.                # Seen as 0x38 on Vista SP2 and 0x30 on 7 SP0
  2359.                ('CheckPoolSize', dict(condition = lambda x: x >= 0x38)),
  2360.                ('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
  2361.                ('CheckPoolIndex', dict(value = 4)),
  2362.                ]    
  2363.  
  2364. class Callbacks(ApiHooks):
  2365.     "[MALWARE] Print system-wide notification routines"
  2366.  
  2367.     def __init__(self, config, *args):
  2368.         ApiHooks.__init__(self, config, *args)
  2369.         config.remove_option("PID")
  2370.         config.remove_option("KERNEL")
  2371.         config.remove_option("OFFSET")
  2372.  
  2373.     def get_kernel_callbacks(self, addr_space, ntmod):
  2374.         """
  2375.        Enumerate the Create Process, Create Thread, and Image Load callbacks.
  2376.  
  2377.        On some systems, the byte sequences will be inaccurate or the exported
  2378.        function will not be found. In these cases, the PoolScanGenericCallback
  2379.        scanner will pick up the pool associated with the callbacks.
  2380.        """
  2381.  
  2382.         routines = [
  2383.                    # push esi; mov esi, offset _PspLoadImageNotifyRoutine
  2384.                    ('PsSetLoadImageNotifyRoutine', "\x56\xbe"),
  2385.                    # push esi; mov esi, offset _PspCreateThreadNotifyRoutine
  2386.                    ('PsSetCreateThreadNotifyRoutine', "\x56\xbe"),
  2387.                    # mov edi, offset _PspCreateProcessNotifyRoutine
  2388.                    ('PsSetCreateProcessNotifyRoutine',"\xbf"),
  2389.                    ]
  2390.  
  2391.         for info in routines:
  2392.             symbol, hexbytes = info
  2393.  
  2394.             # Locate the exported symbol in the NT module
  2395.             symaddr = ntmod.getprocaddress(symbol)
  2396.             if symaddr == None: continue
  2397.  
  2398.             # Find the global variable referenced by the exported symbol
  2399.             data = addr_space.zread(symaddr, 100)
  2400.             offset = data.find(hexbytes)
  2401.             if offset == -1: continue
  2402.  
  2403.             array_base = obj.Object('unsigned int', offset = symaddr + offset + len(hexbytes), vm = addr_space)
  2404.             addrs = obj.Object('Array', offset = array_base, vm = addr_space, targetType = 'unsigned long', count = 8)  
  2405.  
  2406.             for addr in addrs:
  2407.                 if addr == 0: continue
  2408.                 callback = obj.Object('_GENERIC_CALLBACK', offset = addr - 0x7, vm = addr_space)
  2409.                 yield symbol, callback.Callback, None
  2410.  
  2411.     def get_generic_callbacks(self, addr_space):
  2412.         """
  2413.        Enumerate generic callbacks of the following types:
  2414.  
  2415.        * PsSetCreateProcessNotifyRoutine
  2416.        * PsSetThreadCreateNotifyRoutine
  2417.        * PsSetLoadImageNotifyRoutine
  2418.        * CmRegisterCallback (on XP only)
  2419.        * DbgkLkmdRegisterCallback (on Windows 7 only)
  2420.  
  2421.        The only issue is that you can't distinguish between the types by just
  2422.        finding the generic callback structure - but its better than nothing ;-)
  2423.        """
  2424.  
  2425.         for offset in PoolScanGenericCallback().scan(self.pspace):
  2426.             callback = obj.Object('_GENERIC_CALLBACK', offset, self.pspace)
  2427.             #if callback.Associated != 0:
  2428.             #    print callback.Associated.dereference_as('_REGISTRY_CALLBACK_LEGACY').CreateTime
  2429.             #    component = callback.Associated.dereference_as('_REGISTRY_CALLBACK').CreateTime
  2430.             #else:
  2431.             component = None
  2432.             yield "GenericKernelCallback", callback.Callback, component
  2433.  
  2434.     def get_fs_callbacks(self):
  2435.         """Enumerate the File System change callbacks"""
  2436.  
  2437.         for offset in PoolScanFSCallback().scan(self.pspace):
  2438.             callback = obj.Object('_NOTIFICATION_PACKET', offset, self.pspace)
  2439.             yield "IoRegisterFsRegistrationChange", callback.NotificationRoutine, None
  2440.  
  2441.     def get_bugcheck_callbacks(self, addr_space):
  2442.         """
  2443.        Enumerate generic Bugcheck callbacks.
  2444.  
  2445.        Note: These structures don't exist in tagged pools, but you can find
  2446.        them via KDDEBUGGER_DATA64 on all versions of Windows.
  2447.        """
  2448.  
  2449.         KeBugCheckCallbackListHead = tasks.get_kdbg(addr_space).KeBugCheckCallbackListHead.dereference_as('_KBUGCHECK_CALLBACK_RECORD')
  2450.  
  2451.         for l in KeBugCheckCallbackListHead.Entry.list_of_type("_KBUGCHECK_CALLBACK_RECORD", "Entry"):
  2452.             component = read_asciiz(addr_space, l.Component)
  2453.             yield "KeBugCheckCallbackListHead", l.CallbackRoutine, component
  2454.  
  2455.     def get_bugcheck_reason_callbacks(self, addr_space, ntmod):
  2456.         """
  2457.        Enumerate Bugcheck Reason callbacks.
  2458.  
  2459.        Note: These structures don't exist in tagged pools, so we find them by
  2460.        locating the list head which is a non-exported NT symbol. The method works
  2461.        on all versions of Windows.
  2462.        """
  2463.    
  2464.         symbol = "KeRegisterBugCheckReasonCallback"
  2465.         # mov [eax+KBUGCHECK_REASON_CALLBACK_RECORD.Entry.Blink], offset _KeBugCheckReasonCallbackListHead
  2466.         hexbytes = "\xC7\x40\x04"
  2467.  
  2468.         symaddr = ntmod.getprocaddress(symbol)
  2469.         if symaddr == None:
  2470.             raise StopIteration
  2471.    
  2472.         data = addr_space.zread(symaddr, 100)
  2473.         offset = data.find(hexbytes)
  2474.         if offset == -1:
  2475.             raise StopIteration
  2476.  
  2477.         bugs = obj.Object('Pointer', offset = symaddr + offset + len(hexbytes), vm = addr_space)
  2478.         bugs = bugs.dereference_as('_KBUGCHECK_REASON_CALLBACK_RECORD')
  2479.  
  2480.         for l in bugs.Entry.list_of_type("_KBUGCHECK_REASON_CALLBACK_RECORD", "Entry"):
  2481.             component = read_asciiz(addr_space, l.Component)
  2482.             yield symbol, l.CallbackRoutine, component
  2483.  
  2484.     def get_registry_callbacks_legacy(self, addr_space, ntmod):
  2485.         """
  2486.        Enumerate registry change callbacks.
  2487.  
  2488.        This method of finding a global variable via disassembly of the
  2489.        CmRegisterCallback function is only for XP systems. If it fails on
  2490.        XP you can still find the callbacks using PoolScanGenericCallback.
  2491.  
  2492.        On Vista and Windows 7, these callbacks are registered using the
  2493.        CmRegisterCallbackEx function.
  2494.        """
  2495.  
  2496.         symbol = "CmRegisterCallback"
  2497.  
  2498.         symaddr = ntmod.getprocaddress(symbol)
  2499.         if symaddr == None:
  2500.             raise StopIteration
  2501.  
  2502.         data = addr_space.zread(symaddr, 200)
  2503.        
  2504.         c = 0
  2505.         vector = None
  2506.  
  2507.         # Looking for MOV EBX, CmpCallBackVector
  2508.         # This may be the first or second MOV EBX instruction
  2509.         for op in distorm3.Decompose(symaddr, data, distorm3.Decode32Bits):
  2510.             if op.valid and op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].name == 'EBX':
  2511.                 vector = op.operands[1].value
  2512.                 if c == 1:
  2513.                     break
  2514.                 else:
  2515.                     c += 1
  2516.  
  2517.         # Can't find the global variable
  2518.         if vector == None:
  2519.             raise StopIteration
  2520.  
  2521.         # We could do an array of Pointers here and then dereference_as, but
  2522.         # the value of 7 must be subtracted from the object offset before use
  2523.         callbacks = obj.Object("Array", targetType = "unsigned int", count = 100, vm = addr_space, offset = vector)
  2524.  
  2525.         for cb in callbacks:
  2526.             if cb != 0:
  2527.                 cbblock = obj.Object("_EX_CALLBACK_ROUTINE_BLOCK", vm = addr_space, offset = cb - 7)
  2528.                 yield symbol, cbblock.Function, '--'
  2529.  
  2530.     def get_registry_callbacks(self):
  2531.         """
  2532.        Enumerate registry callbacks on Vista and 7.
  2533.  
  2534.        These callbacks are installed via CmRegisterCallback or CmRegisterCallbackEx.
  2535.        """
  2536.        
  2537.         for offset in PoolScanRegistryCallback().scan(self.pspace):
  2538.             callback = obj.Object('_REGISTRY_CALLBACK', offset, self.pspace)
  2539.             yield "CmRegisterCallback", callback.Function, None
  2540.  
  2541.     def get_shutdown_callbacks(self, addr_space):
  2542.         """Enumerate shutdown notification callbacks"""
  2543.  
  2544.         valid = addr_space.is_valid_address
  2545.  
  2546.         for offset in PoolScanShutdownCallback().scan(self.pspace):
  2547.             callback = obj.Object('_SHUTDOWN_PACKET', offset, self.pspace)
  2548.  
  2549.             if not valid(callback.Entry.Flink) or \
  2550.                not valid(callback.Entry.Blink) or \
  2551.                not valid(callback.DeviceObject): continue
  2552.  
  2553.             device = obj.Object('_DEVICE_OBJECT', callback.DeviceObject, addr_space)
  2554.  
  2555.             if not valid(device.DriverObject): continue
  2556.  
  2557.             address = device.DriverObject.MajorFunction[irpcodes.index('IRP_MJ_SHUTDOWN')]
  2558.             details = str(device.DriverObject.DriverName)
  2559.             yield "IoRegisterShutdownNotification", address, details
  2560.  
  2561.     def get_dbgprint_callbacks(self):
  2562.         """Enumerate DebugPrint callbacks on Vista and 7"""
  2563.        
  2564.         for offset in PoolScanDbgPrintCallback().scan(self.pspace):
  2565.             callback = obj.Object('_DBGPRINT_CALLBACK', offset, self.pspace)
  2566.             yield "DbgSetDebugPrintCallback", callback.Function, None
  2567.  
  2568.     def calculate(self):
  2569.         addr_space = get_malware_space(self._config)
  2570.  
  2571.         # Get one physical space that all scanners can use
  2572.         self.pspace = utils.load_as(self._config, astype = 'physical')
  2573.        
  2574.         # Get the loaded modules and sort them by base address
  2575.         drivers = list(modules.lsmod(addr_space))
  2576.         mods, mod_addrs = get_sorted_mods(drivers)
  2577.  
  2578.         # Find the NT executive module
  2579.         ntmod = find_module_by_name(drivers, ["ntoskrnl.exe", "ntkrnlpa.exe"])
  2580.         if ntmod == None:
  2581.             debug.error('Cannot find executive module!')
  2582.        
  2583.         callbacks = []
  2584.  
  2585.         callbacks.extend(list(self.get_kernel_callbacks(addr_space, ntmod)))
  2586.         callbacks.extend(list(self.get_fs_callbacks()))
  2587.         callbacks.extend(list(self.get_bugcheck_callbacks(addr_space)))
  2588.         callbacks.extend(list(self.get_bugcheck_reason_callbacks(addr_space, ntmod)))
  2589.         callbacks.extend(list(self.get_shutdown_callbacks(addr_space)))
  2590.         callbacks.extend(list(self.get_registry_callbacks_legacy(addr_space, ntmod)))
  2591.         callbacks.extend(list(self.get_registry_callbacks()))
  2592.         callbacks.extend(list(self.get_generic_callbacks(addr_space)))
  2593.         callbacks.extend(list(self.get_dbgprint_callbacks()))
  2594.  
  2595.         for symbol, callback, component in callbacks:
  2596.             # Lookup the name of the owning module using the loaded module list or driver objects
  2597.             mod = find_module(mods, mod_addrs, callback)
  2598.             try:
  2599.                 name = str(mod.BaseDllName)
  2600.             except:
  2601.                 name = self.get_name_from_driver_object(callback)
  2602.             # Append the optional component name which further describes the owner
  2603.             if component != None:
  2604.                 name += ' (' + component + ')'
  2605.  
  2606.             yield symbol, callback, name
  2607.  
  2608.     def render_text(self, outfd, data):
  2609.         outfd.write("{0:<36} {1:<10} {2}\n".format('Type', 'Callback', 'Owner'))
  2610.         for symbol, callback, owner in data:
  2611.             outfd.write("{0:<36} {1:<#8x} {2}\n".format(symbol, callback, owner))
  2612.  
  2613. #--------------------------------------------------------------------------------
  2614. # driverirp plug-in, based on AS's DriverScan
  2615. #--------------------------------------------------------------------------------
  2616. irpcodes = ['IRP_MJ_CREATE',
  2617.       'IRP_MJ_CREATE_NAMED_PIPE',
  2618.       'IRP_MJ_CLOSE',
  2619.       'IRP_MJ_READ',
  2620.       'IRP_MJ_WRITE',
  2621.       'IRP_MJ_QUERY_INFORMATION',
  2622.       'IRP_MJ_SET_INFORMATION',
  2623.       'IRP_MJ_QUERY_EA',
  2624.       'IRP_MJ_SET_EA',
  2625.       'IRP_MJ_FLUSH_BUFFERS',
  2626.       'IRP_MJ_QUERY_VOLUME_INFORMATION',
  2627.       'IRP_MJ_SET_VOLUME_INFORMATION',
  2628.       'IRP_MJ_DIRECTORY_CONTROL',
  2629.       'IRP_MJ_FILE_SYSTEM_CONTROL',
  2630.       'IRP_MJ_DEVICE_CONTROL',
  2631.       'IRP_MJ_INTERNAL_DEVICE_CONTROL',
  2632.       'IRP_MJ_SHUTDOWN',
  2633.       'IRP_MJ_LOCK_CONTROL',
  2634.       'IRP_MJ_CLEANUP',
  2635.       'IRP_MJ_CREATE_MAILSLOT',
  2636.       'IRP_MJ_QUERY_SECURITY',
  2637.       'IRP_MJ_SET_SECURITY',
  2638.       'IRP_MJ_POWER',
  2639.       'IRP_MJ_SYSTEM_CONTROL',
  2640.       'IRP_MJ_DEVICE_CHANGE',
  2641.       'IRP_MJ_QUERY_QUOTA',
  2642.       'IRP_MJ_SET_QUOTA',
  2643.       'IRP_MJ_PNP']
  2644.  
  2645. class DriverIrp(filescan.DriverScan, ApiHooks):
  2646.     "[MALWARE] Driver IRP hook detection"
  2647.  
  2648.     def __init__(self, config, args=None):
  2649.         filescan.DriverScan.__init__(self, config, args)
  2650.         config.add_option("REGEX", short_option='r', type='str', action='store', help='Analyze drivers matching REGEX')
  2651.  
  2652.     def get_irps(self, driver_obj, kas_zread, mods, mod_addrs):
  2653.  
  2654.         mem_start = driver_obj.DriverStart
  2655.         mem_end   = driver_obj.DriverStart + driver_obj.DriverSize
  2656.  
  2657.         for irpcode in range(0, len(irpcodes)):
  2658.  
  2659.             # Get the address of the IRP from the driver object's MajorFunction table
  2660.             irpaddr = driver_obj.MajorFunction[irpcode]
  2661.  
  2662.             # Try to get the owner of the IRP routine
  2663.             mod = find_module(mods, mod_addrs, irpaddr)
  2664.  
  2665.             # Does the IRP point inside the driver object or elsewhere?
  2666.             points_inside = False
  2667.             # Owner of the IRP routine (or None)
  2668.             irpowner   = None
  2669.             # Disassembly of the instructions at the IRP entry point
  2670.             disasm     = None
  2671.             # Destination address of an inline hook (or None)
  2672.             hookdest   = None
  2673.             # Owner of the hook address if applicable
  2674.             hooker     = None
  2675.  
  2676.             if mod != None:
  2677.                 if mod.DllBase == driver_obj.DriverStart:
  2678.                     points_inside = True
  2679.                 irpowner = str(mod.BaseDllName)
  2680.  
  2681.             # Get a disassembly of the IRP routine's prologue
  2682.             try:
  2683.                 bytes = kas_zread(irpaddr, 100)
  2684.                 disasm = get_disasm_text(bytes, irpaddr.v())
  2685.             except:
  2686.                 pass
  2687.  
  2688.             # Check for inline hooks if the IRP itself is not hooked
  2689.             if points_inside:
  2690.                 ret = self.check_inline(irpaddr.v(), self.kernel_address_space, mem_start, mem_end)
  2691.                 if isinstance(ret, tuple):
  2692.                     hookdest = ret[0]
  2693.                     # Try to get the owner of the hooked address
  2694.                     if hookdest != None:
  2695.                         hookmod  = find_module(mods, mod_addrs, hookdest)
  2696.                         if hookmod != None:
  2697.                             hooker = str(hookmod.BaseDllName)
  2698.                         else:
  2699.                             hooker = 'UNKNOWN'
  2700.  
  2701.             yield irpcode, irpaddr, irpowner, disasm, hookdest, hooker
  2702.  
  2703.     def render_text(self, outfd, data):
  2704.         """Renders the text-based output"""
  2705.  
  2706.         outfd.write("{0:<12} {1:<16} {2:<36} {3:<12} {4:<16} {5:<12} {6}\n".format(
  2707.                 "DriverStart", "Name", "IRP", "IrpAddr", "IrpOwner", "HookAddr", "HookOwner"))
  2708.  
  2709.         addr_space = utils.load_as(self._config)
  2710.  
  2711.         # Get a sorted list of module addresses
  2712.         mods, mod_addrs = get_sorted_mods(modules.lsmod(addr_space))
  2713.  
  2714.         # Compile the regular expression for filtering by driver name
  2715.         if self._config.regex != None:
  2716.             mod_re = re.compile(self._config.regex, re.I)
  2717.         else:
  2718.             mod_re = None
  2719.  
  2720.         # Handle the objects found by the DriverScan plugin
  2721.         for object_obj, driver_obj, extension_obj, ObjectNameString in data:
  2722.  
  2723.             # Continue if a regex was supplied and it doesn't match the current driver name
  2724.             if mod_re != None:
  2725.                 if not (mod_re.search(ObjectNameString) or
  2726.                        mod_re.search(ObjectNameString)): continue
  2727.  
  2728.             kas_zread = self.kernel_address_space.zread
  2729.  
  2730.             for vals in self.get_irps(driver_obj, kas_zread, mods, mod_addrs):
  2731.  
  2732.                 # Unpack the return values from get_irps
  2733.                 irpcode, irpaddr, irpowner, disasm, hookdest, hooker = vals
  2734.                
  2735.                 irpowner = '-' if not irpowner else irpowner
  2736.                 hookdest = '-' if not hookdest else hex(hookdest)
  2737.                 hooker   = '-' if not hooker else hooker
  2738.  
  2739.                 outfd.write("{0:<#12x} {1:<16} {2:<36} {3:<#12x} {4:<16} {5:<12} {6}\n".format(
  2740.                     driver_obj.DriverStart, ObjectNameString,
  2741.                     irpcodes[irpcode], irpaddr, irpowner, hookdest, hooker))
  2742.  
  2743.                 if self._config.VERBOSE:
  2744.                     outfd.write("{0}\n".format(disasm))
  2745.  
  2746.             # Check DriverStartIO per Frank B. and TDL4
  2747.             DriverStartIo = driver_obj.DriverStartIo
  2748.  
  2749.             if  DriverStartIo != 0:
  2750.                 if DriverStartIo < driver_obj.DriverStart or DriverStartIo > (driver_obj.DriverStart + driver_obj.DriverSize):
  2751.                     mod = find_module(mods, mod_addrs, DriverStartIo)
  2752.                     if mod != None:
  2753.                         owner = str(mod.BaseDllName)
  2754.                     else:
  2755.                         owner = "UNKNOWN"
  2756.                 else:
  2757.                     owner = ObjectNameString
  2758.  
  2759.                 outfd.write("{0:<#12x} {1:<16} {2:<36} {3:<#12x} {4:<16} {5:<12} {6}\n".format(
  2760.                     driver_obj.DriverStart, ObjectNameString,
  2761.                     'DriverStartIo', DriverStartIo, owner, '', ''))
  2762.  
  2763.                 if self._config.VERBOSE:
  2764.                     try:
  2765.                         bytes = kas_zread(DriverStartIo, 100)
  2766.                         disasm = get_disasm_text(bytes, DriverStartIo.v())
  2767.                     except:
  2768.                         continue
  2769.  
  2770.                     outfd.write("{0}\n".format(disasm))
  2771.  
  2772. #--------------------------------------------------------------------------------
  2773. # psxview plug-in (used to be csrpslist)
  2774. #--------------------------------------------------------------------------------
  2775.  
  2776. class _PSP_CID_TABLE(windows._HANDLE_TABLE):
  2777.     """Subclass the Windows handle table object for parsing PspCidTable"""
  2778.    
  2779.     def get_item(self, offset):
  2780.         return obj.Object("_OBJECT_HEADER", \
  2781.             offset - self.obj_vm.profile.get_obj_offset('_OBJECT_HEADER', 'Body'), self.obj_vm)
  2782.  
  2783. class PsXview(commands.command):
  2784.     "[MALWARE] Find hidden processes with various process listings"
  2785.  
  2786.     def __init__(self, config, *args):
  2787.         commands.command.__init__(self, config, *args)
  2788.         config.add_option("PHYSICAL-OFFSET", short_option = 'P', default = False,
  2789.                           help = "Physcal Offset", action = "store_true")
  2790.  
  2791.     def get_proc_list(self, csr_obj):
  2792.         """Iterator that walks the linked list of processes in csrss.exe"""
  2793.         for l in csr_obj.ListLink.list_of_type("_CSR_PROCESS", "ListLink"):
  2794.             yield l
  2795.  
  2796.     def check_pslist(self, all_tasks):
  2797.         """Enumerate processes from PsActiveProcessHead"""
  2798.         return dict((p.UniqueProcessId.v(), p) for p in all_tasks)
  2799.  
  2800.     def check_psscan(self):
  2801.         """Enumerate processes with pool tag scanning"""
  2802.         return dict((p.UniqueProcessId.v(), p) for p in filescan.PSScan(self._config).calculate() if p.ExitTime == 0)
  2803.  
  2804.     def check_thrdproc(self, addr_space):
  2805.         """Enumerate processes indirectly by ETHREAD scanning"""
  2806.         threads = dict()
  2807.         for ethread in modscan.ThrdScan(self._config).calculate():
  2808.             if ethread.ExitTime == 0:
  2809.                 if hasattr(ethread.Tcb, 'Process'):
  2810.                     proc = obj.Object("_EPROCESS", offset = ethread.Tcb.Process, vm = addr_space)
  2811.                     if proc.ExitTime == 0:
  2812.                         pid = proc.UniqueProcessId.v()
  2813.                         if pid < 65535:
  2814.                             threads[pid] = proc
  2815.                 elif hasattr(ethread, 'ThreadsProcess'):
  2816.                     proc = obj.Object("_EPROCESS", offset = ethread.ThreadsProcess, vm = addr_space)
  2817.                     if proc.ExitTime == 0:
  2818.                         pid = proc.UniqueProcessId.v()
  2819.                         if pid < 65535:
  2820.                             threads[pid] = proc
  2821.         return threads
  2822.  
  2823.     def is_process_object(self, objhdr, obj_vm):
  2824.         """Determine if an OBJECT_HEADER is for an EPROCESS, taking into
  2825.        account the for changes to the object header for Windows 7"""
  2826.         volmagic = obj.Object("VOLATILITY_MAGIC", 0x0, obj_vm)
  2827.         try:
  2828.             # New object header
  2829.             if objhdr.TypeIndex == volmagic.TypeIndexMap.v()['Process']:
  2830.                 return True
  2831.         except AttributeError:
  2832.             # Old object header
  2833.             if (objhdr.Type.Name):
  2834.                 if str(objhdr.Type.Name) == 'Process':
  2835.                     return True
  2836.         return False
  2837.  
  2838.     def check_csrss_handles(self, csrs):
  2839.         """Enumerate processes using the csrss.exe handle table"""
  2840.         handles = dict()
  2841.  
  2842.         for p in csrs:
  2843.             # Gather the handles to process objects
  2844.             for h in p.ObjectTable.handles():
  2845.                 if self.is_process_object(h, p.obj_vm):
  2846.                     proc_obj = obj.Object('_EPROCESS', h.Body.obj_offset, p.obj_vm, parent = p)
  2847.                     handles[proc_obj.UniqueProcessId.v()] = proc_obj
  2848.  
  2849.         return handles
  2850.  
  2851.     def check_csrss_links(self, csrs):
  2852.         """Enumerate processes using the CsrRootProcess linked list"""
  2853.         links = dict()
  2854.  
  2855.         for p in csrs:        
  2856.             # Get the address space
  2857.             ps_ad = p.get_process_address_space()
  2858.             if ps_ad == None:
  2859.                 continue
  2860.            
  2861.             # Find the DLL which contains the special linked list info
  2862.             mods = list(p.list_modules())
  2863.             mod  = find_module_by_name(mods, ["csrsrv.dll"])
  2864.             if mod == None:
  2865.                 continue
  2866.    
  2867.             # Find the exported function
  2868.             CsrLockProcessByClientId = mod.getprocaddress("CsrLockProcessByClientId")
  2869.             if CsrLockProcessByClientId == None:
  2870.                 continue
  2871.    
  2872.             # Get the global variable referenced by the exported function
  2873.             prologue = ps_ad.zread(CsrLockProcessByClientId, 100)
  2874.             offset = prologue.find("\x8b\x35")
  2875.             if offset == -1:        
  2876.                 # pattern for windows 7
  2877.                 offset = prologue.find("\x8b\x1d")
  2878.                 if offset == -1:
  2879.                     continue
  2880.                
  2881.             csrpp = obj.Object('_CSR_PROCESS_POINTER', offset = CsrLockProcessByClientId + offset + 2, vm = ps_ad)
  2882.    
  2883.             # Locate the linked list by following pointers in the global variable
  2884.             csr_obj = obj.Object("_CSR_PROCESS", offset = csrpp.CsrRootProcess.dereference().dereference().v(), vm = ps_ad)
  2885.             # Save the pids
  2886.             for csr in self.get_proc_list(csr_obj):
  2887.                 links[csr.ClientId.UniqueProcess.v()] = None
  2888.  
  2889.         return links
  2890.    
  2891.     def check_pspcid(self, addr_space):
  2892.         """Enumerate processes by walking the PspCidTable"""
  2893.         pspcid = dict()
  2894.            
  2895.         # Copy the _HANDLE_TABLE type into _PSP_CID_TABLE
  2896.         addr_space.profile.add_types(
  2897.             {"_PSP_CID_TABLE" : addr_space.profile.abstract_types["_HANDLE_TABLE"]},
  2898.             )
  2899.            
  2900.         # Tell Volatility that KDBG.PspCidTable is a **_PSP_CID_TABLE
  2901.         addr_space.profile.add_types(
  2902.             {"_KDDEBUGGER_DATA64" : [None,
  2903.                 {'PspCidTable': [addr_space.profile.get_obj_offset("_KDDEBUGGER_DATA64", "PspCidTable"),
  2904.                     ["pointer", ["pointer", ['_PSP_CID_TABLE']]]],
  2905.             }] },
  2906.         )
  2907.                
  2908.         # Follow the pointers to the table base
  2909.         PspCidTable = tasks.get_kdbg(addr_space).PspCidTable.dereference().dereference()
  2910.        
  2911.         # Walk the handle table
  2912.         for h in PspCidTable.handles():
  2913.             if self.is_process_object(h, addr_space):
  2914.                 proc_obj = obj.Object('_EPROCESS', h.Body.obj_offset, addr_space)
  2915.                 pspcid[proc_obj.UniqueProcessId.v()] = proc_obj
  2916.                
  2917.         return pspcid
  2918.  
  2919.     def calculate(self):
  2920.         addr_space = get_malware_space(self._config)
  2921.        
  2922.         all_tasks = list(tasks.pslist(addr_space))
  2923.        
  2924.         # The CSR process is necessary for two of the process listings
  2925.         csrexe = [p for p in all_tasks if str(p.ImageFileName).lower() == "csrss.exe"]
  2926.        
  2927.         # For the following dictionaries, the keys are pids and the values are eproc objects
  2928.         ps_sources = dict(
  2929.             pslist      = self.check_pslist(all_tasks),
  2930.             psscan      = self.check_psscan(),
  2931.             thrdproc    = self.check_thrdproc(addr_space),
  2932.             pspcid      = self.check_pspcid(addr_space),
  2933.             csr_handles = self.check_csrss_handles(csrexe) if csrexe else dict(),
  2934.             csr_links   = self.check_csrss_links(csrexe)   if csrexe else dict(),
  2935.         )
  2936.        
  2937.         # Build a list of pids from all sources
  2938.         unique_pids = []
  2939.         for source in ps_sources.values():
  2940.             unique_pids.extend(source.keys())
  2941.  
  2942.         # For each unique pid, see if it exists in the lists
  2943.         for pid in list(set(unique_pids)):
  2944.        
  2945.             eproc = None
  2946.            
  2947.             for source in ps_sources.values():
  2948.                 if pid in source.keys():
  2949.                     if source[pid] != None:
  2950.                         eproc = source[pid]
  2951.                         break
  2952.  
  2953.             yield pid, eproc, ps_sources
  2954.  
  2955.     def render_text(self, outfd, data):
  2956.    
  2957.         outfd.write("{0:<12} {1:<20} {2:<8} {3:<10} {4:<10} {5:<10} {6:<10} {7:<10} {8:<10}\n".format(
  2958.             'Offset', 'Name', 'Pid', 'pslist', 'psscan', 'thrdproc', 'pspcid', 'csr_hnds', 'csr_list'))
  2959.        
  2960.         for pid, eproc, ps_sources in data:
  2961.        
  2962.             # csrss links dont have an eprocess
  2963.             if eproc == None:
  2964.                 offset = "----"
  2965.             else:
  2966.                 # force physical offsets for any EPROCESS not already
  2967.                 # instantiated from a file address space
  2968.                 if self._config.PHYSICAL_OFFSET and not eproc.obj_vm.__class__.__name__.endswith("FileAddressSpace"):
  2969.                     offset = hex(eproc.obj_vm.vtop(eproc.obj_offset))
  2970.                 else:
  2971.                     offset = hex(eproc.obj_offset)
  2972.        
  2973.             outfd.write("{0:<12} {1:<20} {2:<8} {3:<10} {4:<10} {5:<10} {6:<10} {7:<10} {8:<10}\n".format(
  2974.                 offset,
  2975.                 eproc.ImageFileName if eproc != None else "----",
  2976.                 pid,
  2977.                 ps_sources['pslist'].has_key(pid),
  2978.                 ps_sources['psscan'].has_key(pid),
  2979.                 ps_sources['thrdproc'].has_key(pid),
  2980.                 ps_sources['pspcid'].has_key(pid),
  2981.                 ps_sources['csr_handles'].has_key(pid),
  2982.                 ps_sources['csr_links'].has_key(pid),
  2983.                 ))
  2984.  
  2985. #--------------------------------------------------------------------------------
  2986. # ssdt_ex plugin
  2987. #--------------------------------------------------------------------------------
  2988. class SSDT_EX(ImpScan):
  2989.     "[MALWARE] SSDT Hook Explorer for IDA Pro (and SSDT by thread)"
  2990.  
  2991.     def __init__(self, config, *args):
  2992.         ImpScan.__init__(self, config, *args)
  2993.         config.remove_option("PID")
  2994.         config.remove_option("ADDR")
  2995.         config.remove_option("SIZE")
  2996.         config.remove_option("SCAN")
  2997.         config.remove_option("UNSAFE")
  2998.         config.add_option('DUMP-DIR', short_option = 'D', default = None,
  2999.                           help = 'Directory in which to dump the files')
  3000.                          
  3001.         # Names of the legit executive modules for SSDT tables
  3002.         self.executive_modules = [
  3003.             ["ntoskrnl.exe", "ntkrnlpa.exe", "ntkrnlmp.exe", "ntkrpamp.exe"], # SSDT 0
  3004.             ["win32k.sys"],                                                   # SSDT 1
  3005.             ["spud.sys"],                                                     # SSDT 2
  3006.             []]                                                               # SSDT 3
  3007.  
  3008.     def get_ssdt(self, addr_space, procs, mods, mod_addrs):
  3009.         # Gather up all SSDTs referenced by threads
  3010.         ssdts = set()
  3011.         for proc in procs:
  3012.             for thread in proc.ThreadListHead.list_of_type("_ETHREAD", "ThreadListEntry"):
  3013.                 ssdt_obj = thread.Tcb.ServiceTable.dereference_as('_SERVICE_DESCRIPTOR_TABLE')
  3014.                 ssdts.add(ssdt_obj)
  3015.  
  3016.         # Get a list of *unique* SSDT entries. Typically we see only two.
  3017.         tables = set()
  3018.  
  3019.         for ssdt_obj in ssdts:
  3020.             for i, desc in enumerate(ssdt_obj.Descriptors):
  3021.                 if desc.is_valid() and desc.ServiceLimit > 0 and desc.ServiceLimit < 0xFFFF and desc.KiServiceTable > 0x80000000:
  3022.                     tables.add((i, desc.KiServiceTable.v(), desc.ServiceLimit.v()))
  3023.  
  3024.         tables_with_vm = []
  3025.         for idx, table, n in tables:
  3026.             found = False
  3027.             for p in procs:
  3028.                 ## This is the process address space
  3029.                 ps_ad = p.get_process_address_space()
  3030.                 ## Is the table accessible from the process AS?
  3031.                 if ps_ad.is_valid_address(table):
  3032.                     tables_with_vm.append((idx, table, n, ps_ad))
  3033.                     found = True
  3034.                     break
  3035.             ## If not we use the kernel address space
  3036.             if not found:
  3037.                 # Any VM is equally bad...
  3038.                 tables_with_vm.append((idx, table, n, addr_space))
  3039.  
  3040.         syscalls = addr_space.profile.syscalls
  3041.                
  3042.         for idx, table, n, vm in sorted(tables_with_vm, key = itemgetter(0)):
  3043.             if not vm.is_valid_address(table):
  3044.                 continue
  3045.             for i in range(n):
  3046.                 func_addr = obj.Object('unsigned long', table + (i * 4), vm).v()
  3047.                 try:
  3048.                     func_name = syscalls[idx][i]
  3049.                 except IndexError:
  3050.                     func_name = "Unknown"
  3051.  
  3052.                 mod = find_module(mods, mod_addrs, func_addr)
  3053.  
  3054.                 try:
  3055.                     mod_base = mod.DllBase
  3056.                     mod_name = mod.BaseDllName
  3057.                 except:
  3058.                     mod_base = None
  3059.                     mod_name = ""
  3060.  
  3061.                 yield table, idx, i, func_addr, func_name, mod_base, mod_name
  3062.  
  3063.     def calculate(self):
  3064.         config = self._config
  3065.        
  3066.         # If we're dumping drivers, we need an output path
  3067.         if config.DUMP_DIR == None:
  3068.             debug.error("Please specify a dump directory (--dump-dir)")
  3069.         if not os.path.isdir(config.DUMP_DIR):
  3070.             debug.error(config.DUMP_DIR + " is not a directory")
  3071.            
  3072.         addr_space = get_malware_space(config)
  3073.  
  3074.         # Apply the right SSDT structs depending on OS version
  3075.         addr_space.profile.add_types(ssdt.sde_types)
  3076.  
  3077.         if addr_space.profile.metadata.get('major', 0) == 5 and addr_space.profile.metadata.get('minor',0) == 2:
  3078.             addr_space.profile.add_types(ssdt.sdt_types_2k3)
  3079.         else:
  3080.             addr_space.profile.add_types(ssdt.sdt_types)
  3081.  
  3082.         # Get a sorted list of module addresses
  3083.         drivers = list(modules.lsmod(addr_space))
  3084.  
  3085.         mods, mod_addrs = get_sorted_mods(drivers)
  3086.         procs = list(tasks.pslist(addr_space))
  3087.  
  3088.         dump_info = []
  3089.  
  3090.         for info in self.get_ssdt(addr_space, procs, mods, mod_addrs):
  3091.             (table, idx, i, func_addr, func_name, mod_base, mod_name) = info
  3092.             # If the module containing the syscall is not the nt executive, then its hooked
  3093.             if mod_name not in self.executive_modules[idx]:
  3094.                 dump_info.append((mod_base, func_addr, func_name))
  3095.                 print "  Entry {0:#06x}: {1:#x} ({2}) owned by {3}".format(idx * 0x1000 + i, func_addr, func_name, mod_name)
  3096.  
  3097.         exports = self.get_kernel_exports(addr_space, procs, drivers)
  3098.        
  3099.         # The hooks may be spread across multiple drivers, so handle each one separately
  3100.         bases = list(set([mod_base for (mod_base, _, _) in dump_info]))
  3101.         for base in bases:
  3102.             # no PE for the rootkit driver
  3103.             if base == None: continue
  3104.             # Dump and rebuild the rootkit driver
  3105.             pedata = self.rebuild(addr_space, base)
  3106.             pedata = self.rebase(addr_space, base, pedata)
  3107.             fname = os.path.join(config.DUMP_DIR, "driver.{0:x}.sys".format(base))
  3108.             f = open(fname, "wb")
  3109.             f.write(pedata)
  3110.             f.close()
  3111.             # Create a dictionary of the hooked SSDT functions
  3112.             hooks = dict((func_addr, func_name) for (mod_base, func_addr, func_name) in dump_info if mod_base == base)
  3113.             imports = self.get_all_imports(addr_space, pedata, base, exports)
  3114.             # Build the data for IDC statements
  3115.             funcs = self.find_functions(pedata, base)
  3116.             # Build a dictionary of imports for IDC statements
  3117.             names = dict((const, vals[1]) for (const, vals) in imports.items())
  3118.             self.automate_ida(fname, names, funcs, hooks)
  3119.  
  3120.     def render_text(self, outfd, data):
  3121.         pass
  3122.  
  3123. #--------------------------------------------------------------------------------
  3124. # threads plugin - replaces orphanthreads and ssdt_by_threads
  3125. #--------------------------------------------------------------------------------
  3126.  
  3127. KTHREAD_STATE = [
  3128.     'Initialized',
  3129.     'Ready',
  3130.     'Running',
  3131.     'Standby',
  3132.     'Terminated',
  3133.     'Waiting',
  3134.     'Transition',
  3135.     'DeferredReady',
  3136.     'GateWait',
  3137. ]
  3138.  
  3139. KWAIT_REASONS = [
  3140.     'Executive',
  3141.     'FreePage',
  3142.     'PageIn',
  3143.     'PoolAllocation',
  3144.     'DelayExecution',
  3145.     'Suspended',
  3146.     'UserRequest',
  3147.     'WrExecutive',
  3148.     'WrFreePage',
  3149.     'WrPageIn',
  3150.     'WrPoolAllocation',
  3151.     'WrDelayExecution',
  3152.     'WrSuspended',
  3153.     'WrUserRequest',
  3154.     'WrEventPair',
  3155.     'WrQueue',
  3156.     'WrLpcReceive',
  3157.     'WrLpcReply',
  3158.     'WrVirtualMemory',
  3159.     'WrPageOut',
  3160.     'WrRendezvous',
  3161.     'Spare2',
  3162.     'Spare3',
  3163.     'Spare4',
  3164.     'Spare5',
  3165.     'Spare6',
  3166.     'WrKernel',
  3167.     'WrResource',
  3168.     'WrPushLock',
  3169.     'WrMutex',
  3170.     'WrQuantumEnd',
  3171.     'WrDispatchInt',
  3172.     'WrPreempted',
  3173.     'WrYieldExecution',
  3174.     'WrFastMutex',
  3175.     'WrGuardedMutex',
  3176.     'WrRundown',
  3177.     'MaximumWaitReason'
  3178. ]
  3179.  
  3180. PS_CROSS_FLAGS = dict(
  3181.     PS_CROSS_THREAD_FLAGS_TERMINATED = 0x00000001,
  3182.     PS_CROSS_THREAD_FLAGS_DEADTHREAD = 0x00000002,
  3183.     PS_CROSS_THREAD_FLAGS_HIDEFROMDBG = 0x00000004,
  3184.     PS_CROSS_THREAD_FLAGS_IMPERSONATING = 0x00000008,
  3185.     PS_CROSS_THREAD_FLAGS_SYSTEM = 0x00000010,
  3186.     PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED = 0x00000020,
  3187.     PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION = 0x00000040,
  3188.     PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG = 0x00000080,
  3189.     PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG = 0x00000100,
  3190. )
  3191.  
  3192. IS_SET = lambda x, y : x.CrossThreadFlags & PS_CROSS_FLAGS[y]
  3193.  
  3194. class Threads(SSDT_EX):
  3195.     "[MALWARE] Investigate _ETHREAD and _KTHREADs"
  3196.  
  3197.     def __init__(self, config, *args):
  3198.         SSDT_EX.__init__(self, config, *args)
  3199.         config.remove_option("DUMP-DIR")
  3200.         config.add_option("FILTER", short_option='F', default=None,
  3201.                             help='Tags to filter (comma-separated)')
  3202.         config.add_option("ALLOW-HOOK", short_option='A', default=None,
  3203.                             help='Allow SSDT hooks from these mods (comma-separated)')
  3204.         config.add_option('PID', short_option = 'p', default = None,
  3205.                           help = 'Operate on these Process IDs (comma-separated)',
  3206.                           action = 'store', type = 'str')
  3207.         config.add_option("LISTTAGS", short_option='L', default=False,
  3208.                           action='store_true', help='List all available tags')
  3209.  
  3210.         self.kernel = 0x80000000
  3211.         self.kernel_space = None
  3212.         self.kernel_mods = None
  3213.         self.kernel_mod_addrs = None
  3214.         self.hooked_tables = dict()
  3215.  
  3216.     def check_hooked_ssdt(self, thread, hooked_tables):
  3217.         """Check if a thread is using a hooked SSDT"""
  3218.         ssdt_obj = thread.Tcb.ServiceTable.dereference_as('_SERVICE_DESCRIPTOR_TABLE')
  3219.         for i, desc in enumerate(ssdt_obj.Descriptors):
  3220.             tbl = desc.KiServiceTable.v()
  3221.             if tbl in hooked_tables.keys():
  3222.                 return True
  3223.         return False
  3224.  
  3225.     def check_orphan_thread(self, thread, mods, mod_addrs):
  3226.         """Check if a system thread has been abandoned"""
  3227.         if IS_SET(thread, 'PS_CROSS_THREAD_FLAGS_SYSTEM'):
  3228.             mod = find_module(mods, mod_addrs, thread.StartAddress)
  3229.             if not mod:
  3230.                 return True
  3231.         return False
  3232.  
  3233.     def check_hw_breakpoints(self, thread):
  3234.         """Check active threads for Dr* registers"""
  3235.         if not IS_SET(thread, 'PS_CROSS_THREAD_FLAGS_TERMINATED'):
  3236.             trapframe = thread.Tcb.TrapFrame
  3237.             if thread.obj_vm.is_valid_address(trapframe):
  3238.                 if (trapframe.Dr0 != 0 or trapframe.Dr1 != 0 or trapframe.Dr2 != 0 or trapframe.Dr3 != 0) and \
  3239.                    (trapframe.Dr6 != 0 and trapframe.Dr7 != 0):
  3240.                         return True
  3241.         return False
  3242.  
  3243.     def get_owner(self, address):
  3244.         """Return the owning module for an address"""
  3245.         if address > self.kernel:
  3246.             return find_module(self.kernel_mods, self.kernel_mod_addrs, address)
  3247.  
  3248.     def get_image_name(self, proc_offset):
  3249.         """Safely read a process's name, assuming it could be invalid"""
  3250.         data = self.kernel_space.zread(proc_offset + self.kernel_space.profile.get_obj_offset("_EPROCESS", "ImageFileName"), 16)
  3251.         if data:
  3252.             if data.find("\x00") != -1:
  3253.                 data = data[:data.find("\x00")]
  3254.             return repr(data)
  3255.         return ""
  3256.  
  3257.     def calculate(self):
  3258.  
  3259.         checks = dict(
  3260.                 OrphanThread = 'Detect orphan threads',
  3261.                 SystemThread = 'Detect system threads',
  3262.                 HookedSSDT = 'Detect threads using a hooked SSDT',
  3263.                 ScannerOnly = 'Detect threads no longer in a linked list',
  3264.                 DkomExit = 'Detect inconsistencies wrt exit times and termination',
  3265.                 HideFromDebug = 'Detect threads hidden from debuggers',
  3266.                 HwBreakpoints = 'Detect threads with hardware breakpoints',
  3267.                 AttachedProcess = 'Detect threads attached to another process',
  3268.             )
  3269.  
  3270.         if self._config.LISTTAGS:
  3271.             print "{0:<20} {1}".format("Tag", "Description")
  3272.             print "{0:<20} {1}".format("-" * 14, "-" * 14)
  3273.             for k, v in checks.items():
  3274.                 print "{0:<20} {1}".format(k, v)
  3275.             return
  3276.  
  3277.         addr_space = utils.load_as(self._config)
  3278.        
  3279.         # Apply the right SSDT structs depending on OS version
  3280.         addr_space.profile.add_types(ssdt.sde_types)
  3281.  
  3282.         if addr_space.profile.metadata.get('major', 0) == 5 and addr_space.profile.metadata.get('minor',0) == 2:
  3283.             addr_space.profile.add_types(ssdt.sdt_types_2k3)
  3284.         else:
  3285.             addr_space.profile.add_types(ssdt.sdt_types)
  3286.  
  3287.         # save the space so it can be used in render_text
  3288.         self.kernel_space = addr_space
  3289.  
  3290.         # ignore ssdt hooks if they point to user-specified "allowed" modules
  3291.         if self._config.ALLOW_HOOK:
  3292.             exlist = self._config.ALLOW_HOOK.split(",")
  3293.             self.executive_modules[0].extend(exlist)
  3294.             self.executive_modules[1].extend(exlist)
  3295.  
  3296.         # only show threads owned by particular processes
  3297.         if self._config.PID:
  3298.             pidlist = [int(p) for p in self._config.PID.split(',')]
  3299.         else:
  3300.             pidlist = []
  3301.  
  3302.         # get sorted list of kernel modules
  3303.         (kernel_mods, \
  3304.             kernel_mod_addrs) = get_sorted_mods(modules.lsmod(addr_space))
  3305.  
  3306.         # gather processes
  3307.         procs = list(tasks.pslist(addr_space))
  3308.  
  3309.         seen = dict()
  3310.  
  3311.         # gather up the SSDTs with hooks in them
  3312.         hooked_tables = dict()
  3313.         for info in self.get_ssdt(addr_space, procs, kernel_mods, kernel_mod_addrs):
  3314.             (table, idx, i, func_addr, func_name, mod_base, mod_name) = info
  3315.             if mod_name not in self.executive_modules[idx]:
  3316.                 if hooked_tables.has_key(table):
  3317.                     hooked_tables[table].append((i, func_name, func_addr, mod_name))
  3318.                 else:
  3319.                     hooked_tables[table] = [(i, func_name, func_addr, mod_name)]
  3320.  
  3321.         # save the lists so they can be used in render_text
  3322.         self.kernel_mods = kernel_mods
  3323.         self.kernel_mod_addrs = kernel_mod_addrs
  3324.         self.hooked_tables = hooked_tables
  3325.  
  3326.         # walk the thread lists
  3327.         for proc in self.filter_tasks(procs):
  3328.             for thread in proc.ThreadListHead.list_of_type("_ETHREAD", "ThreadListEntry"):
  3329.                 # the False here means its not found by the scanner
  3330.                 seen[thread.obj_vm.vtop(thread.obj_offset)] = (False, thread)
  3331.                
  3332.         # scan for thread objects, save them if not already seen
  3333.         for thread in modscan.ThrdScan(self._config).calculate():
  3334.             if not seen.has_key(thread.obj_offset):
  3335.                 seen[thread.obj_offset] = (True, thread)
  3336.  
  3337.         for offset, (found_by_scanner, thread) in seen.items():
  3338.  
  3339.             # skip processes the user doesn't want to see
  3340.             if pidlist and thread.Cid.UniqueProcess not in pidlist:
  3341.                 continue
  3342.  
  3343.             # get the thread's owner, taking into account the windows 7 changes
  3344.             if hasattr(thread.Tcb, 'Process'):
  3345.                 proc_offset = thread.Tcb.Process
  3346.             elif hasattr(thread, 'ThreadsProcess'):
  3347.                 proc_offset = thread.ThreadsProcess
  3348.  
  3349.             # the keys are "tags" which can be filtered on command-line with the --filter
  3350.             # parameter. each tag is True or False depending on the specified criteria.
  3351.             checks['DkomExit']        = thread.ExitTime != 0 and thread.Tcb.State != 4 and not IS_SET(thread, 'PS_CROSS_THREAD_FLAGS_TERMINATED')
  3352.             checks['HideFromDebug']   = IS_SET(thread, 'PS_CROSS_THREAD_FLAGS_HIDEFROMDBG')
  3353.             checks['SystemThread']    = IS_SET(thread, 'PS_CROSS_THREAD_FLAGS_SYSTEM')
  3354.             checks['Impersonation']   = IS_SET(thread, 'PS_CROSS_THREAD_FLAGS_IMPERSONATING')
  3355.             checks['ScannerOnly']     = found_by_scanner
  3356.             checks['HookedSSDT']      = self.check_hooked_ssdt(thread, hooked_tables)    
  3357.             checks['OrphanThread']    = self.check_orphan_thread(thread, kernel_mods, kernel_mod_addrs)
  3358.             checks['HwBreakpoints']   = self.check_hw_breakpoints(thread) if not found_by_scanner else False
  3359.             checks['AttachedProcess'] = thread.ExitTime == 0 and proc_offset != thread.Tcb.ApcState.Process
  3360.  
  3361.             yield thread, proc_offset, checks
  3362.  
  3363.     def render_text(self, outfd, data):
  3364.  
  3365.         # determine which filters the user wants to see
  3366.         if self._config.FILTER:
  3367.             filters = set(self._config.FILTER.split(','))
  3368.         else:
  3369.             filters = set()
  3370.  
  3371.         for thread, proc_offset, checks in data:
  3372.  
  3373.             # if the user didn't set filters, display all results. if the user
  3374.             # set one or more filters, only show threads with matching results.
  3375.             tags = set([t for t, v in checks.items() if v])
  3376.  
  3377.             if filters and not filters & tags:
  3378.                 continue
  3379.  
  3380.             s  = "------\n"
  3381.             s += "ETHREAD: {0:#010x} Pid: {1} Tid: {2}\n".format(thread.obj_offset, \
  3382.                 thread.Cid.UniqueProcess, thread.Cid.UniqueThread)
  3383.             s += "Tags: {0}\n".format(','.join(tags))
  3384.             s += "Created: {0}\n".format(thread.CreateTime or '-')
  3385.             s += "Exited: {0}\n".format(thread.ExitTime or '-')
  3386.  
  3387.             owner_proc  = self.get_image_name(proc_offset)
  3388.             attach_proc = self.get_image_name(thread.Tcb.ApcState.Process)
  3389.  
  3390.             s += "Owning Process: {0:#x} {1}\n".format(proc_offset, \
  3391.                 owner_proc)
  3392.             s += "Attached Process: {0:#x} {1}\n".format(thread.Tcb.ApcState.Process, \
  3393.                 attach_proc)
  3394.  
  3395.             # lookup the thread's state
  3396.             try:
  3397.                 state = KTHREAD_STATE[thread.Tcb.State]
  3398.             except IndexError:
  3399.                 state = hex(thread.Tcb.State)
  3400.  
  3401.             # append the wait reason
  3402.             if state == 'Waiting':
  3403.                 state = state + ':' + KWAIT_REASONS[thread.Tcb.WaitReason]
  3404.  
  3405.             s += "State: {0}\n".format(state)
  3406.             s += "BasePriority: {0:#x}\n".format(thread.Tcb.BasePriority)
  3407.             s += "Priority: {0:#x}\n".format(thread.Tcb.Priority)
  3408.             s += "TEB: {0:#010x}\n".format(thread.Tcb.Teb)
  3409.  
  3410.             # lookup the owning module according to start address
  3411.             owner = self.get_owner(thread.StartAddress)
  3412.  
  3413.             s += "StartAddress: {0:#010x} {1}\n".format(thread.StartAddress, \
  3414.                 owner.BaseDllName if owner else '')
  3415.  
  3416.             # check the flag which indicates whether Win32StartAddress is valid
  3417.             if thread.SameThreadApcFlags & 1:
  3418.                 s += "Win32StartAddress: {0:#010x}\n".format(thread.Win32StartAddress)
  3419.            
  3420.             # pretty print the ssdt's
  3421.             s += "ServiceTable: {0:#010x}\n".format(thread.Tcb.ServiceTable)
  3422.  
  3423.             ssdt_obj = obj.Object("_SERVICE_DESCRIPTOR_TABLE", \
  3424.                 offset = thread.Tcb.ServiceTable, vm = self.kernel_space)
  3425.  
  3426.             if ssdt_obj != None:
  3427.                 for i, desc in enumerate(ssdt_obj.Descriptors):
  3428.                     if desc.is_valid() and desc.ServiceLimit > 0 and \
  3429.                        desc.ServiceLimit < 0xFFFF and desc.KiServiceTable > self.kernel:
  3430.                         s += "  [{0}] {1:#010x}\n".format(i, desc.KiServiceTable.v())
  3431.                     else:
  3432.                         s += "  [{0}] -\n".format(i)
  3433.  
  3434.                     # show exactly which functions are hooked
  3435.                     tbl = desc.KiServiceTable.v()
  3436.  
  3437.                     if tbl in self.hooked_tables.keys():
  3438.                         for (i, func_name, func_addr, mod_name) in self.hooked_tables[tbl]:
  3439.                             s += "      [{0:#x}] {1} {2:#x} {3}\n".format(i, func_name, func_addr, mod_name)
  3440.  
  3441.             s += "Win32Thread: {0:#010x}\n".format(thread.Tcb.Win32Thread)
  3442.             s += "CrossThreadFlags: {0}\n".format(\
  3443.                 '|'.join(get_flags(PS_CROSS_FLAGS, thread.CrossThreadFlags)))
  3444.  
  3445.             # print the registers if possible
  3446.             if self.kernel_space.is_valid_address(thread.Tcb.TrapFrame):
  3447.        
  3448.                 trapframe = obj.Object("_KTRAP_FRAME", vm = self.kernel_space, \
  3449.                     offset = thread.Tcb.TrapFrame)
  3450.  
  3451.                 owner = self.get_owner(trapframe.Eip)
  3452.  
  3453.                 s += "Eip: {0:#10x} {1}\n".format(trapframe.Eip, owner.BaseDllName if owner else '')
  3454.                 s += "  eax={0:#010x} ebx={1:#010x} ecx={2:#010x} edx={3:#010x} esi={4:#010x} edi={5:#010x}\n".format(
  3455.                     trapframe.Eax, trapframe.Ebx, trapframe.Ecx, \
  3456.                     trapframe.Edx, trapframe.Esi, trapframe.Edi)
  3457.                 s += "  eip={0:#010x} esp={1:#010x} ebp={2:#010x} err={3:#010x}\n".format(
  3458.                     trapframe.Eip, trapframe.HardwareEsp, \
  3459.                     trapframe.Ebp, trapframe.ErrCode)
  3460.                 s += "  cs={0:#04x} ss={1:#04x} ds={2:#04x} es={3:#04x} gs={4:#04x} efl={5:#010x}\n".format(
  3461.                     trapframe.SegCs, trapframe.HardwareSegSs, trapframe.SegDs, \
  3462.                     trapframe.SegEs, trapframe.SegGs, trapframe.EFlags)
  3463.                 s += "  dr0={0:#010x} dr1={1:#010x} dr2={2:#010x} dr3={3:#010x} dr6={4:#010x} dr7={5:#010x}\n".format(
  3464.                     trapframe.Dr0, trapframe.Dr1, trapframe.Dr2, \
  3465.                     trapframe.Dr3, trapframe.Dr6, trapframe.Dr7)
  3466.  
  3467.             # disasemble the start address if possible
  3468.             if self.kernel_space.is_valid_address(thread.StartAddress):
  3469.                 buf = self.kernel_space.zread(thread.StartAddress, 24)
  3470.                 dis = get_disasm_text(buf, thread.StartAddress.v())
  3471.                 if dis:
  3472.                     s += "{0}\n".format(dis)
  3473.  
  3474.             outfd.write("{0}\n".format(s))
  3475.  
  3476. #--------------------------------------------------------------------------------
  3477. # devicetree plugin
  3478. #--------------------------------------------------------------------------------
  3479.  
  3480. device_types = {
  3481.     0x00000027 : 'FILE_DEVICE_8042_PORT',          
  3482.     0x00000032 : 'FILE_DEVICE_ACPI',                
  3483.     0x00000029 : 'FILE_DEVICE_BATTERY',            
  3484.     0x00000001 : 'FILE_DEVICE_BEEP',                
  3485.     0x0000002a : 'FILE_DEVICE_BUS_EXTENDER',        
  3486.     0x00000002 : 'FILE_DEVICE_CD_ROM',              
  3487.     0x00000003 : 'FILE_DEVICE_CD_ROM_FILE_SYSTEM',  
  3488.     0x00000030 : 'FILE_DEVICE_CHANGER',            
  3489.     0x00000004 : 'FILE_DEVICE_CONTROLLER',          
  3490.     0x00000005 : 'FILE_DEVICE_DATALINK',            
  3491.     0x00000006 : 'FILE_DEVICE_DFS',                
  3492.     0x00000035 : 'FILE_DEVICE_DFS_FILE_SYSTEM',    
  3493.     0x00000036 : 'FILE_DEVICE_DFS_VOLUME',          
  3494.     0x00000007 : 'FILE_DEVICE_DISK',                
  3495.     0x00000008 : 'FILE_DEVICE_DISK_FILE_SYSTEM',    
  3496.     0x00000033 : 'FILE_DEVICE_DVD',                  
  3497.     0x00000009 : 'FILE_DEVICE_FILE_SYSTEM',          
  3498.     0x0000003a : 'FILE_DEVICE_FIPS',                
  3499.     0x00000034 : 'FILE_DEVICE_FULLSCREEN_VIDEO',    
  3500.     0x0000000a : 'FILE_DEVICE_INPORT_PORT',          
  3501.     0x0000000b : 'FILE_DEVICE_KEYBOARD',            
  3502.     0x0000002f : 'FILE_DEVICE_KS',                  
  3503.     0x00000039 : 'FILE_DEVICE_KSEC',                
  3504.     0x0000000c : 'FILE_DEVICE_MAILSLOT',            
  3505.     0x0000002d : 'FILE_DEVICE_MASS_STORAGE',        
  3506.     0x0000000d : 'FILE_DEVICE_MIDI_IN',              
  3507.     0x0000000e : 'FILE_DEVICE_MIDI_OUT',            
  3508.     0x0000002b : 'FILE_DEVICE_MODEM',                
  3509.     0x0000000f : 'FILE_DEVICE_MOUSE',                
  3510.     0x00000010 : 'FILE_DEVICE_MULTI_UNC_PROVIDER',  
  3511.     0x00000011 : 'FILE_DEVICE_NAMED_PIPE',          
  3512.     0x00000012 : 'FILE_DEVICE_NETWORK',              
  3513.     0x00000013 : 'FILE_DEVICE_NETWORK_BROWSER',      
  3514.     0x00000014 : 'FILE_DEVICE_NETWORK_FILE_SYSTEM',
  3515.     0x00000028 : 'FILE_DEVICE_NETWORK_REDIRECTOR',  
  3516.     0x00000015 : 'FILE_DEVICE_NULL',                
  3517.     0x00000016 : 'FILE_DEVICE_PARALLEL_PORT',      
  3518.     0x00000017 : 'FILE_DEVICE_PHYSICAL_NETCARD',    
  3519.     0x00000018 : 'FILE_DEVICE_PRINTER',            
  3520.     0x00000019 : 'FILE_DEVICE_SCANNER',            
  3521.     0x0000001c : 'FILE_DEVICE_SCREEN',              
  3522.     0x00000037 : 'FILE_DEVICE_SERENUM',            
  3523.     0x0000001a : 'FILE_DEVICE_SERIAL_MOUSE_PORT',  
  3524.     0x0000001b : 'FILE_DEVICE_SERIAL_PORT',        
  3525.     0x00000031 : 'FILE_DEVICE_SMARTCARD',          
  3526.     0x0000002e : 'FILE_DEVICE_SMB',                
  3527.     0x0000001d : 'FILE_DEVICE_SOUND',              
  3528.     0x0000001e : 'FILE_DEVICE_STREAMS',            
  3529.     0x0000001f : 'FILE_DEVICE_TAPE',                
  3530.     0x00000020 : 'FILE_DEVICE_TAPE_FILE_SYSTEM',    
  3531.     0x00000038 : 'FILE_DEVICE_TERMSRV',            
  3532.     0x00000021 : 'FILE_DEVICE_TRANSPORT',          
  3533.     0x00000022 : 'FILE_DEVICE_UNKNOWN',            
  3534.     0x0000002c : 'FILE_DEVICE_VDM',                
  3535.     0x00000023 : 'FILE_DEVICE_VIDEO',              
  3536.     0x00000024 : 'FILE_DEVICE_VIRTUAL_DISK',        
  3537.     0x00000025 : 'FILE_DEVICE_WAVE_IN',            
  3538.     0x00000026 : 'FILE_DEVICE_WAVE_OUT',            
  3539. }
  3540.  
  3541. class DeviceTree(filescan.DriverScan):
  3542.     "[MALWARE] Show device tree"
  3543.  
  3544.     def render_text(self, outfd, data):
  3545.  
  3546.         for object_obj, driver_obj, extension_obj, object_name in data:
  3547.  
  3548.             outfd.write("DRV 0x{0:08x} {1}\n".format(driver_obj.obj_offset,
  3549.                 self.parse_string(driver_obj.DriverName)))
  3550.  
  3551.             next_device = obj.Object("_DEVICE_OBJECT",
  3552.                 driver_obj.DeviceObject, self.kernel_address_space)
  3553.  
  3554.             while next_device:
  3555.  
  3556.                 outfd.write("---| DEV {0:#x} {1} {2}\n".format(
  3557.                     next_device.obj_offset,
  3558.                     get_obj_name(self.kernel_address_space, next_device),
  3559.                     device_types.get(next_device.DeviceType.v(), "FILE_DEVICE_UNKNOWN")))
  3560.  
  3561.                 att_device = next_device.AttachedDevice.dereference()
  3562.  
  3563.                 level = 0
  3564.  
  3565.                 while att_device:
  3566.  
  3567.                     name = get_obj_name(self.kernel_address_space, att_device)
  3568.                     name = name + " - " + self.parse_string(att_device.DriverObject.DriverName)
  3569.  
  3570.                     outfd.write("------{0}| ATT {1:#x} {2} {3}\n".format(
  3571.                         "---" * level,
  3572.                         att_device.obj_offset,
  3573.                         name,
  3574.                         device_types.get(att_device.DeviceType.v(), "FILE_DEVICE_UNKNOWN")))
  3575.  
  3576.                     att_device = att_device.AttachedDevice.dereference()
  3577.                     level += 1
  3578.  
  3579.                 next_device = next_device.NextDevice.dereference()
  3580.  
  3581. class Timers(commands.command):
  3582.     """Print kernel timers and associated module DPCs"""
  3583.  
  3584.     def __init__(self, config, *args):
  3585.         commands.command.__init__(self, config, *args)
  3586.  
  3587.     def find_list_head(self, mods, func, sig):
  3588.         """Find the KiTimerTableListHead given an exported
  3589.        function as a starting point and a small signature"""
  3590.         ntmod = find_module_by_name(mods, ["ntoskrnl.exe", "ntkrnlpa.exe"])
  3591.         if ntmod:
  3592.             func_addr = ntmod.getprocaddress(func)
  3593.             if func_addr:
  3594.                 data = ntmod.obj_vm.zread(func_addr, 200)
  3595.                 if data:
  3596.                     n = data.find(sig)
  3597.                     if n != -1:
  3598.                         return obj.Object('Pointer', vm = ntmod.obj_vm, offset = func_addr + n + len(sig))
  3599.  
  3600.     def calculate(self):
  3601.  
  3602.         addr_space = get_malware_space(self._config)
  3603.  
  3604.         # get the OS version we're analyzing
  3605.         version = (addr_space.profile.metadata.get('major', 0),
  3606.                    addr_space.profile.metadata.get('minor', 0))
  3607.  
  3608.         # get a list of drivers and sort them for easy lookups
  3609.         drivers = list(modules.lsmod(addr_space))
  3610.         mods, mod_addrs = get_sorted_mods(drivers)
  3611.  
  3612.         # KTIMERs collected
  3613.         timers = []
  3614.  
  3615.         # valid KTIMER.Header.Type values
  3616.         TimerNotificationObject    = 8
  3617.         TimerSynchronizationObject = 9
  3618.         valid_types = (TimerNotificationObject, TimerSynchronizationObject)
  3619.  
  3620.         if version == (5,1) or (version == (5,2) and addr_space.profile.__class__.__name__ == "Win2K3SP0x86"):
  3621.  
  3622.             """
  3623.            On XP SP0-SP3 x86 and Windows 2003 SP0, KiTimerTableListHead
  3624.            is an array of 256 _LIST_ENTRY for _KTIMERs.
  3625.            """
  3626.  
  3627.             KiTimerTableListHead = self.find_list_head(drivers,
  3628.                                         "KeUpdateSystemTime",
  3629.                                         "\x25\xFF\x00\x00\x00\x8D\x0C\xC5")
  3630.  
  3631.             lists = obj.Object("Array", offset = KiTimerTableListHead,
  3632.                                         vm = addr_space,
  3633.                                         targetType = '_LIST_ENTRY',
  3634.                                         count = 256)
  3635.  
  3636.             for l in lists:
  3637.                 for t in l.list_of_type("_KTIMER", "TimerListEntry"):
  3638.                     timers.append(t)
  3639.  
  3640.         elif version == (5,2) or version == (6,0):
  3641.  
  3642.             """
  3643.            On XP x64, Windows 2003 SP1-SP2, and Vista SP0-SP2, KiTimerTableListHead
  3644.            is an array of 512 _KTIMER_TABLE_ENTRY structs.
  3645.            """
  3646.  
  3647.             addr_space.profile.add_types({
  3648.                 '_KTIMER_TABLE_ENTRY' : [ 0x10, {
  3649.                     'Entry' : [ 0x0, ['_LIST_ENTRY']],
  3650.                     'Time'  : [ 0x8, ['_ULARGE_INTEGER']],
  3651.                 }]})
  3652.  
  3653.             KiTimerTableListHead = self.find_list_head(drivers,
  3654.                                         "KeCancelTimer",
  3655.                                         "\xC1\xE7\x04\x81\xC7")
  3656.  
  3657.             lists = obj.Object("Array", offset = KiTimerTableListHead,
  3658.                                         vm = addr_space,
  3659.                                         targetType = '_KTIMER_TABLE_ENTRY',
  3660.                                         count = 512)
  3661.  
  3662.             for l in lists:
  3663.                 for t in l.Entry.list_of_type("_KTIMER", "TimerListEntry"):
  3664.                     timers.append(t)
  3665.    
  3666.         elif version == (6,1):
  3667.  
  3668.             """
  3669.            On Windows 7, there is no more KiTimerTableListHead. The list is
  3670.            at _KPCR.PrcbData.TimerTable.TimerEntries (credits to Matt Suiche
  3671.            for this one. See http://pastebin.com/FiRsGW3f).
  3672.            """
  3673.  
  3674.             volmagic = obj.Object('VOLATILITY_MAGIC', 0x0, addr_space)
  3675.             kpcr = obj.Object("_KPCR", offset = volmagic.KPCR.v(), vm = addr_space)
  3676.            
  3677.             for table in kpcr.PrcbData.TimerTable.TimerEntries:
  3678.                 for t in table.Entry.list_of_type("_KTIMER", "TimerListEntry"):
  3679.                     timers.append(t)
  3680.  
  3681.         for timer in timers:
  3682.  
  3683.             # sanity check on the timer type
  3684.             if timer.Header.Type not in valid_types:
  3685.                 continue
  3686.  
  3687.             # ignore timers without DPCs
  3688.             if not timer.Dpc.is_valid() or not timer.Dpc.DeferredRoutine.is_valid():
  3689.                 continue
  3690.  
  3691.             # lookup the module containing the DPC
  3692.             mod = find_module(mods, mod_addrs, timer.Dpc.DeferredRoutine)
  3693.  
  3694.             yield timer, mod.BaseDllName if mod else "UNKNOWN"
  3695.  
  3696.     def render_text(self, outfd, data):
  3697.  
  3698.         outfd.write("{0:<12} {1:<20} {2:<10} {3:<10} {4:<12} {5}\n".format("Offset",
  3699.                 "DueTime", "Period(ms)", "Signaled", "Routine", "Module"))
  3700.  
  3701.         for timer, owner in data:
  3702.  
  3703.             outfd.write("{0:<#12x} {1:#010x}:{2:#010x} {3:<10} {4:<10} {5:<#12x} {6}\n".format(
  3704.                 timer.obj_offset,
  3705.                 timer.DueTime.HighPart,
  3706.                 timer.DueTime.LowPart,
  3707.                 timer.Period,
  3708.                 "Yes" if timer.Header.SignalState.v() else "-",
  3709.                 timer.Dpc.DeferredRoutine,
  3710.                 owner))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement