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?
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?
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 kernelkallsyms_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 tablekallsyms_token_table
- a list of 256 zero-terminated tokens from which symbol names are builtkallsyms_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
.
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!
kallsyms_token_table
should be pretty obvious - it will look similar to "tcp_",0,"ca",0,"unc_",0,...
etc.
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.
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):
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.
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").
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.