mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 16:35:02 +01:00
1274 lines
34 KiB
Perl
Executable file
1274 lines
34 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
#****************************************************************************
|
|
#* Copyright (c) Andrew Gross 2003-2004
|
|
#* All Rights Reserved
|
|
#*
|
|
#* The following information and material is confidential and proprietary
|
|
#* information of Andrew Gross (the "Confidential Material") and
|
|
#* is protected by copyright, patent, trade secrets and other intellectual
|
|
#* and property laws. Access to this Confidential Material is limited to
|
|
#* authorized employees and/or licensees. Any unauthorized use of the
|
|
#* Confidential Material could subject the user to criminal and/or civil
|
|
#* penalties.
|
|
#*
|
|
#* This work is intended for Immunix, Inc. (the "Client") on a
|
|
#* work-for-hire basis pending contract completion and payment whereupon
|
|
#* all rights confer to the Client.
|
|
#*
|
|
#****************************************************************************
|
|
|
|
# NB: This script works only on dynamically linked programs. Static
|
|
# programs require a similar but different approach.
|
|
|
|
|
|
use Data::Dumper;
|
|
|
|
use strict;
|
|
my ( $DEBUG, # debug printing control
|
|
$addr, $byte, $data, $flag, $i, $t2, $t3, # counters and temps
|
|
$first, $first_addr, $k, $last_cv, $last_push,
|
|
$subno, $drop_push_ebp, $reloc_flag, $name,
|
|
$cf, $rod, $rw, $sstr, $str, $tmp, $x, $y,
|
|
%DATA, %FNS, %RELOC, %RODATA, %SEC, %PLT, # data store hashes
|
|
%Branches,
|
|
%obj_list, %obj_ref, @o_list, # for object file info
|
|
@line, $cv, # text seg. loop info
|
|
%Regs, %Seta, @pargs, @p2args,
|
|
$NO_MATCH_DELIVERED_DEMOS, # turn off bug fixes to match delivered data
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Comments:
|
|
#
|
|
# subroutine args: 0x8(%ebp) [argc], 0xc(%ebp) [argv]
|
|
#
|
|
# add'l instruction support / invalidate registers when necessary
|
|
#
|
|
# ACL generation and mapping:
|
|
# bit of library stuff as example
|
|
# +open +sprintf +chdir +creat +mkdir +opendir +rename
|
|
#
|
|
# rarely a routine will save extra registers at the beginning
|
|
# and this hoses the first function call if push based args.
|
|
|
|
|
|
|
|
# Debug printing setting. 0 is default, 5 is highest (most verbose)
|
|
#
|
|
#$DEBUG=5;
|
|
$DEBUG=0;
|
|
|
|
|
|
|
|
# Flag to disable improved functionality and bug fixes so that runs will
|
|
# match demos delivered to the Client.
|
|
#
|
|
#$NO_MATCH_DELIVERED_DEMOS=0;
|
|
$NO_MATCH_DELIVERED_DEMOS=1;
|
|
|
|
|
|
|
|
#=========================================================================
|
|
#
|
|
# All supporting data is read in, parsed, and stored in this portion of the code.
|
|
#
|
|
# NB: In reading data all addresses are converted from hex and stored in decimal.
|
|
|
|
|
|
|
|
#=== Get sections and address ranges
|
|
# Read output from "objdump -x":
|
|
# section start and end addresses are needed later so we can
|
|
# decide how to translate addresses
|
|
|
|
$flag=0;
|
|
|
|
print "reading header...\n";
|
|
open(F, "$ARGV[0]/header") || die $!;
|
|
while (<F>) {
|
|
$flag=1 if /^Sections:$/; # when we've passed the header info
|
|
next if /^Sections:$/;
|
|
next if !$flag;
|
|
|
|
# ignore the junk lines
|
|
next if /ALLOC|CONTENTS|LOAD|READONLY|CODE|DATA/;
|
|
next if /^Idx/;
|
|
last if /^SYMBOL TABLE:/;
|
|
|
|
# extract section start address, end address, and length:
|
|
#> 0 .interp 00000013 080480f4 080480f4 000000f4 2**0
|
|
|
|
split;
|
|
$t2 = hex ("0x".$_[2]);
|
|
$t3 = hex ("0x".$_[3]);
|
|
$SEC{$_[1]} = [ $t3, $t3+$t2, $t2 ]; # name = start, end, len
|
|
}
|
|
close(F);
|
|
|
|
# Print the loaded section information
|
|
#
|
|
if ($DEBUG>4) {
|
|
print "\n";
|
|
foreach $i (sort keys %SEC) {
|
|
printf "%-20s\t%08x\t%08x\t%08x\n", $i, @{ $SEC{$i} };
|
|
}
|
|
}
|
|
|
|
# Print the loaded section information sorted by start address (for reference)
|
|
#
|
|
if ($DEBUG>4) {
|
|
print "\n";
|
|
foreach $i (sort { ${ $SEC{$a} }[0] <=> ${ $SEC{$b} }[0] } keys %SEC) {
|
|
printf "%-20s\t%08x\t%08x\t%08x\n", $i, @{ $SEC{$i} };
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#=== Get symbol names and addresses (dynamic symbol table)
|
|
# Read output from "objdump -T":
|
|
# dynamic symbol information for xlating calls
|
|
#
|
|
print "reading dyn_syms...\n";
|
|
open(F, "$ARGV[0]/dyn_syms") || die $!;
|
|
while (<F>) {
|
|
# ignore junk lines
|
|
next if /^$/;
|
|
next if /file format elf32-i386/;
|
|
next if /^DYNAMIC SYMBOL TABLE:/;
|
|
|
|
# extract the address and dynamic symbol name (library functions typically)
|
|
#> 08049260 DF *UND* 0000003a GLIBC_2.0 mkdir
|
|
|
|
chomp;
|
|
split;
|
|
$_[0] =~ s/^0+//;
|
|
next if $_[-1] eq "_start" && !$NO_MATCH_DELIVERED_DEMOS; # This bug fix throws off sub names
|
|
$FNS{hex("0x".$_[0])} = $_[-1];
|
|
}
|
|
close(F);
|
|
|
|
# Print the loaded symbol information sorted by symbol name
|
|
#
|
|
if ($DEBUG>4) {
|
|
print "\n";
|
|
foreach $i (sort keys %FNS) {
|
|
printf "%08x\t%-20s\n", $i, $FNS{$i};
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#=== Get symbol names and addresses (linker RR info)
|
|
# Read output from "objdump -R":
|
|
# dynamic relocation information also for xlating calls
|
|
#
|
|
print "reading dynam...\n";
|
|
open(F, "$ARGV[0]/dynam") || die $!;
|
|
while (<F>) {
|
|
# ignore junk lines
|
|
next if /^$/;
|
|
next if /file format elf32-i386/;
|
|
next if /^DYNAMIC SYMBOL TABLE:/;
|
|
next if /^OFFSET/;
|
|
|
|
# extract the address and symbol name
|
|
#> 0804f260 R_386_JUMP_SLOT mkdir
|
|
|
|
chomp;
|
|
split;
|
|
$_[0] =~ s/^0+//;
|
|
$FNS{hex("0x".$_[0])} = $_[-1];
|
|
}
|
|
close(F);
|
|
|
|
# Print the loaded relocation information sorted by symbol name
|
|
#
|
|
if ($DEBUG>4) {
|
|
print "\n";
|
|
foreach $i (sort keys %FNS) {
|
|
printf "%08x\t%-20s\n", $i, $FNS{$i};
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#=== Get read only data (rodata)
|
|
# Read output from "objdump -s --section=.rodata":
|
|
# read only text data (string and other constants)
|
|
#
|
|
print "reading rodata...\n";
|
|
open(F, "$ARGV[0]/rodata") || die $!;
|
|
while (<F>) {
|
|
# ignore junk lines
|
|
next if /^$/;
|
|
next if /file format elf32-i386/;
|
|
next if /^Contents of section/;
|
|
|
|
# extract bytes and store with corresponding address key
|
|
# in the %RODATA hash
|
|
#> 804d9e0 03000000 01000200 756e6162 6c652074 ........unable t
|
|
|
|
split;
|
|
$addr=$_[0];
|
|
$addr = hex("0x".$addr);
|
|
shift @_;
|
|
$data=join(' ', @_);
|
|
$data =~ s/ //g;
|
|
while ( length($data) ) {
|
|
$byte=substr($data,0,2);
|
|
$byte = hex("0x".$byte);
|
|
$RODATA{$addr}=$byte;
|
|
$data=substr($data,2);
|
|
$addr++;
|
|
}
|
|
}
|
|
close(F);
|
|
|
|
# Print the loaded string data
|
|
#
|
|
if ($DEBUG>4) {
|
|
print "\n";
|
|
foreach $i (sort keys %RODATA) {
|
|
printf "%08x\t%02x\n", $i, $RODATA{$i};
|
|
}
|
|
}
|
|
|
|
print Data::Dumper->Dump([\%RODATA]);
|
|
|
|
|
|
|
|
#=== Get data (data)
|
|
# Read output from "objdump -s --section=.data":
|
|
# data section (for variable tracking and the odd string)
|
|
#
|
|
print "reading data...\n";
|
|
open(F, "$ARGV[0]/data") || die $!;
|
|
while (<F>) {
|
|
# ignore junk lines
|
|
next if /^$/;
|
|
next if /file format elf32-i386/;
|
|
next if /^Contents of section/;
|
|
|
|
# identical to RODATA section but stores in %DATA hash
|
|
#> 804f000 00000000 00000000 4cf20408 19000000 ........L.......
|
|
|
|
$addr=substr($_,0,8);
|
|
$addr =~ s/ //g;
|
|
$addr = hex("0x".$addr);
|
|
$data=substr($_,9,35);
|
|
$data =~ s/ //g;
|
|
while ( length($data) ) {
|
|
$byte=substr($data,0,2);
|
|
$byte = hex("0x".$byte);
|
|
$DATA{$addr}=$byte;
|
|
$data=substr($data,2);
|
|
$addr++;
|
|
}
|
|
}
|
|
close(F);
|
|
|
|
# Print the loaded data
|
|
#
|
|
if ($DEBUG>4) {
|
|
print "\n";
|
|
foreach $i (sort keys %DATA) {
|
|
printf "%08x\t%02x\n", $i, $DATA{$i};
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#=== Get reloc symbol names and addresses
|
|
# Read output from "objdump -r":
|
|
# read in relocation information which contains
|
|
# hints as to in which section a datum is stored
|
|
#
|
|
# Note: this is only for handling object files
|
|
# as executables will not have data in this
|
|
# section of the file.
|
|
#
|
|
print "reading reloc...\n";
|
|
open(F, "$ARGV[0]/reloc") || die $!;
|
|
while (<F>) {
|
|
# ignore junk lines
|
|
next if /^$/;
|
|
next if /file format elf32-i386/;
|
|
next if /^RELOCATION RECORDS/;
|
|
next if /^OFFSET/;
|
|
|
|
chomp;
|
|
split;
|
|
|
|
# skip BSS symbols
|
|
next if $_[2] eq ".bss";
|
|
|
|
#
|
|
#> 00000098 R_386_32 .rodata
|
|
|
|
# if a symbol (32 bit relocation), store with other symbols
|
|
# else store with relocation symbols
|
|
#
|
|
if ( $_[1] eq "R_386_PC32" ) {
|
|
$FNS{hex("0x".$_[0])} = $_[-1];
|
|
} else {
|
|
$RELOC{hex("0x".$_[0])} = $_[-1];
|
|
}
|
|
}
|
|
close(F);
|
|
|
|
# Print the loaded relocation symbols
|
|
#
|
|
if ($DEBUG>4) {
|
|
print "\n";
|
|
foreach $i (sort {$a <=> $b} keys %RELOC) {
|
|
printf "%08x\t%-20s\n", $i, $RELOC{$i};
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# Note: data from the "objdump -t" is not currently used. This information
|
|
# would only exist in an unstripped binary and is only of help
|
|
# to a human reading the code. It only contains symbols internal
|
|
# to the program and not of interest for library calls.
|
|
# We can crib this from the text directly.
|
|
|
|
|
|
|
|
#=========================================================================
|
|
#
|
|
# This portion of the code reads in the disassembly of the text segment.
|
|
# The processing is done in two phases which correspond to the
|
|
# two loops (the first split into two pieces) each of which makes
|
|
# a complete pass of the text segment. The text segment data is
|
|
# not stored in core due possible large size.
|
|
#
|
|
# The first pass pulls the PLT data as the newer compiler uses a
|
|
# different relocation scheme; then the subroutines are found and named,
|
|
# main() is located, and branch target addresses are found.
|
|
#
|
|
# The second pass does all of the bookkeeping required to generate
|
|
# coherent subroutine call representations.
|
|
|
|
|
|
|
|
# First pass of text segment, part 1:
|
|
# Locate .plt section and pull jump information
|
|
# which is required to xlate external library
|
|
# calls under the newer compiler.
|
|
|
|
$flag=0;
|
|
|
|
print "reading text...\n";
|
|
open(F, "$ARGV[0]/text") || die $!;
|
|
while ($i=<F>) {
|
|
|
|
# Skip lines until we find "<.plt>:", then
|
|
# reset state (exit loop in this case) when
|
|
# we finish that section (blank line).
|
|
# Except for the last and section names
|
|
# this is the same as for the other loops.
|
|
#
|
|
if ( $i =~ /^$/ || $i =~ /^Disassembly/ ) {
|
|
last if $flag;
|
|
$flag=0;
|
|
next;
|
|
} elsif ( $i =~ /<.plt>:/ ) {
|
|
$flag=1;
|
|
next;
|
|
} elsif ( !$flag ) {
|
|
next;
|
|
}
|
|
|
|
# We are looking for lines like this:
|
|
#> 804a58c: ff 25 74 51 0e 08 jmp *0x80e5174
|
|
#
|
|
# Library calls will be to this address and the destination
|
|
# address will have to be dereferenced via the %FNS hash
|
|
|
|
# removed unneeded trailer, split, and dump null 1st element
|
|
# set $cv to current line address
|
|
#
|
|
chomp($i);
|
|
$i =~ s/ <[\w+]+>$//;
|
|
@line=split(/\s+/, $i);
|
|
shift @line;
|
|
$line[0] =~ s/:$//;
|
|
$cv = hex("0x".$line[0]);
|
|
|
|
# $cv == current value == address of current line
|
|
#
|
|
$line[0] =~ s/:$//;
|
|
$cv = hex("0x".$line[0]);
|
|
|
|
# if we have a jmp *addr line, enter the info into %PLT
|
|
#
|
|
if ( $line[-2] eq "jmp" && $line[-1] =~ /^\*/ ) {
|
|
$line[-1] =~ s/^\*//;
|
|
$PLT{$cv} = hex($line[-1]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# First pass of text segment, part 2:
|
|
# In which subroutines are found and named,
|
|
# and branch targets are identified.
|
|
|
|
$first_addr=0; # first address of non-system code
|
|
$first=1; # flag to make sure first subroutine is named
|
|
$last_cv=-1; # address of the previous line (for object files)
|
|
$subno="00"; # subroutine name counter
|
|
|
|
$flag=0;
|
|
|
|
while ($i=<F>) {
|
|
|
|
# Skip lines until we reach the beginning of the
|
|
# actual "text" (either .text or an internal
|
|
# subroutine name.
|
|
#
|
|
if ( $i =~ /^$/ || $i =~ /^Disassembly/ ) {
|
|
$flag=0;
|
|
next;
|
|
} elsif ( $i =~ /<.text>:/ || $i =~ /<(\w+)>:/ ) {
|
|
$name = $1;
|
|
$flag=1;
|
|
next;
|
|
} elsif ( !$flag ) {
|
|
next;
|
|
}
|
|
|
|
|
|
print ">>> ",$i,"\n" if $DEBUG>3;
|
|
|
|
|
|
# Clean up line as above and set $cv to current address
|
|
#
|
|
chomp($i);
|
|
$i =~ s/ <[\w+]+>$//;
|
|
@line=split(/\s+/, $i);
|
|
shift @line;
|
|
$line[0] =~ s/:$//;
|
|
$cv = hex("0x".$line[0]);
|
|
|
|
|
|
# set $first_addr only once
|
|
#
|
|
$first_addr=$cv if !$first_addr;
|
|
|
|
|
|
# For object files we need to know if there is a relocation
|
|
# entry that refers to the previous line of assembly. If so,
|
|
# note that for later xlation in loop #2.
|
|
#
|
|
foreach $k (sort {$a <=> $b} keys %RELOC) {
|
|
next if $k < $last_cv;
|
|
last if $k > $cv;
|
|
|
|
$obj_list{$last_cv}++;
|
|
$obj_ref {$last_cv}=$RELOC{$k};
|
|
}
|
|
|
|
|
|
# Main logic of pass 1. Find start of subroutines, identify
|
|
# address of main(), and branch target addresses.
|
|
#
|
|
if ( $first ) {
|
|
|
|
# Special case for first line of code so that it is
|
|
# always flagged as a subroutine. Clear first flag
|
|
# and enter address with subroutine name.
|
|
#
|
|
$first=0;
|
|
printf "START %08x\n", $cv if $DEBUG>4;
|
|
if ( !length($FNS{$cv}) ) {
|
|
$FNS{$cv} = "sub_".$subno;
|
|
$subno++;
|
|
}
|
|
} elsif ( $line[-2] eq "push" && $line[-1] eq "%ebp" ) {
|
|
printf "STOP %08x\n", $cv if $DEBUG>3;
|
|
printf "START %08x\n", $cv if $DEBUG>3;
|
|
|
|
# This is a subroutine start [cf ref. #1] so name it
|
|
# if it doesn't already have a name.
|
|
#
|
|
if ( !length($FNS{$cv}) ) {
|
|
if ( length($name) && $NO_MATCH_DELIVERED_DEMOS ) { # This throws off sub names
|
|
$FNS{$cv} = $name;
|
|
} else {
|
|
$FNS{$cv} = "sub_".$subno;
|
|
$subno++;
|
|
}
|
|
}
|
|
} elsif ( $line[-2] eq "push" ) {
|
|
|
|
# Save last value pushed onto the stack as for
|
|
# the __libc_start_main call that value will be
|
|
# the address of main() [cf ref. #2]
|
|
#
|
|
$last_push = $line[-1];
|
|
$last_push =~ s/^\$//;
|
|
$last_push = hex($last_push);
|
|
} elsif ( $line[-2] eq "call" ) {
|
|
$addr = $line[-1];
|
|
if ( $addr !~ /%/ ) {
|
|
$addr = hex($addr);
|
|
|
|
# Check to see if the destination address of the call is in
|
|
# the text segment. If so, make sure it's not a fake
|
|
# localization call. [cf ref. #3]
|
|
#
|
|
if ( $addr >= ${ $SEC{".text"} }[0] && $addr < ${ $SEC{".text"} }[1] ) {
|
|
if ( $line[1] eq "e8" && $line[2] eq "00" && $line[3] eq "00"
|
|
&& $line[4] eq "00" && $line[5] eq "00" ) {
|
|
$FNS{$addr} = "fake_localization_call";
|
|
print $i,"\n" if $DEBUG>3;
|
|
printf "CALL= %08x\n", $addr if $DEBUG>3;
|
|
} else {
|
|
printf "CALL %08x\n", $addr if $DEBUG>3;
|
|
|
|
# Should probably keep a list of calls into the
|
|
# text segment as a double check for problems
|
|
# with the disassembly.
|
|
|
|
}
|
|
} else {
|
|
printf "CALL* %08x\n", $addr if $DEBUG>3;
|
|
}
|
|
|
|
# Starting program setup call. Last pushed address
|
|
# is main(). [cf ref. #2]
|
|
#
|
|
if ( $FNS{$addr} eq "__libc_start_main" ||
|
|
$NO_MATCH_DELIVERED_DEMOS && $FNS{$PLT{$addr}} eq "__libc_start_main" ) { # Fixes PLT lookup issue
|
|
$FNS{$last_push} = "main";
|
|
printf "main = %08x\n", $last_push if $DEBUG>3;
|
|
}
|
|
} else {
|
|
|
|
# If the call target is *%e__, then it's a register indirect call
|
|
# and we won't have information about the destination in most cases.
|
|
#
|
|
print "CALL- ", $addr, "\n" if $DEBUG>3;
|
|
}
|
|
} elsif ( $line[-2] =~ /^j/ ) {
|
|
|
|
# All branches start with "j" so this is a change of control and
|
|
# we note all destinations so that we can invalidate the registers
|
|
# at that point. More intensive branch analysis and register
|
|
# bookkeeping can avoid having to do this in all cases but
|
|
# beware loops/backward branches which cause problems.
|
|
#
|
|
$line[-1] =~ s/^0x//;
|
|
$Branches{hex("0x".$line[-1])}++;
|
|
print ">>> $line[-1]\n" if $DEBUG>3;
|
|
} else {
|
|
print ">>> Unused\n" if $DEBUG>4;
|
|
}
|
|
|
|
|
|
# reset last address value
|
|
#
|
|
$last_cv=$cv;
|
|
}
|
|
close(F);
|
|
|
|
|
|
|
|
# Print separator tag into the output file to show we've completed pass 1.
|
|
#
|
|
print "====\n";
|
|
|
|
|
|
|
|
# Second pass of text segment:
|
|
# Keep track of subroutines, registers, and branches as required to
|
|
# generate coherent subroutine calls with arguments. And generate
|
|
# output.
|
|
|
|
|
|
|
|
$drop_push_ebp=0; # flag to ignore subroutine start push
|
|
$reloc_flag=0; # relocation info present flag
|
|
@o_list = sort {$a <=> $b} keys %obj_list; # sorted list of object file info
|
|
|
|
$flag=0;
|
|
|
|
open(F, "$ARGV[0]/text") || die $!;
|
|
while ($i=<F>) {
|
|
|
|
# Same intro as previous loop.
|
|
#
|
|
if ( $i =~ /^$/ || $i =~ /^Disassembly/ ) {
|
|
$flag=0;
|
|
next;
|
|
} elsif ( $i =~ /<.text>:/ || $i =~ /<\w+>:/ ) {
|
|
$flag=1;
|
|
next;
|
|
} elsif ( !$flag ) {
|
|
next;
|
|
}
|
|
|
|
|
|
# Third verse, same as the first...
|
|
#
|
|
chomp($i);
|
|
$i =~ s/ <[\w+]+>$//;
|
|
@line=split(/\s+/, $i);
|
|
shift @line;
|
|
$line[0] =~ s/:$//;
|
|
$cv = hex("0x".$line[0]);
|
|
|
|
|
|
# If this is a real subroutine, we need to ignore the push %ebp
|
|
# as it isn't involved in a subroutine call.
|
|
#
|
|
if ( length($FNS{$cv}) && $FNS{$cv} ne "fake_localization_call" ) {
|
|
print "\n",$FNS{$cv},":\n";
|
|
$drop_push_ebp=1;
|
|
|
|
if ( $NO_MATCH_DELIVERED_DEMOS ) { # Fixes registers not invalidated over end of subroutine
|
|
undef @pargs;
|
|
undef @p2args;
|
|
undef %Regs;
|
|
}
|
|
}
|
|
|
|
|
|
# If this address is the target of a branch, reset registers to
|
|
# prevent incorrect answers and note that we have done so.
|
|
#
|
|
if ( $Branches{$cv} ) {
|
|
undef @pargs;
|
|
undef @p2args;
|
|
undef %Regs;
|
|
print "Branch-target\n";
|
|
}
|
|
|
|
|
|
# Check for object file relocation entry that applies to
|
|
# this line and set flag if so.
|
|
#
|
|
if ( $#o_list > -1 ) {
|
|
if ( $cv == $o_list[0] ) {
|
|
$reloc_flag=1;
|
|
shift @o_list;
|
|
print "*** <<$obj_ref{$cv}>> " if $DEBUG>5;
|
|
} elsif ( $cv > $o_list[0] ) {
|
|
print "ERROR: o_list mishandled $cv $o_list[0]\n";
|
|
}
|
|
}
|
|
|
|
|
|
# This case statement is the heart of the matter. This handles
|
|
# each assembly instruction and maintains state, generating
|
|
# whatever output is apropos. Each line that is recognized
|
|
# and processed has a '+' prepended when it is output.
|
|
#
|
|
# The heart of the heart is the if's that deal with call,
|
|
# mov, and push as these instructions are the ones that
|
|
# control function calls and argument setups. [cf refs. #4,#5]
|
|
# For push, @pargs contains the arguments. For mov,
|
|
# @p2args contains the arguments. There are a few cases
|
|
# where additional registers are pushed onto the stack
|
|
# and this can interfere with arguments to function calls
|
|
# but a little more bookkeeping will make those rare cases
|
|
# go away.
|
|
#
|
|
# There is a rare third case of the argument setup which
|
|
# seems to be only in optimized code. In this case
|
|
# the arguments passed in are left on the stack and
|
|
# implicitly referred to by function calls in the
|
|
# subroutine.
|
|
#
|
|
# All code that references $reloc_flag is intended for
|
|
# dealing with object files as this was out of scope
|
|
# the support is enough to help see what is going on
|
|
# but not sufficient for reliable use -- there
|
|
# are a number of different segments that would have
|
|
# to be supported for a more solid implementation.
|
|
|
|
if ( $#line == 1 ) {
|
|
|
|
# Single element -- nothing to do. Usually a long instruction length.
|
|
#
|
|
print "+",$i,"\n";
|
|
} elsif ( $line[-2] eq "push" ) {
|
|
|
|
# Clear rodata flag and set up for relocation entry if one exists.
|
|
#
|
|
$rod=0;
|
|
if ( $reloc_flag ) {
|
|
$line[-1] =~ s/^\$//;
|
|
$tmp=$line[-1];
|
|
$line[-1] = "<<" . $obj_ref{$cv} . "+" . $tmp . ">>";
|
|
}
|
|
|
|
# If the argument of the push is an address, check to see if there is a
|
|
# corresponding rodata string or if it is the address of a function
|
|
# (such as for signal(3)). Otherwise just print out the line
|
|
# with relocation info.
|
|
#
|
|
if ( $line[-1] =~ /^\$/ ) {
|
|
$line[-1] =~ s/^\$//;
|
|
$a = hex($line[-1]);
|
|
$str="";
|
|
if ( $a >= ${ $SEC{".rodata"} }[0] && $a < ${ $SEC{".rodata"} }[1] && ${ $SEC{".rodata"} }[0] ) {
|
|
$str = &get_rodata_str($a);
|
|
$rod=1;
|
|
} elsif ( $a >= ${ $SEC{".text"} }[0] && $a < ${ $SEC{".text"} }[1] && ${ $SEC{".text"} }[0] ) {
|
|
$str = length($FNS{$a}) ? $FNS{$a} : "unknown_text_addr";
|
|
} else {
|
|
; # Otherwise no xlation.
|
|
}
|
|
print "+",$i, length($str) ? "\t".$str : "" ,"\n";
|
|
} else {
|
|
print "+",$i, $reloc_flag ? " reloc $line[-1]\n" : "\n";
|
|
}
|
|
|
|
# Transfer argument to stack. If from an initial push %ebp, ignore it.
|
|
# Otherwise put rodata strings on preferentially or just the register/value.
|
|
#
|
|
if ( $line[-1] eq "%ebp" && $drop_push_ebp ) {
|
|
|
|
# Subroutine start, ignore
|
|
} else {
|
|
unshift(@pargs, $rod ? $str : $line[-1]);
|
|
}
|
|
} elsif ( $line[-2] eq "call" ) {
|
|
|
|
# Set the called address. Note the type of call and resolve the
|
|
# called address if possible.
|
|
#
|
|
if ( $line[-1] =~ /\*/ ) {
|
|
$addr = -1;
|
|
print "+",$i,"\tindirect_call\n";
|
|
} else {
|
|
$addr = hex($line[-1]);
|
|
print "+",$i,"\t",$FNS{$addr},"\n" if !$NO_MATCH_DELIVERED_DEMOS; # Old code
|
|
if ( !length($FNS{$addr}) ) {
|
|
if ( length($FNS{$PLT{$addr}}) ) { # Check for new style lib calls
|
|
$addr=$PLT{$addr};
|
|
} else {
|
|
print "!!! ERR: no sub name defined\n";
|
|
}
|
|
}
|
|
print "+",$i,"\t",length($FNS{$addr}) ? $FNS{$addr} : "UNKNOWN","\n" if $NO_MATCH_DELIVERED_DEMOS; # PLT support
|
|
}
|
|
|
|
# If this is a real call (not a fake localization call), then handle the args.
|
|
#
|
|
if ( $i =~ /\*/ || $FNS{$addr} ne "fake_localization_call" ) {
|
|
|
|
# Print the name of the function call or INDIR if indirect.
|
|
#
|
|
if ( -1 == $addr ) {
|
|
$sstr="INDIR";
|
|
print "INDIR";
|
|
} else {
|
|
$sstr=$FNS{$addr};
|
|
print $FNS{$addr};
|
|
}
|
|
|
|
|
|
# Decide which calling mode is in use. Favor mov's onto
|
|
# the stack. Print the arguments as we have them to go with
|
|
# the function name just printed. Then construct $sstr
|
|
# for use in loading registers with function return
|
|
# values.
|
|
#
|
|
if ( $#pargs > -1 && $#p2args == -1 ) {
|
|
$x=$,; $,=' '; print "(", @pargs, ")\n"; $,=$x;
|
|
$sstr .= "(-" . join(", ", @pargs) . ")";
|
|
} else {
|
|
|
|
# Rarely %eax is used as an implicit argument --
|
|
# seems to depend on optimizer and compiler version.
|
|
#
|
|
if ( !length($p2args[0]) ) {
|
|
if ( length($Regs{"eax"}) ) {
|
|
$p2args[0] = "%eax:" . $Regs{"%eax"};
|
|
} else {
|
|
$p2args[0] = "";
|
|
}
|
|
}
|
|
|
|
# Mark any empty spaces in the argument list --
|
|
# will flag any missed/improperly processed
|
|
# instructions.
|
|
#
|
|
for ($i=0; $i <= $#p2args; $i++) {
|
|
$p2args[$i] = "<<undef>>" if !length($p2args[$i]);
|
|
}
|
|
|
|
$x=$,; $,=' '; print "(", @p2args, ")\n"; $,=$x;
|
|
@pargs = @p2args;
|
|
$sstr .= "(+" . join(", ", @pargs) . ")";
|
|
}
|
|
|
|
# At this point we are prepared to handle a function call.
|
|
# $FNS{$addr} contains the function name and @pargs contains
|
|
# the arguments. $sstr contains the function call and arguments
|
|
# for use in registers. What remains is knowing what to do for
|
|
# each function name and how to massage the arguments to
|
|
# get what we want.
|
|
#
|
|
# I'm not clear on how all of these map into ACLs for subdomain
|
|
# so I've just decoded what I could and left it at that.
|
|
|
|
if ( $FNS{$addr} eq "chdir" ) {
|
|
|
|
# Print a warning since this can't be tracked w/o global
|
|
# context.
|
|
#
|
|
print "EMIT: WARN: chdir global $pargs[0]\n";
|
|
} elsif ( $FNS{$addr} eq "open" ) {
|
|
|
|
# If filename points to register, see if that register
|
|
# contains anything.
|
|
#
|
|
if ( $pargs[0] =~ /^%e..$/ && length($Regs{$pargs[0]}) ) {
|
|
$pargs[0] = $Regs{$pargs[0]};
|
|
}
|
|
|
|
# Decode open mode flags if constant, $rw gets
|
|
# read/write modes; $cf gets '+' for append.
|
|
#
|
|
$rw = "?"; $cf="";
|
|
if ( $pargs[1] =~ /^0x/ ) {
|
|
$tmp = hex($pargs[1]);
|
|
if ( ($tmp & 3) == 0 ) {
|
|
$rw="r";
|
|
} elsif ( ($tmp & 3) == 1 ) {
|
|
$rw="w";
|
|
} elsif ( ($tmp & 3) == 2 ) {
|
|
$rw="rw";
|
|
}
|
|
$cf="+" if ($tmp & 0x200);
|
|
}
|
|
print "EMIT: open $pargs[0] $rw $cf\n";
|
|
} elsif ( $FNS{$addr} eq "fopen" ) {
|
|
|
|
# fopen flags, while text, will need massaging to
|
|
# put into the proper ACL format.
|
|
#
|
|
print "EMIT: fopen $pargs[0] $pargs[1]\n";
|
|
} elsif ( $FNS{$addr} eq "opendir" ) {
|
|
|
|
# Read on foo
|
|
#
|
|
print "EMIT: opendir $pargs[0]\n";
|
|
} elsif ( $FNS{$addr} eq "creat" ) {
|
|
|
|
# Write on foo
|
|
#
|
|
print "EMIT: creat $pargs[0] w\n";
|
|
} elsif ( $FNS{$addr} eq "mkdir" ) {
|
|
|
|
# ??? ACLs?
|
|
#
|
|
print "EMIT: mkdir $pargs[0]\n";
|
|
} elsif ( $FNS{$addr} eq "unlink" ) {
|
|
|
|
# ??? Write?
|
|
#
|
|
print "EMIT: unlink $pargs[0]\n";
|
|
} elsif ( $FNS{$addr} eq "rename" ) {
|
|
|
|
# Delete on old, write on new?
|
|
#
|
|
print "EMIT: rename [" . $sstr . "]\n";
|
|
} elsif ( $FNS{$addr} eq "sprintf" ) {
|
|
|
|
# In this case put results into corresponding register in case
|
|
# it is used later on.
|
|
#
|
|
print "EMIT: sprintf [$sstr]\n";
|
|
# put in reg for later use
|
|
$Regs{$pargs[0]} = "[" . $sstr . "]";
|
|
} elsif ( $FNS{$addr} eq "snprintf" ) {
|
|
|
|
# In this case put results into corresponding register in case
|
|
# it is used later on.
|
|
#
|
|
print "EMIT: snprintf [$sstr]\n";
|
|
# put in reg for later use
|
|
$Regs{$pargs[0]} = "[" . $sstr . "]";
|
|
} elsif ( $FNS{$addr} eq "getenv" ) {
|
|
|
|
# In this case put results into corresponding register in case
|
|
# it is used later on.
|
|
#
|
|
$Regs{"%eax"} = "[" . $sstr . "]";
|
|
} elsif ( $FNS{$addr} eq "localtime" ) {
|
|
|
|
# Example 1 of a fixed library call. Whenever localtime(3) is
|
|
# called, an open on /etc/localtime will result.
|
|
#
|
|
print "EMIT: localtime \"/etc/localtime\" r\n";
|
|
} elsif ( $FNS{$addr} eq "openlog" ) {
|
|
|
|
# Example 2 of a fixed library call. Whenever openlog(3) is
|
|
# called, an open on /dev/log will result. Similar things
|
|
# happen for nameservice calls, *pwent, *grent, etc.
|
|
#
|
|
print "EMIT: openlog \"/dev/log\" r\n";
|
|
} elsif ( $FNS{$addr} =~ /^exec(l|lp|le|v|vp)$/ ) {
|
|
|
|
# we want to know about all the exec*() variants
|
|
|
|
print "EMIT: $FNS{$addr} [$sstr]\n";
|
|
} elsif ( $FNS{$addr} eq "system" ) {
|
|
|
|
# system is important too
|
|
|
|
print "EMIT: system [$sstr]\n";
|
|
}
|
|
}
|
|
|
|
# Reset arguments since we just passed a call.
|
|
#
|
|
undef @pargs;
|
|
undef @p2args;
|
|
} elsif ( $line[-2] eq "mov" && ( $line[-1] eq "%esp,%ebp" || $line[-1] eq "%ebp,%esp" ) ) {
|
|
|
|
# Begin / end of subroutine mov's
|
|
#
|
|
shift @pargs if !$NO_MATCH_DELIVERED_DEMOS; # Bug in previous. This is handled elsewhere
|
|
print "+",$i,"\n";
|
|
} elsif ( $line[-2] =~ /^mov/ && $line[-1] =~ /^(.*),(%e..)$/ ) {
|
|
|
|
# Save source, destination register from operands.
|
|
#
|
|
$str="";
|
|
$x=$1;
|
|
$y=$2;
|
|
|
|
# Check for $x being a local variable that we are tracking.
|
|
# If so, use that value to set the destination register.
|
|
# Then check for funky indirection modes and clear them out.
|
|
# Otherwise, check for relocation information and possible
|
|
# rodata string else use the values given.
|
|
# We set the "set time" of the register for use elsewhere
|
|
# for register indirection (cf #1#).
|
|
#
|
|
if ( $x =~ /^0xf[0-9a-f]+\(%ebp\)$/ && length($Regs{$x})) {
|
|
$Regs{$y} = $Regs{$x};
|
|
} elsif ( $x =~ /\(.*\),/ ) { # funky indirection modes
|
|
$Regs{$y} = $x;
|
|
} else {
|
|
if ( $reloc_flag ) {
|
|
$x =~ s/^$//;
|
|
if ( $obj_ref{$cv} eq ".rodata" ) {
|
|
$str = &get_rodata_str(hex($x));
|
|
$Regs{$y} = $str;
|
|
$str = "\t".$str;
|
|
} else {
|
|
$tmp = "<<" . $obj_ref{$cv} . "+" . $x . ">>";
|
|
$Regs{$y} = $tmp;
|
|
}
|
|
} else {
|
|
if ( $x =~ /^\$/ ) {
|
|
$x =~ s/^\$//;
|
|
$a = hex($x);
|
|
my $str="";
|
|
if ( $a >= ${ $SEC{".rodata"} }[0] && $a < ${ $SEC{".rodata"} }[1] && ${ $SEC{".rodata"} }[0] ) {
|
|
$str = &get_rodata_str($a);
|
|
$Regs{$y} = $str;
|
|
} else {
|
|
$Regs{$y} = $x;
|
|
}
|
|
} else {
|
|
$Regs{$y} = $x;
|
|
}
|
|
}
|
|
}
|
|
$Seta{$y} = $cv;
|
|
|
|
# Handle relocation information if any. If we were able to resolve
|
|
# a string, print it. Otherwise, note reloc info.
|
|
# Just mark the line as handled otherwise.
|
|
#
|
|
if ( $reloc_flag ) {
|
|
if ( length($str) ) {
|
|
print "+",$i,$str,"\n";
|
|
} else {
|
|
print "+",$i," reloc <<$obj_ref{$cv}>>\n";
|
|
}
|
|
} else {
|
|
print "+",$i,"\n";
|
|
}
|
|
print "== $y = $Regs{$y}\n";
|
|
} elsif ( $line[-2] =~ /^mov/ && $line[-1] =~ /\(%esp(,1)?\)/ ) {
|
|
|
|
# Extract offset and operand from a statement like:
|
|
# mov %eax,0x8(%esp,1)
|
|
# $x gets the 1st operand, $y gets the offset into the argument list.
|
|
#
|
|
$line[-1] =~ /^(.*),(0x.*)?\(%esp(,1)?\)$/;
|
|
$x=$1; $y=hex($2)/4;
|
|
|
|
if ( $x =~ /^\$/ ) {
|
|
|
|
# If the operand is a value, check for rodata string.
|
|
# Otherwise just put the literal value in the argument list.
|
|
#
|
|
$x =~ s/^\$//;
|
|
$a = hex($x);
|
|
if ( $a>= ${ $SEC{".rodata"} }[0] && $a< ${ $SEC{".rodata"} }[1] ) {
|
|
$str = &get_rodata_str($a);
|
|
print "+",$i,"\t",$str,"\n";
|
|
print "[",$y,"]=",$str,"\n";
|
|
$p2args[$y] = $str;
|
|
} else {
|
|
print "+",$i,"\n";
|
|
print "[",$y,"]=",$x,"\n";
|
|
$p2args[$y] = $x;
|
|
}
|
|
} elsif ( $x =~ /^%/ ) {
|
|
|
|
# If a register was set to the value of another register
|
|
# *and* that register has not been modified since, then
|
|
# substitute the second register's contents for the first.
|
|
# Otherwise just put the register name. #1#
|
|
#
|
|
print "+",$i,"\n";
|
|
if ( length($Regs{$x}) && $Seta{$x} > $Seta{$Regs{$x}} ) {
|
|
print "[",$y,"]=",$x," ==> ", $Regs{$x},"\n";
|
|
$p2args[$y] = $Regs{$x};
|
|
} else {
|
|
print "[",$y,"]=",$x,"\n";
|
|
$p2args[$y] = $x;
|
|
}
|
|
} else {
|
|
|
|
# Wasn't a format we deal with, so mark the line as unprocessed.
|
|
#
|
|
print "X",$i,"\n";
|
|
}
|
|
} elsif ( $line[-2] eq "pushl" ) {
|
|
|
|
# Push long (64-bit) quantity.
|
|
#
|
|
if ( $reloc_flag ) {
|
|
$line[-1] =~ s/^\$//;
|
|
$tmp=$line[-1];
|
|
$line[-1] = "<<" . $obj_ref{$cv} . "+" . $tmp . ">>";
|
|
print "+",$i," reloc $line[-1]\n";
|
|
} else {
|
|
print "+",$i,"\n";
|
|
}
|
|
unshift(@pargs, "L:".$line[-1]);
|
|
} elsif ( $line[-2] eq "add" || $line[-2] eq "sub" || $line[-2] eq "and" || $line[-2] eq "ror" ) {
|
|
|
|
# Not implementing these opcodes. Invalidate affected register.
|
|
#
|
|
$line[-1] =~ /^.*,(%e..)$/;
|
|
$Regs{$1} = undef;
|
|
print "+",$i, $reloc_flag ? " reloc <<$obj_ref{$cv}>>\n" : "\n";
|
|
} elsif ( $line[-2] =~ /^mov[lwb]?$/ ) {
|
|
|
|
# We only care about (%esp,1), (%esp), and register versions of mov opcodes
|
|
# and those are handled above.
|
|
#
|
|
print "+",$i, $reloc_flag ? " reloc <<$obj_ref{$cv}>>\n" : "\n";
|
|
} elsif ( $line[-2] =~ /^j/ ) {
|
|
|
|
# Branch/change of control. Reset regs and stack.
|
|
#
|
|
print "+",$i,"\n";
|
|
undef @pargs;
|
|
undef @p2args;
|
|
undef %Regs;
|
|
} elsif ( $line[-2] =~ /^test[bwl]?$/ || $line[-2] =~ /^cmp[bwl]?$/ ) {
|
|
|
|
# Just ignore. Nothing to do with comparison opcodes.
|
|
# Print relocation info if present.
|
|
#
|
|
print "+",$i, $reloc_flag ? " reloc <<$obj_ref{$cv}>>\n" : "\n";
|
|
} elsif ( $line[-3] eq "repz" || $line[-3] eq "repnz" || $line[-2] eq "setne" ) {
|
|
|
|
# Moving loop opcodes. Probably should invalidate related registers
|
|
# and not depend on the compiler to reinitialize them.
|
|
#
|
|
print "+",$i,"\n";
|
|
} elsif ( $line[-2] eq "pop" && $line[-1] eq "%ebp" ) {
|
|
|
|
# Just ignore. End of subroutine cruft.
|
|
#
|
|
print "+",$i,"\n";
|
|
} elsif ( $line[-2] eq "lea" && ( $line[-1] =~ /,(%e..)$/ || $line[-1] =~ /[sd]i.*[sd]i/ ) ) {
|
|
|
|
# Just ignore these as they are filler between subroutines.
|
|
#
|
|
$Regs{$1} = undef if length($1);
|
|
print "+",$i,"\n";
|
|
} elsif ( $line[-2] eq "xor" && $line[-1] =~ /^(%e..),\1$/ ) {
|
|
|
|
# xor-ing a register with itself is a shorthand for setting it to zero.
|
|
#
|
|
$Regs{$1} = 0;
|
|
print "+",$i,"\n";
|
|
} elsif ( $line[-2] eq "inc" || $line[-2] eq "dec" || $line[-2] eq "not" || $line[-2] eq "neg" ) {
|
|
|
|
# Not implementing these opcodes; just invalidate register contents.
|
|
#
|
|
$Regs{$line[-1]} = undef;
|
|
print "+",$i,"\n";
|
|
} elsif ( $line[-1] eq "hlt" ||
|
|
$line[-1] eq "nop" ||
|
|
$line[-1] eq "leave" ||
|
|
$line[-1] eq "ret" ) {
|
|
|
|
# These are all control flow related opcodes; all ignored.
|
|
#
|
|
print "+",$i,"\n";
|
|
} elsif ( $line[-2] eq "pop" ) {
|
|
|
|
# Currently not used to manipulate the stack.
|
|
# Stack is dumped after each "call".
|
|
#
|
|
print "+",$i,"\n";
|
|
} else {
|
|
|
|
# This line is not recognized nor processed.
|
|
#
|
|
print $i,"\n";
|
|
}
|
|
|
|
|
|
# Reset relocation info flag for next pass
|
|
#
|
|
$reloc_flag=0;
|
|
}
|
|
close(F);
|
|
|
|
|
|
|
|
# All done
|
|
#
|
|
exit 0;
|
|
|
|
|
|
|
|
#=========================================================================
|
|
#
|
|
# Subroutines:
|
|
|
|
|
|
|
|
# get_rodata_str:
|
|
# Takes a decimal address and returns the best representation
|
|
# of data at that address in the %RODATA hash as a string.
|
|
# String is returned in quotes and with most metacharacters
|
|
# replaced with '?'
|
|
#
|
|
sub get_rodata_str () {
|
|
my($addr) = @_;
|
|
my($str);
|
|
|
|
$str = "\"";
|
|
|
|
while ( $RODATA{$addr} ) {
|
|
if ( $RODATA{$addr} > 31 && $RODATA{$addr} < 127 ) {
|
|
$str .= sprintf "%c", $RODATA{$addr};
|
|
} elsif ( 10 == $RODATA{$addr} ) {
|
|
$str .= "\\n";
|
|
} elsif ( 9 == $RODATA{$addr} ) {
|
|
$str .= "\\t";
|
|
} else {
|
|
$str .= "?";
|
|
}
|
|
$addr++;
|
|
}
|
|
|
|
$str .= "\"";
|
|
|
|
return $str;
|
|
}
|
|
|
|
|
|
|
|
|
|
#=========================================================================
|
|
#
|
|
# References:
|
|
#
|
|
# Ref. #1:
|
|
# User subroutines always start begin with "push %ebp". If this
|
|
# changes, then a number of modifications of this script will be
|
|
# required. For example:
|
|
#
|
|
# 8049864: 55 push %ebp
|
|
#
|
|
#
|
|
#
|
|
# Ref. #2:
|
|
#
|
|
# This is the typical start() routine. Note the last item
|
|
# pushed on the stack is the address of main().
|
|
#
|
|
# 8049840: 31 ed xor %ebp,%ebp
|
|
# 8049842: 5e pop %esi
|
|
# 8049843: 89 e1 mov %esp,%ecx
|
|
# 8049845: 83 e4 f0 and $0xfffffff0,%esp
|
|
# 8049848: 50 push %eax
|
|
# 8049849: 54 push %esp
|
|
# 804984a: 52 push %edx
|
|
# 804984b: 68 50 d9 04 08 push $0x804d950 sub_75
|
|
# 8049850: 68 20 d9 04 08 push $0x804d920 sub_74
|
|
# 8049855: 51 push %ecx
|
|
# 8049856: 56 push %esi
|
|
# 8049857: 68 10 b2 04 08 push $0x804b210 main
|
|
# 804985c: e8 4f fd ff ff call 0x80495b0 __libc_start_main
|
|
# __libc_start_main( 0x804b210 %esi %ecx 0x804d920 0x804d950 %edx %esp %eax )
|
|
# 8049861: f4 hlt
|
|
# 8049862: 90 nop
|
|
# 8049863: 90 nop
|
|
#
|
|
#
|
|
#
|
|
# Ref. #3:
|
|
# A fake call used for getting the current execution address (PC).
|
|
# It is a call to relative address 0 (next instruction) and then
|
|
# the return address is popped off the stack for manipulation.
|
|
# I've only seen this in system code. For example:
|
|
#
|
|
# 8049869: e8 00 00 00 00 call 0x804986e fake_localization_call
|
|
# 804986e: 5b pop %ebx
|
|
# 804986f: 81 c3 e6 59 00 00 add $0x59e6,%ebx
|
|
# 8049875: 8b 83 84 01 00 00 mov 0x184(%ebx),%eax
|
|
#
|
|
#
|
|
#
|
|
# Ref. #4:
|
|
# Calling sequence based on mov. In this case operands are put in the stack
|
|
# by moving them to 0xXX(%esp,1) where 0xXX is 4 time the parameter number,
|
|
# e.g., 0x0 is first, 0x4 is second, etc. Variants include just (%esp).
|
|
#
|
|
# 8049bb6: c7 44 24 04 bd dd 04 movl $0x804ddbd,0x4(%esp,1) "%s"
|
|
# 8049bbd: 08
|
|
# 8049bbe: 8b 45 08 mov 0x8(%ebp),%eax
|
|
# 8049bc1: c7 04 24 03 00 00 00 movl $0x3,(%esp,1)
|
|
# 8049bc8: 89 44 24 08 mov %eax,0x8(%esp,1)
|
|
# 8049bcc: e8 bf f8 ff ff call 0x8049490 syslog
|
|
# syslog( 0x3 "%s" 0x8(%ebp) )
|
|
#
|
|
#
|
|
#
|
|
# Ref. #5:
|
|
# Calling sequence based on push. In this case operands are put on the stack in
|
|
# reverse order for the call. For example:
|
|
#
|
|
# 804b286: 68 aa 53 0c 08 push $0x80c53aa "none"
|
|
# 804b28b: a1 60 b4 21 08 mov 0x821b460,%eax
|
|
# 804b290: 50 push %eax
|
|
# 804b291: e8 d6 fb ff ff call 804ae6c strcasecmp
|
|
# strcasecmp( %eax "none" )
|