volatilityfoundation / volatility

An advanced memory forensics framework
http://volatilityfoundation.org/
GNU General Public License v2.0
7.35k stars 1.28k forks source link

mac_lsof: Issues with unicode from a String object #516

Open gleeda opened 6 years ago

gleeda commented 6 years ago

I have a Hebrew version of MacOS, and have a file open with a Hebrew name. Here is the output from lsof from the terminal:

# lsof |grep swp
vim       529              jamie    4u      REG                1,2      12288 447821 /Users/jamie/.שלום.txt.swp

Now when I run the mac_lsof plugin against the memory sample:

$ python vol.py -f macOS\ 10.12-4930b223.vmem --profile=MacHighSierra_10_13_1_17B48x64 mac_lsof |grep swp
Volatility Foundation Volatility Framework 2.6
     529               4 /Macintosh HD/Users/jamie/.????????.txt.swp

So let's investigate why:

$ python vol.py -f macOS\ 10.12-4930b223.vmem --profile=MacHighSierra_10_13_1_17B48x64 mac_volshell -p 529

In [1]: num_fds = proc().p_fd.fd_lastfile
   ...: nfiles  = proc().p_fd.fd_nfiles
   ...: if nfiles > num_fds:
   ...:     num_fds = nfiles
   ...: 
   ...: if num_fds > 4096:
   ...:     num_fds = 1024
   ...: 
   ...: fds = obj.Object('Array', offset = proc().p_fd.fd_ofiles, vm = proc().obj_vm, targetType = 'Pointer', count = num_fds)
   ...: 
   ...: for i, fd in enumerate(fds):
   ...:     f = fd.dereference_as("fileproc")
   ...:     if f and f.f_fglob.is_valid():
   ...:         ftype = f.f_fglob.fg_type
   ...:         if ftype == 'DTYPE_VNODE':
   ...:             vnode = f.f_fglob.fg_data.dereference_as("vnode")
   ...:             path = vnode.full_path()
   ...:             if path.find("jamie") != -1:
   ...:                 break
   ...:         else:
   ...:             path = "<%s>" % ftype.replace("DTYPE_", "").lower()
  ...:   
In [2]: print vnode.v_name.dereference()
.????????.txt.swp

# I can see the data is there though, and it looks like valid unicode:

In [3]: db(18446743524195602272)
0xffffff800e6a8f60  2e d7 a9 d7 9c d7 95 d7 9d 2e 74 78 74 2e 73 77   ..........txt.sw
0xffffff800e6a8f70  70 00 ad de ef be ad de ef be ad de ef be ad de   p...............
0xffffff800e6a8f80  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xffffff800e6a8f90  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xffffff800e6a8fa0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xffffff800e6a8fb0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xffffff800e6a8fc0  20 49 f0 0d 80 ff ff ff c0 a8 66 0e 80 ff ff ff   .I........f.....
0xffffff800e6a8fd0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

# To verify, I'll manually pull it out:

In [4]: item = addrspace().read(18446743524195602272, len(vnode.v_name.dereference()))

# Looky here!  This is correct output:

In [5]: print str(item)
.שלום.txt.swp

Now I thought that maybe I could fix it like I did in https://volatility-labs.blogspot.com/2015/06/volshell-quickie-case-of-missing.html , however, there's an issue with how the length is found (from volatility/plugins/overlays/basic.py):

 55     def v(self):
 56         """
[snip]
 69         """
 70         result = self.obj_vm.zread(self.obj_offset, self.length) # <-- self.length is wrong
 71         if not result:
 72             return obj.NoneObject("Cannot read string length {0} at {1:#x}".format(self.length, self.obj_offset))
 73         return result

We can see that self.length is wrong:

In [6]: vnode.v_name.length
Out[6]: 256                             # <- this should be 17!

In [7]: len(vnode.v_name.dereference())
Out[7]: 17

In [8]: vnode.v_name.dereference().v()
Out[8]: '.\xd7\xa9\xd7\x9c\xd7\x95\xd7\x9d.txt.swp\x00\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\xef\xbe\xad\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 I\xf0\r\x80\xff\xff\xff\xc0\xa8f\x0e\x80\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\xd3\xfd\r\x80\xff\xff\xff\x00(u\x0e\x80\xff\xff\xff\x00\xe6u\x0e\x80\xff\xff\xff\x00\xeay\x0e\x80\xff\xff\xff\x00V\x0f\x0e\x80\xff\xff\xff\x00P\xd6\x10\x80\xff\xff\xff\x00\x00%\x0f\x80\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

I'm not sure what's the best way to fix this, and I'm not sure yet if there are issues on the windows, or linux sides if they rely on this function of the String object.

gleeda commented 6 years ago

BTW, I'm guessing any other (at least Mac) plugins that rely on String will have the same issue.