It looks worse than you can imagine! I can imagine some pretty bad things! That's why I said *worse*! | |
Terry Pratchett, Moving Pictures |
The fancy output format of The address of main was chosen for a reason. It is valid input for /bin/sh. Let's see whether the program from The magic of the Elf has main at the same offset.
While the simplicity of ndisasm is charming, objdump requires heavy machinery. The output includes function labels. Filtering the complete disassembly can yield the desired code without prior knowledge of the function address. We use --start-address for symmetry with ndisasm, however. That option accepts only numeric values, not symbol names.
Command: pre/sparc-sunos5.7/magic_elf/objdump.sh
#!/usr/xpg4/bin/sh
. out/sparc-sunos5.7/magic_elf/addr_of_main
/usr/local/bin/objdump -d --start-address=0x${main_l} \
tmp/sparc-sunos5.7/magic_elf/magic_elf \
| pre/sparc-sunos5.7/magic_elf/objdump_format.sh ${main_l} |
Command: pre/sparc-sunos5.7/magic_elf/objdump_format.sh
#!/usr/xpg4/bin/sh
xdigit='[[:xdigit:]]\{1,\}'
start_address=${1:-$xdigit}
# white space is tab-stop, not just spaces
tab='[^ ]\{1,\} '
space='[[:space:]]\{1,\}'
nospace='[^[:space:]]\{1,\}'
/usr/xpg4/bin/sed -n -e "/^ *${start_address}:/,$ p" \
| /usr/xpg4/bin/sed \
-e 's/ * / /g' \
-e "s/${space}\(!\)/ \1/" \
-e "s/^\(${tab}${tab}${nospace}\)${space}/\1 /" \
-e "/^[[:space:]]*\.*[[:space:]]*$/q" \
-e "/\<\(restore\|unimp\)\>/q" \
| /usr/bin/expand -t 12,32,40,60 |
Output: out/sparc-sunos5.7/magic_elf/objdump.asm
10658: 9d e3 bf 90 save %sp, -112, %sp
1065c: 90 10 20 01 mov 1, %o0
10660: 13 00 00 40 sethi %hi(0x10000), %o1
10664: 92 12 60 01 or %o1, 1, %o1 ! 10001 <*ABS*+0x10001>
10668: 40 00 43 4d call 2139c <_PROCEDURE_LINKAGE_TABLE_+0x78>
1066c: 94 10 20 03 mov 3, %o2
10670: 81 c7 e0 08 ret
10674: 91 e8 20 00 restore %g0, 0, %o0
|
This looks like a real main. So both programs indeed have main at the same offset. Unfortunately a brief look through /bin proves this to be pure chance. And instead of a real system call for write(2) we see something strange. It resolves to a location in a shared library. But what function in what library?
Command: pre/sparc-sunos5.7/magic_elf/gdb_core.sh
#!/usr/xpg4/bin/sh
/usr/local/bin/gdb ${1} -q <<EOF 2>&1
disassemble ${2}
EOF |
Command: pre/sparc-sunos5.7/magic_elf/gdb_format.sh
#!/usr/xpg4/bin/sh
space='[[:space:]]\{1,\}'
nospace='[^[:space:]]\{1,\}'
# sed-expressions substitute tab-stops, not spaces
/usr/bin/col -bx \
| /usr/xpg4/bin/sed -n \
-e "s/:${space}\(${nospace}\)${space}/: \1 /p" \
-e "s/${space}!/ !/" \
-e "/\(restore\|unimp\)/q" \
| /usr/bin/expand -t 32,40,60 |
Command: pre/sparc-sunos5.7/magic_elf/gdb.sh
#!/usr/xpg4/bin/sh
file=${1:-tmp/sparc-sunos5.7/magic_elf/magic_elf}
func=${2:-main}
/usr/bin/echo "[func=${func}]"
pre/sparc-sunos5.7/magic_elf/gdb_core.sh ${file} ${func} \
| pre/sparc-sunos5.7/magic_elf/gdb_format.sh |
Output: out/sparc-sunos5.7/magic_elf/gdb
[func=main]
0x10658 <main>: save %sp, -112, %sp
0x1065c <main+4>: mov 1, %o0
0x10660 <main+8>: sethi %hi(0x10000), %o1
0x10664 <main+12>: or %o1, 1, %o1 ! 0x10001
0x10668 <main+16>: call 0x2139c <write>
0x1066c <main+20>: mov 3, %o2
0x10674 <main+28>: restore %g0, 0, %o0 |
Looks better. We need a way to retrieve the function name, write, from this output. Then we can feed gdb this argument for disassembly.
Command: pre/sparc-sunos5.7/evil_magic/first_gdb_func.sed
#!/usr/xpg4/bin/sed -nf
/.*<\(.*\)>$/ {
s//\1/
p
q
} |
Command: pre/sparc-sunos5.7/evil_magic/gdb_write.sh
#!/usr/xpg4/bin/sh
file=${1:-tmp/sparc-sunos5.7/magic_elf/magic_elf}
func=$( pre/sparc-sunos5.7/evil_magic/first_gdb_func.sed \
< out/sparc-sunos5.7/magic_elf/gdb )
/usr/bin/echo "[func=${func}]"
pre/sparc-sunos5.7/magic_elf/gdb_core.sh ${file} ${func} \
| pre/sparc-sunos5.7/magic_elf/gdb_format.sh |
Output: out/sparc-sunos5.7/evil_magic/write.gdb
[func=write]
0x2139c <write>: sethi %hi(0x1e000), %g1
0x213a0 <write+4>: b,a 0x21324 <__DTOR_END__+4> |
Oops. Shared libraries don't share their secrets with everyone.
We can now search for a fine manual explaining how to debug shared libraries. Or just compile the bugger static.
Command: pre/sparc-sunos5.7/magic_elf/cc_static.sh
#!/usr/xpg4/bin/sh
/usr/local/bin/gcc -O1 -I out/sparc-sunos5.7 -D NDEBUG -static \
-o tmp/sparc-sunos5.7/magic_elf/magic_elf_static \
pre/sparc-sunos5.7/magic_elf/magic_elf.c \
&& /usr/xpg4/bin/ls -l tmp/sparc-sunos5.7/magic_elf \
&& tmp/sparc-sunos5.7/magic_elf/magic_elf_static |
Output: out/sparc-sunos5.7/magic_elf/magic_elf_static
total 600
-rwxr-xr-x 1 alba alba 23293 Oct 23 01:56 magic_elf
-rwxr-xr-x 1 alba alba 277483 Oct 23 01:56 magic_elf_static
ELF |
Seems we found an easy way to fill up the hard disk. Anyway, what has gdb(1) to say about it?
Output: out/sparc-sunos5.7/evil_magic/static_main.gdb
[func=main]
0x101d0 <main>: save %sp, -112, %sp
0x101d4 <main+4>: mov 1, %o0
0x101d8 <main+8>: sethi %hi(0x10000), %o1
0x101dc <main+12>: or %o1, 1, %o1 ! 0x10001
0x101e0 <main+16>: call 0x11960 <write>
0x101e4 <main+20>: mov 3, %o2
0x101ec <main+28>: restore %g0, 0, %o0 |
The function was called write before, it is called write now. Let's look what is behind the name.
Source: pre/sparc-sunos5.7/evil_magic/static_write.sh
#!/usr/xpg4/bin/sh
file=${1:-tmp/sparc-sunos5.7/magic_elf/magic_elf_static}
func=$( pre/sparc-sunos5.7/evil_magic/first_gdb_func.sed \
< out/sparc-sunos5.7/evil_magic/static_main.gdb )
/usr/bin/echo "[func=${func}]"
pre/sparc-sunos5.7/magic_elf/gdb_core.sh ${file} ${func} \
| pre/sparc-sunos5.7/magic_elf/gdb_format.sh |
Output: out/sparc-sunos5.7/evil_magic/static_write.gdb
[func=write]
0x11960 <write>: sethi %hi(0x45000), %g1
0x11968 <write+8>: jmp %g1 |
Above disassembly is not guaranteed to work. The names of symbols imported by libraries differ from one platform to the other, and from one compiler to the other. A more rational approach is to search the listing of all symbols for similar names and identical addresses.
Command: pre/sparc-sunos5.7/evil_magic/nm.sh
#!/usr/xpg4/bin/sh
# -p produces same output format on SunOS and GNU
/usr/xpg4/bin/nm -p tmp/sparc-sunos5.7/magic_elf/magic_elf_static \
| /usr/xpg4/bin/grep '[^[:alnum:]]write\>' \
| /usr/xpg4/bin/sort |
Output: out/sparc-sunos5.7/evil_magic/nm
0000000000 f write.s
0000072032 T write
0000091852 T _libc_write
0000091852 T _write |
I suspect there is actually order behind the chaos. The symbol _write, with a varying number of leading underscores, seems to be "the real thing" on all platforms. The aliases for the value, 0x91852, differ a lot.
Command: pre/sparc-sunos5.7/evil_magic/gdb_nm.sh
#!/usr/xpg4/bin/sh
file=${1:-tmp/sparc-sunos5.7/magic_elf/magic_elf_static}
func=$( /usr/xpg4/bin/nm -p ${file} \
| /usr/xpg4/bin/sed -ne 's/.*[tTwW] \(\<__*write\>\)/\1/p' )
/usr/bin/echo "[func=${func}]"
pre/sparc-sunos5.7/magic_elf/gdb_core.sh ${file} ${func} \
| pre/sparc-sunos5.7/magic_elf/gdb_format.sh |
Output: out/sparc-sunos5.7/evil_magic/gdb_nm
[func=_write]
0x166cc <_write>: st %o0, [ %sp + 0x44 ]
0x166d0 <_write+4>: mov 4, %g1
0x166d4 <_write+8>: ta 8
0x166d8 <_write+12>: bcc 0x166f0 <_write+36>
0x166dc <_write+16>: cmp %o0, 0x5b
0x166e0 <_write+20>: be,a 0x166d0 <_write+4>
0x166e4 <_write+24>: ld [ %sp + 0x44 ], %o0
0x166e8 <_write+28>: b 0x16724 <_cerror> |
intro(2) describes the surroundings of system calls and contains a list of available man-pages. An additional look into directory /usr/share/man/sman2 can't hurt, though. A general purpose interface to system calls is described by syscall(2). Anyway, the statement mov 4,%g1 corresponds to the value of SYS_write in /usr/include/sys/syscall.h. Note that Linux uses ta 0x10 while Solaris uses ta 8. There are other differences, but they are beyond the scope of this document.