
Here are some hints for using Fenris for the "Reverse Challenge"
contest announced by Lance Spitzner and the Honeynet Project in May 2002
[http://project.honeynet.org/reverse/]. I don't want to spoil the fun,
but will provide some very basic guidance on how to begin. This text
applies to Fenris 0.02b. This (or newer) release can be obtained 
from http://lcamtuf.coredump.cx/fenris/devel.shtml. Some of the features
described here were not available in Fenris 0.01. Special thanks go to
all the contributors who made this project possible, and especially to
Bulba, for pointing out my stupidity ;-) 

Before proceeding, read the documentation for Fenris, available at
http://lcamtuf.coredump.cx/fenris/README, especially the section about 
security considerations for additional hints and security precautions on 
using real-time tracers for forensics. Basically, you need to know what
level of security is provided by Fenris, where to run it, what privileges
to give, what other tools to use. Then, be advised that Fenris is not supposed 
to give you all the answers. It is just going to make your life much simplier.
For in-depth analysis of a single procedure, it is always best to use a
disassembler. Fenris will help you to separate code coming from different
sources, determine call structure, parameters, general behavior, and so on,
but you still have to do your homework. This text is not very technical,
and for some of you, many sentences can be more than obvious - but it gives
a good idea of how Fenris can be used to simplify reverse engineering tasks,
and that's its only purpose.

So, let's get started...

First of all, the binary is linked statically. This is quite obvious, try
'file' on it. It is a C program, compiled with GCC. You can find GCC string
signatures inside the binary. Then, it came from a libc5 system, which can be 
told from the "The Linux C library 5.3.12" string inside. The ability to find
small hints such as this string is extremely important for reverse
engineering. NEVER RELY ON A SINGLE SOURCE OF INFORMATION. Always verify your
findings, always look before trying. Forensics is like surgery, it requires
precision and systematic approach. Even if you train on dummies, midless
force won't make you a good surgeon. Take notes. Note every detail, all
the things to "check out later". Draw a structural graph as you dive
into the code. Use pen and paper, not your computer... Well, ok, back to
the code...

So, an obscure libc version not directly supported by Fenris was used.
Fenris will not be able to detect libc prolog automatically, so you need
to use -s option. You will get some irrelevant internal libc functions
at the beginning, not a big deal, just skip them, you can tell them by
their names. More importantly, to detect what belongs to libc and what comes
from the author of this code, you'd have to use a special fingerprints 
database, provided in support/ subdirectory. The database is named 
'fn-libc5.dat'. This code is very likely to fork into background, so be sure 
to trace all execution branches, use -f option. Let the fun begin:

nobody@sandbox$ ./fenris -s -f -L support/fn-libc5.dat ./the-binary
...
19953:03    SYS geteuid () = 99
19953:03    <805721a> cndt: if-above block (signed) +16 executed
19953:02   ...return from function = <void>
19953:02   <8048182> cndt: conditional block +8 executed
...
19953:05      SYS exit (-1) = ???
19953:04     ...function never returned (program exited before).
19953:03    ...function never returned (program exited before).

Hmm, geteuid() followed by some conditionals followed by exit? That's not 
very nice, it apparently wants to be launched as root... Humm, let's
run it again, tracing eip...

nobody@sandbox$ ./fenris -s -f -L support/fn-libc5.dat -p ./the-binary
...
[08057218] 19963:03    SYS geteuid () = 99
...

The address probably isn't very precise, as Fenris does report syscalls
after they return, So, let's look at the surrounding area:

(gdb) disass 0x0805720f 0x08057220
Dump of assembler code from 0x805720f to 0x8057220:
0x805720f:      mov    $0x31,%eax
0x8057214:      int    $0x80
0x8057216:      mov    %eax,%edx
0x8057218:      test   %edx,%edx
0x805721a:      jge    0x805722c
0x805721c:      neg    %edx
0x805721e:      push   %edx
0x805721f:      call   0x8056e64
End of assembler dump.

You can also use objdump if you're not sure about the address:

nobody@sandbox$ objdump -d the-binary|grep -A 1 '$0x31,%eax'
objdump: the-binary: no symbols
 805720f:       b8 31 00 00 00          mov    $0x31,%eax
 8057214:       cd 80                   int    $0x80

Maybe it makes sense to change 0x805720f to 'mov $0,%eax' and nop out
'int $0x80'? As syscalls return their results in eax, this way, it'd
look like it returned 0 to the subsequent code... Hmm...

nobody@sandbox$ ./fenris -s -f -L support/fn-libc5.dat -P 0x8057210:0 \
                -P 0x8057214:0x90 -P 0x8057215:0x90 ./the-binary

We replaced '31', second byte of the mov instruction, and the least
significant byte of used immediate value (little endian, yup) with 0,
and both bytes of 'int' instruction with NOP (0x90).

...
19906:00 local fnct_4 (g/80675d0)
19906:00 + fnct_4 = 0x8055f08
19906:00 # No matches for signature D8F7AA72 (36275).
19915:02   local fnct_10 (l/bffffc1e "./the-binary", 0, 16)
19915:02   + fnct_10 = 0x8057764
19915:02   \ new buffer candidate: bffffc1e:17
19915:02   # Matches for signature 4E05FA21: memset
19926:02   local fnct_11 (17, 1)
19926:02   # No matches for signature 8AE66F9A (36275).

Note that fenris, even for unknown functions, tries to display parameters
in most useful way. This means, if a parameter looks like a pointer to
a readable string, it is displayed for you. It also greps our fingerprint
database and tells you what the function probably is, even thou there is
no symbols in this binary.

Voila, we got past the execve(). Now, the code is apparently messing with
its own name. Well, indeed, it will be changed to "[mingetty]" to be
less suspicious. As you can see, Fenris can tell you what functions belong to
libc, despite of static linking. Isn't that nice? Of course, don't trust
this feature blindly, treat it as a nice hint. Write down any comments you
have about every function fenris detected. Fenris automatically assigns
unique names to each function, like fnct_4. You can also use ragnarok to
do some basic analysis for you and generate function call summaries.
On some occassions, Fenris will have problems determining the exact numbers of 
parameters passed to a function because this binary was compiled with heavy 
optimizations - so your notes and ragnarok would be very helpful to get
more accurate results.

Also, be advised that it is useful to add -A option if you have reasons
to believe the binary was highly optimized. Optimized code does not show
any particular pattern that can differentiate functions with return value
from ones without return value. Passing -A option will cause Fenris to
assume all functions return something. In many cases, you have to
disregard the possible result, but you'll get more information that is
otherwise missing when fenris reports as functions as "void".

...
19926:03    local fnct_12 (17, l/bfffb5e8, l/bfffb5d8)
19926:03    + l/bfffb5e8 (maxsize 28) = stack of fcnt_11 (0 down)
19926:03    + l/bfffb5d8 (maxsize 44) = stack of fcnt_11 (0 down)
19926:03    # No matches for signature 885E11CD (36275).
19926:04     <80574d4> cndt: conditional block +25 executed
19926:04     <80574da> cndt: conditional block +12 executed
19926:04     <80574fd> cndt: if-above block (signed) +17 skipped
19926:03    ...return from function = <void>

...and here we have some local functions, yes. Trace it with IPs, and
get a IP range this function is in, then use objdump or gdb to dive into
details. You can also use -R option of Fenris to get more information
about all ways this function behaves in the program. 

Soon, the problems begin:

19926:02   local fnct_13 ()
19926:02   # Matches for signature BCF79788: fork libc_fork vfork
19926:03    fork () = 19932
>> OS error       : Operation not permitted [1]
>> Error condition: PTRACE_ATTACH failed
>> This condition occoured while tracing pid 19926 (eip 80571f0).
>> Traced 359 user CPU cycles (0 libcalls, 17 fncalls, 7 syscalls).

So, is there some sort of anti-debugging code? Like ptrace() immediately 
after fork, so we can't attach to it? Well, let's get rid of it.
First, find the offensive location (syscall 0x2 == fork):

nobody@sandbox$ objdump -d the-binary | grep -A 1 'mov    $0x2,%eax' \
                | grep -B 1 'int'
objdump: the-binary: no symbols
 80571eb:       b8 02 00 00 00          mov    $0x2,%eax
 80571f0:       cd 80                   int    $0x80

Ahh, well done. So, to fool this code, we probably simply have to
convince the software it forked successfully and is already running as
a child. Then, we can see if the child tries any further tricks, and get
rid of the parent who'd exit anyway (try running the binary without -f
option to investigate this). Child process gets zero result from fork()... 
This way, the parent execution branch will be ignored, and we're back on the 
track:

nobody@sandbox$ ./fenris -s -f -L support/fn-libc5.dat -P 0x8057210:0 \
                -P 0x8057214:0x90 -P 0x8057215:0x90 -P 0x80571f0:90 \
                -P 0x80571f1:90 -P 0x80571ec:0 -p ./the-binary

...

[08048211] 20516:02   local fnct_15 (g/80675e3)
[08048211] 20516:02   + fnct_15 = 0x8057134
[08048211] 20516:02   # Matches for signature 20F1D1E3: chdir libc_chdir
[08057144] 20516:03    SYS chdir (80675e3 "/") = 0
[08057144] 20516:03    \ new buffer candidate: 80675e3:2
[08057146] 20516:03    <8057146> cndt: if-above block (signed) +16 skipped
[0805715c] 20516:02   ...return from function = <void>

Oh, good, it prepares to perform normal operations...

...

[0804824b] 20533:02   local fnct_17 (0)
[0804824b] 20533:02   + fnct_17 = 0x8057444
[0804824b] 20533:02   # Matches for signature 58B72F00: libc_time time
[08057454] 20533:03    SYS time (0x0) = 1020875229 [Wed May  8 12:27:09 2002]
[08057456] 20533:03    <8057456> cndt: if-above block (signed) +16 executed
[0805746c] 20533:02   ...return from function = <void>

... ahh. So now it thinks it is running in the background, not traced, as
root, and just settled in.

[08055b9c] 20516:03    local fnct_19 ()
[08055b9c] 20516:03    # No matches for signature 60DCBA5A (36275).
[08055e42] 20516:04     <8055e42> cndt: on-match block +36 skipped
[08055e93] 20516:04     <8055e93> cndt: if-below block (signed) +19 executed
[08055eba] 20516:04     <8055eba> cndt: if-below block (signed) +10 executed
[08055ecb] 20516:03    ...return from function = <void>
[08055b9c] 20516:03    local fnct_19 ()
[08055b9c] 20516:03    # No matches for signature 60DCBA5A (36275).
[08055e42] 20516:04     <8055e42> cndt: on-match block +36 skipped
[08055e93] 20516:04     <8055e93> cndt: if-below block (signed) +19 executed
[08055eba] 20516:04     <8055eba> cndt: if-below block (signed) +10 executed
[08055ecb] 20516:03    ...return from function = <void>

Good excercise: this function is called in a conditional loop... and is 
something internal, not a libc code... is it used to decrypt something?
copy some data? Or maybe it is some library function linked from some
obscure library Fenris does not know? Use objdump, take your guess!

[08056d20] 20533:03    SYS socket (PF_INET, SOCK_RAW, 11 [unknown]) = -1 (Operation not permitted)

Here's a little problem. It tries to bind to a RAW socket. Why not make it
UDP, so you can work on it safely and talk to it? Another task for you.
It can get tricky, you say, there's not enough space to do it? Rest assured,
there's more than enough areas full of nops somewhere else in the memory,
feel free to put your UDP code there, and simply "call" it. Pass 
--disassemble-zeroes option to objdump and be surprised. Also, you have
lots of space on stack... yes, that's right, portions of stack are not used,
and this segment is executable. If you specify third parameter to -P
option, you'll be able to insert your modifications when the code reaches
certain point, which can be useful.

[08056b76] 20533:03    SYS socketcall_10 (0xbfffb5e0 <invalid>) = -9 (Bad file descriptor)
[08056b78] 20533:03    <8056b78> cndt: if-above block (signed) +13 executed
[08056b8f] 20533:02   ...return from function = <void>
[08056b8f] 20533:02   // function has accessed non-local memory:
[08056b8f] 20533:02   * READ buffer bfffb5fc
[08056b8f] 20533:02   + bfffb5fc = bfffb5fc:12 <off 0> (first seen in fnct_21)
[080482d9] 20533:02   <80482d9> cndt: on-match block +3033 skipped
[08048ebd] 20533:02   local fnct_22 (10000)
[08048ebd] 20533:02   + fnct_22 = 0x80555b0
[08048ebd] 20533:02   # Matches for signature 5186CEA1: usleep
[080555f0] 20533:03    local fnct_23 (1, 0, 0, 0, l/bfffb5f4)
[080555f0] 20533:03    + fnct_23 = 0x80574a0
[080555f0] 20533:03    + l/bfffb5f4 (maxsize 20) = stack of fcnt_22 (0 down)
[080555f0] 20533:03    # No matches for signature 19F45966 (36275).
[080574b0] 20533:04     SYS82 select ??? (l/bfffb5e0, 10000, 0) = 0
[080574b0] 20533:04     + l/bfffb5e0 (maxsize 4) = stack of fcnt_23 (0 down)
[080574b0] 20533:04     <80574b0> cndt: if-above block (signed) +12 skipped
[080574c4] 20533:03    ...return from function = <void>
[080555f8] 20533:02   ...return from function = <void>
[080555f8] 20533:02   \ discard: mem bfffb5fc:12 (first seen in fnct_21)

Now it enters and endless receive loop, usleep, select, some obscure
socketcall Fenris couldn't recognize... Well, I'll leave you here.
Have fun :-)
