9

My girlfriend's Macbook crashed while attempting to restore from a hibernated file. The progress bar stopped at ~10%, after which we restarted the computer for a normal startup.

This hibernated memory image had an unsaved document open in Pages, which we'd like to recover. There is a sleepimage in /private/var/vm, which I assume is the hibernate image which never got correctly restored. We backed up this thing to keep it alive.

We tried to strings sleepimage | grep known_substring but it returned nothing. grep -a known_substring sleepimage also did nothing, so I'm assuming that Pages didn't keep the text data in memory as plain text.

Edit: After reading this answer on Binary grep I tried to perl -ln0777e 'print unpack("H*",$1), "\n", pos() while /(null_padded_substring)/g' sleepimage, again being fruitless. I padded it with nulls in order to attempt a match for UTF-8 text. Then I tried with .* globs between each character –- still no dice.

So Pages probably doesn't store text by any common encoding in memory. I would need to find a translation rule between ASCII string and Pages data representation -- I'm thinking maybe some kind of Objective C string buffer. To me it seems very weird to store character data as anything else than a sequence of characters, but this seems to be what Pages is doing.

If you have any idea on how to figure out the in-memory representation of text inside Pages, it might be very helpful in solving this problem. Maybe I can dump and read the process memory in some simple way?

Another possible solution is simpler -- I'm assuming it is somehow possible to reboot the computer from this sleepimage, but I can't find any documentation as to how you would proceed with that. Some other users (macrumors) seem to have encountered this, but for all the forum questions I've found, none of them have responses.

The OS X version is Snow Leopard, 10.6.8.

Complex suggestions involving programming are welcome. I do C and Python.

Thank you.

7
  • 1
    Hopefully you made a copy of that file so you don't end up examining a newer sleepimage that got written after the reboot. Then you may wanna recreate the situation (without crash) with a maximum free RAM - i.e. open only Pages write a unique text and let the OS write a new sleepimage; and then start examining that for your unique text.
    – iolsmit
    Commented Sep 27, 2012 at 20:56
  • @iolsmit Yes, all tests are performed on a copy of sleepimage. Sifting through another image looking for unique text would be just as difficult, since the image would still be 4GB in size, and the Pages memory block would be allocated somewhere random in that file. I suppose I could zero out the RAM, then open pages, and then look for non-zero sequences in the sleepimage, though. But Pages eats up 200MB of memory regardless -- still a small needle in the haystack.
    – sapht
    Commented Sep 27, 2012 at 21:16
  • Your text is stored with 0x00 in-between each character, so you have to search for that or for this string: loobsdpkdbik ; see also my answer below
    – iolsmit
    Commented Sep 27, 2012 at 23:42
  • Doesn't pages have versions turned on by default even if you don't have a time machine backup (look for mobile backups where the system backs things up even without the backup drive connected)? Have you ruled out easier ways to get the file back without heroically conducting a forensic analysis on the sleep image file format? (no matter how awesome that will be if you pull it off ;)
    – bmike
    Commented Sep 28, 2012 at 18:42
  • @bmike Versions only came with Lion but that machine is on Snow Leopard (10.6.8) and I remember losing quite a bit of work because of iWork crashing on SL and not having an auto-save ...
    – iolsmit
    Commented Sep 29, 2012 at 9:33

2 Answers 2

1
+100

Update with pictures:

  • that loobsdpkdbik identifier mentioned first, isn't one - just happend to be before my text the fist time I tried it.

  • part of the text seems to get "lost" (i.e. not saved in one continuous memory stretch) and this may worsen with RAM usage

  • you may not be able to recover meaningful text from the sleepimage

Now my original text (with typo in 1st paragraph, sry Mr. Matisse):

Hidden Gems: MoMa’s Abby Aldrich Rockefeller Sculpture Garden, designed by Philip Johnson in 1953, is a spectacular urban oasis with its reflecting pools and beautiful landscaping. This outdoor gallery is installed with changing displays of outdoor sculpture, including works by Aristide Maillol, Alexander Calder, Henri Maisse, Pablo Picasso, and Richard Serra.

While visiting the new painting and sculpture galleries at MoMa, be sure to traverse the staircase bridging the forth and fifth floors in order to see Henri Matisse’s monumental image of joy and energy, Dance (1909). The painting was originally intended to hang in the stair hall of a Russian palace in Moscow.

And the recovered text:

Hidden Gems: Ma s Abby Aldrich Rockeller Sculpre Gn, desigd by Phip John 1953, is spectacular ursithtseflecting pools autifulandscapg. This outdoor gallery is italled with changing displays of outor sculpre, includg workby Aristide Maillol, Alexander Calder, Henri Maisse, Pabloicasso, anchard Sea.

While ving the new paintg sculpture gallies at Ma, be sure to traver t stase bridging the forth fth flrsn ordeto s Henri Matse s mtal imagof joy and ey, Dan (19). The painting waorinally intded to hg t stair hall of Rsian palace Moscow.

And the screen-shots:

Original text in Pages

Recovered text from sleepimage


It seems that for an (unsaved) Pages document (almost) all characters in your text are separated by 0x00 in memory - thus STRING becomes S.T.R.I.N.G with . being 0x00. So you either have to search for that; I can recommend 0xED for a graphical front-end... ..or you search for loobsdpkdbik which seems to be (part of) an identifier, which comes 5 bytes before the text (at least only in one case).

4
  • Hmm, I did a search for "loobsdpkdbik", but still empty. Did this identifier appear before every variant of the unsaved document? Maybe it signifies something about the document -- like window inheritance, default font, etc... I searched for a null-padded string using perl earlier, i.e. s\0u\0b\0s\0t\0r\0i\0n\0g, didn't work, more description is in my original question. Oh -- how did you find this out?
    – sapht
    Commented Sep 28, 2012 at 6:49
  • @sapht I updated my answer; it seems that the text is not stored in an continuous stretch in memory, which could make it impossible to recover from the sleepimage. And that "loobsdpkdbik" is not related to the Pages document, just happend to be before my text.
    – iolsmit
    Commented Sep 28, 2012 at 11:40
  • Maybe the substring was among the mumbled words of discontinuous memory then. I still haven't found any data in the sleepimage, but we might just have to search for the right substring. Or the memory block was never written. Good work investigating the sleepimage, thanks.
    – sapht
    Commented Oct 1, 2012 at 16:00
  • @sapht If your sleepimage is not corrupt it should contain the full text of the Pages document - as restoring the RAM would put it where the system was when it hibernated. I would recommend to try the sleepimage in a virtual machine: Install any supported OS X in a virtual machine (or use VMware fusion 4.1 ;) - then clone your machine to the virtual HDD and try booting from the sleepimage.
    – iolsmit
    Commented Oct 1, 2012 at 18:39
2

First try, IF known_string WAS stored in plain text (not the case)

I guess you could try using

grep -Ubo --binary-files=text "known_substring" sleepimage 

From that, -U parameter specifies search on binary files, -b specifies that the offset in bytes to the matching part should be displayed and, lastly, -o specifies that only the matching part should be printed.

If that works, you would know the offset in bytes to get to that region, but I would not know exactly how to proceed there. Depending on the filetype, you could probably check for the filetype signature near that informed offset and try to isolate only the bytes that do make part of that file. For this, I guess you could either write a C program to do that, or maybe execute hexdump -s known_offset sleepimage and try getting only the bytes that relate to the file you need.

For instance, suppose I wanted to know something about Chrome:

$ sudo grep -Ubo --binary-files=text -i "chrome" sleepimage
3775011731:chrome

So I know I got an occurrence of chrome at the byte offset 3775011731. Hence I could:

$ sudo hexdump -s 3775011731 sleepimage | head -n 3
e1021b93 09 09 3c 73 74 72 69 6e 67 3e 2e 63 68 72 6f 6d
e1021ba3 65 2e 67 6f 6f 67 6c 65 2e 63 6f 6d 3c 2f 73 74
e1021bb3 72 69 6e 67 3e 0a 09 09 3c 6b 65 79 3e 45 78 70

The tricky part would be to get only the bytes you want. If the filetype has a known header you could maybe subtract the header size in bytes from the hexdump offset, so you get the file "since the beginning". If the filetype has a known "EOF" signature, you could try searching for it too and hence get only the bytes up to that point.

What is your filetype? Do you think that some procedure like this could be used in your case? Note that I have never done this before, and I am basing myself on a lot of "guesses", but I suppose something like this has a little chance of working..

Second try, a slow method for parsing all bytes

The method before does not work because it also searches only for plain text, my bet. For this second text I created a simple C program containing:

#include <stdio.h>

int main () {
  printf("assim");
  return 0;
}

So I could search for "assim", which would be your known_string, in that text. In order to know what bytes to search for I did:

$ echo -n "assim" | hexdump
0000000 61 73 73 69 6d                                 
0000005

Hence, I must find "61 73 73 69 6d". After compiling that simple C source into the program "tt", I did the following:

hexdump -v -e '/1 "%02X\n"' tt | # format output for hexdump of file tt
    pcregrep -M --color -A 3 -B 3 "61\n73\n73\n69\n6D" # get 3 bytes A-fter and 3 bytes B-fore the occurence

Which returned to me:

enter image description here

If you did something like that, I guess you could get your data.. It would be kind of slow to parse 2~8GBs of bytes though...

Note that in this approach you must find the hexes in capital letter (write 6D instead of 6d on the last grep), not in under-case letters, and use \n instead of white-spaces (so you can use -A and -B for the grep). You could use grep -i so it became case-insensitive, but it would be a little slower. Hence, just use capitals if this is used.

Or, if you want a do-all automated "script":

FILENAME=tt # file to parse looking for string
BEFORE=3 # bytes before occurrence
AFER=3 # bytes after occurrence
KNOWNSTRING="assim" # string to search for

ks_bytes="$(echo -n "$KNOWNSTRING" | hexdump | head -n1 | cut -d " " -f2- | tr '[:lower:]' '[:upper:]' | sed -e 's/ *$//g' -e 's/ /\\n/g')"

hexdump -v -e '/1 "%02X\n"' $FILENAME | pcregrep -M --color -A $AFER -B $BEFORE $ks_bytes
5
  • The text is only stored in memory, as the file was never saved. So there is no real file type, only the kind of representation that Pages is keeping internally for the data. Passing -U to grep didn't seem to make much difference (a is short for --binary-files=text). If I had the byte offset, I could definitely proceed, but either the file is corrupt, or Pages is storing the data in some non-ASCII way. Perhaps UTF-8, but grep won't accept null bytes for a match character.
    – sapht
    Commented Sep 27, 2012 at 19:22
  • I edited the post with another try.. it seems to work.. but is really slow and you will have to "guess" how many bytes you want before and after the occurrence of the known_string. Note: when I do echo -n "assim" | hexdump I get the hexdump for the UTF-8 encoding, you could try echo -n "assim" | iconv -t UTF-16 | hexdump for other encodings, UTF-16 in this case, I have no Idead on how it is stored on memory.. But in my case it was stored as UTF-8 indeed :)
    – FernandoH
    Commented Sep 27, 2012 at 20:18
  • Hmm, well, the hex dump for your C program prints the text since it's actually embedded in the binary -- gcc compiles that way so that all static character buffers are stored in the program itself for in-memory reference. But for Pages that data was created at runti e. I updated my answer with a new match I tried via perl, which was fruitless, so I'm pretty sure the text is stored in some weird non-standard manner, as the ASCII bytes aren't even the same. Perhaps some objective C string buffer...
    – sapht
    Commented Sep 27, 2012 at 21:04
  • Hummm.. What if you tried searching for the string "Pages.app" instead? I would not know how to proceed from there if anything was found (such as, what belongs to the App and what is your document?), but if we were to keep this train of thought, it could be the beginning of a try.. Although I must admit that there must be easier alternatives, this would be a pretty laborious one
    – FernandoH
    Commented Sep 27, 2012 at 21:14
  • Actually, do you remember pieces from that Papers file? Even though it was stored on memory, if you know some exact sentences that were written there (if you remember or if you have a previous version of the file), you could try searching directly for these! This would be much easier, I guess :) And as Pages is a word editing program, I guess you want to recover what was written, right? If that is the case, search for the content rather than meta information, it may be easier.. I hope, at least..
    – FernandoH
    Commented Sep 27, 2012 at 21:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .