12

Igor posted a great answer previously on SO about the format of the Linux kernel image on ARM.

Assuming I can't boot my kernel image, can someone give me pointers on finding this compressed symbol table in the binary?

  • on what basis do you assume there are symbols? It is possible they have been stripped, because they're only needed - for example - to load kernel modules. If everything is linked into the kernel there is no need to have symbols. ARM being used disproportionately on embedded devices, there could be good reasons to strip symbols out of concern for size. On a (similar) running kernel (or in an emulator like QEMU), have you tried Igor's suggestions to see whether symbols exist? – 0xC0000022L Apr 24 '13 at 17:24
  • @0xC0000022L I have a running copy of the kernel and can cat /proc/kallsyms but they don't have the addresses, just the names (addresses appear as 0). I'm trying to understand (1) where these names are coming from -- I'm assuming it's from that compressed table, but I'm academically interested in seeing it; (2) how to resolve the addresses. – mrduclaw Apr 24 '13 at 17:39
  • 0xC0000022L keeping the symbol is an option you can choose on menu config. Without symbols kernel oops will be meaningless. I have strong reason to believe that the symbols weren't stripped. – Mellowcandle Apr 24 '13 at 17:56
  • @Mellowcandle do you happen to know which menu config option removes the symbols? – mrduclaw Apr 24 '13 at 18:03
  • @mrduclaw: fair enough, just trying to make sure we all aren't chasing a ghost ;) - Mellowcandle: if size is a main motivation then kernel OOPS are meaningless by themselves, because if the kernel doesn't fit on the device, it won't run anyway :) – 0xC0000022L Apr 24 '13 at 18:09
13

After decompressing and loading the kernel, you need to find a couple of tables that encode the compressed symbol table. These are (in the usual order they are placed in binary):

  • kallsyms_addresses - a table of addresses to all public symbols in the kernel
  • kallsyms_num_syms - not a table but just an integer with total number of symbols (should match previous table)
  • kallsyms_names - a list of length-prefixed byte arrays that encode indexes into the token table
  • kallsyms_token_table - a list of 256 zero-terminated tokens from which symbol names are built
  • kallsyms_token_index - 256 shorts pointing to the corresponding entry in kallsyms_token_table

They're not hard to find with some experience. A good way to find the first one is to look for several 0xC0008000 values in a row, because a typical kernel symbol table starts like this:

C0008000 T __init_begin
C0008000 T _sinittext
C0008000 T _stext
C0008000 T stext

After locating the tables the symbol recovery is trivial. I made a script for IDA that does it automatically, you can find it here (kallsyms.py in the tools zip).

For more the details of how it's implemented in the kernel, see kernel/kallsyms.c.

  • Ah, this is exactly what I was looking for. Thank you! So far I was able to identify the kallsyms_addresses, kallsyms_num_syms, and kall_syms_names. I thought I had located the other two, but your script failed to decode the names; I'm sure I did something wrong. It seems difficult to know how large the kallsyms_names table will be. Do you have any suggestions? Thank you again! – mrduclaw Apr 26 '13 at 2:22
  • kallsyms_token_table should be pretty obvious - it will look similar to "tcp_",0,"ca",0,"unc_",0,... etc. – Igor Skochinsky Apr 26 '13 at 2:49
  • Damn, I think that's the one I found. I'll play with it some more. I'm sure it's something incredibly silly on my part. Thanks again! – mrduclaw Apr 26 '13 at 3:06
9

You mentioned that you do have a running kernel available. It is possible to obtain symbol information from a running kernel by reading /proc/kallsyms. On newer distributions, this information is disabled by default for security reasons (all symbols will be displayed as 0x0 addresses), but you can manually enable it by running the following command as root:

echo 0 > /proc/sys/kernel/kptr_restrict

Once you've obtained the list of kernel symbols/address pairs, it should be easy to convert to any format desired, e.g. an IDA .idc script for import.

  • @0xC0000022L He responded to you in the comments: "I have a running copy of the kernel and can cat /proc/kallsyms but they don't have the addresses, just the names (addresses appear as 0)." – mncoppola Apr 25 '13 at 14:42
5

This is a bit tricky to answer without getting my hands on the file and verifying a few assumptions based on the question and the linked answer. However, let me try, perhaps we can extend it further if you elaborate more on some aspects of the file.

We know it's an ARM file and from your description this pretty much sounds like an ARM kernel image for U-Boot. Now the problem is that I cannot know whether that's true, but you could run the oft mentioned binwalk or firmware-mod-kit on the file to see what that gives you.

If this is an ARM kernel image for U-Boot, you can likely get to the gzip data by skipping the first 64 Bytes of the image (see this answer over at StackOverflow). The gist is:

dd if=<image> of=<recovered file> bs=64 skip=1

This skips 1 block of 64 bytes and otherwise writes the data from <image> into <recovered file>. Essentially it reverses - in part - the effects of the mkimage tool, which is part of U-Boot.

Now, assuming everything so far works - and that is a huge assumption - you should be able to decompress (gzip -d) the resulting file and end up with something you can hopefully grep. If I was you I'd then use file to check what kind of file it is and process it further if I happened to get anything meaningful out of it. If not, I would treat the file with binwalk again and failing that run strings on it.


Edit x+1:

Okay, tried the process myself. Downloaded this Debian package, unpacked it, got a zImage-2.6.28.10-power51 which I then looked at in 010 Editor and it's true, this is an ARM kernel image according to the marker (see reading section below):

010 Editor showing the zImage

After that I tried to skip the first 64 Byte and then decompress the rest, to no avail. Investigating a bit more.

If you manage to get more information using this incomplete answer, please edit your question and I'll amend my answer once I notice your edit, to add more (hopefully useful) information.

Edit x+2:

Okay, for the zImage in question it turns out binwalk, mentioned in my original answer, can at least handle the file and outputs:

12900           0x3264          gzip compressed data, from Unix, last modified: Mon Jul 23 13:41:37 2012, max compression

Lovely. Do let's run dd to extract the gzip stuff and then extract it:

dd if=zImage-2.6.28.10-power51 of=extract.gz bs=12900 skip=1 && gunzip extract.gz && ls -l extract

Once I extracted it, I ran binwalk again after noticing that file didn't yield a result:

DECIMAL         HEX             DESCRIPTION
-------------------------------------------------------------------------------------------------------
135456          0x21120         gzip compressed data, from Unix, last modified: Mon Jul 23 13:38:47 2012, max compression
973460          0xEDA94         ELF
1070320         0x1054F0        CramFS filesystem, big endian size 2126262976 CRC 0xdc0a0e1, edition 4040895977, 78662882 blocks, 271618533 files

However, I don't want to proceed now without further input from you. Just an example how it could be investigated. One more thing strings does produce a list of symbols, but since I presume you want symbols and their addresses, I reckon there is more to be investigated.


Further reading:

  • this documentation to verify whether this indeed is an ARM kernel image in the format we expect/assume. In particular check the assumption that 0x016F2818 can be found at offset 0x24.
  • this forum entry, in particular the post by user fattire, which mentions

    There's a 64 byte header you have to cut off of uRamdisk/uRecRam:

    dd if=uRamdisk of=uRamdisk.cpio.gz bs=64 skip=1

    gunzip uRamdisk.cpi.gz

    cpio -i -F uRamdisk.cpio

    which essentially implies that you have to expect an initrd (hence the CPIO format) at offset 64. That is, the "kernel image" would actually turn out to be the old kernel format which embedded the initrd (also see mkimage man page under "Create old legacy image").

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.