所以我在一个带有可执行文件的Linux机器上。模糊-我知道。当我尝试使用参数/参数运行它时会发生以下情况:

me@there:~$ ./theFile theOption -a
./theFile : option requires an argument -- 'a'
usage: theOption [parameters]


我的同事说just use objdump to reverse theFile first part of the main function

我的问题是可以找到可以传递给程序的参数。我有一些函数的名称,例如do_thisdo_that,但请确保如何将它们传递给可执行文件。

评论

查看此链接,可能会有所帮助:tldp.org/LDP/LG/issue84/hawk.html

#1 楼

可以确定可以将哪些命令行参数或选项传递给Linux可执行文件。当然,如何实现将取决于程序的类型和设计以及诸如混淆,加密,压缩等因素。

硬编码文档

Linux设计为易于人类使用且其行为会根据收到的命令行参数而变化的可执行文件(例如lscatgrep等)通常是ELF二进制文件,其用法文档在.rodata部分中进行了硬编码。可以使用readelf -x .rodata ELF_BINARY_NAME检查此数据。

示例:

  $ readelf -x .rodata /bin/ls | less
                               .
                         <lots of data>
                               .
  0x00413e90 20202d61 2c202d2d 616c6c20 20202020   -a, --all     
  0x00413ea0 20202020 20202020 20202020 20646f20              do 
  0x00413eb0 6e6f7420 69676e6f 72652065 6e747269 not ignore entri
  0x00413ec0 65732073 74617274 696e6720 77697468 es starting with
  0x00413ed0 202e0a20 202d412c 202d2d61 6c6d6f73  ..  -A, --almos
  0x00413ee0 742d616c 6c202020 20202020 20202020 t-all           
  0x00413ef0 646f206e 6f74206c 69737420 696d706c do not list impl
  0x00413f00 69656420 2e20616e 64202e2e 0a202020 ied . and ...   
                               .
                        <even more data>


这意味着即使您的程序上没有man页面,系统,没有README文件,也没有源代码,您仍然可以查看内部如何手动对其进行记录。

这特别与问题中提供的示例有关:


这是我尝试使用参数/参数运行它时发生的情况:


me@there:~$ ./theFile theOption -a
./theFile : option requires an argument -- 'a'
usage: theOption [parameters]


本示例中发生的事情是命令行选项传递给可执行文件,然后可执行文件将一些有关正确用法的字符串输出到STDOUT。在推论有效的命令行参数的上下文中,这意味着在可执行文件中存在硬编码的字符串,这些字符串记录了可以使用静态分析找到的正确用法。

没有硬编码的文档

第1部分:静态分析

在设计接受命令行参数的Linux用户空间应用程序时,我们可以利用程序设计方面的考虑因素,以识别分析二进制文件时程序期望的参数。例如,在执行程序时,存在哪些机制来解析传递给程序的n个命令行参数?如何设计程序,使其能够成功确定哪些参数有效和哪些参数无效?如果传递了1000个参数怎么办?处理命令行参数的程序的设计必须反映这样的输入可以是任意的这一事实。

Linux环境中用来解决此问题的常见设计模式是结合使用GNU C库函数循环内的getopt()switch构造。 getopt()解析命令行参数,然后解析的参数由switch构造求值。在分析二进制文件时,可以将其用作试探法。

为了说明这一点,我们可以分析一个处理命令行参数的“神秘” ELF二进制文件。

$ file mystery_program 
mystery_program: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=3286c11349d82c58257a14e4d174b9ec5f313714, stripped


该程序是动态链接的,这意味着即使剥离了该程序,它仍包含有关在运行时链接和加载哪些库以及程序使用这些库中的哪些功能的信息。由于getopt()glibc(共享库或.so*)函数,因此,如果我们的动态链接二进制文件使用它,则.dynsym部分中的条目将包含有关它的信息。 readelf --dyn-syms mystery_program输出中的条目21确认程序中确实使用了getopt()

 $ readelf --dyn-syms mystery_program 

Symbol table '.dynsym' contains 79 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __uflow@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.2.5 (2)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strncmp@GLIBC_2.2.5 (2)
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _exit@GLIBC_2.2.5 (2)
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcpy@GLIBC_2.2.5 (2)
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __fpending@GLIBC_2.2.5 (2)
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND iconv@GLIBC_2.2.5 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND iswcntrl@GLIBC_2.2.5 (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND write@GLIBC_2.2.5 (2)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND textdomain@GLIBC_2.2.5 (2)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fclose@GLIBC_2.2.5 (2)
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND bindtextdomain@GLIBC_2.2.5 (2)
    16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND stpcpy@GLIBC_2.2.5 (2)
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dcgettext@GLIBC_2.2.5 (2)
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_get_mb_cur_max@GLIBC_2.2.5 (2)
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.2.5 (2)
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@GLIBC_2.4 (3)
==> 21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getopt_long@GLIBC_2.2.5 (2)
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mbrtowc@GLIBC_2.2.5 (2)
    23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strchr@GLIBC_2.2.5 (2)
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strrchr@GLIBC_2.2.5 (2)
<output snipped>


该程序中使用的getopt()版本是getopt_long()。要查看在什么上下文中调用getopt_long(),我们可以使用objdump

$ objdump -dj .text mystery_program | grep getopt -B10 -A25
  401aa6:   c6 84 24 80 00 00 00    movb   
  .
  .
  401add:   83 f8 62                cmp    
 $ objdump -dj .text mystery_program | grep getopt
   401acf:  e8 3c fc ff ff          callq  401710 <getopt_long@plt>
x62,%eax # 0x62 is '>' . . 401ae8: 83 f8 73 cmp
#include <unistd.h>

int getopt(int argc, char * const argv[],
           const char *optstring);

extern char *optarg;
extern int optind, opterr, optopt;

#include <getopt.h>

int getopt_long(int argc, char * const argv[],
                const char *optstring,
                const struct option *longopts, int *longindex);

int getopt_long_only(int argc, char * const argv[],
                     const char *optstring,
                     const struct option *longopts, int *longindex);
x73,%eax # 0x73 is 'I' . . 401af8: 83 f8 75 cmp
$ gdb -q mystery_program
Reading symbols from mystery_program...(no debugging symbols found)...done.
(gdb) break *0x401acf
Breakpoint 1 at 0x401acf
(gdb) run
Starting program: mystery_program 

Breakpoint 1, 0x0000000000401acf in ?? ()
(gdb) x/i $rip
=> 0x401acf:    callq  0x401710 <getopt_long@plt>
(gdb)
x75,%eax # 0x75 is 'K' . . 401b04: 83 f8 76 cmp
(gdb) info registers 
rax            0x0  0
rbx            0x7fffffffe138   140737488347448
rcx            0x409000 4231168
rdx            0x408f23 4230947
rsi            0x7fffffffe138   140737488347448
rdi            0x1  1
rbp            0x1000   0x1000
rsp            0x7fffffffdee0   0x7fffffffdee0
r8             0x0  0
r9             0x2  2
r10            0x7fffffffdca0   140737488346272
r11            0x7ffff7a51410   140737348178960
r12            0x402602 4204034
r13            0x7fffffffe130   140737488347440
r14            0x0  0
r15            0x0  0
rip            0x401acf 0x401acf
eflags         0x246    [ PF ZF IF ]
x76,%eax # 0x76 is 'L' . . 401b14: 83 f8 41 cmp
(gdb) x/s $rdx
0x408f23:   "benstuvAET"
x41,%eax # 0x41 is ')' . . 401b34: 83 f8 45 cmp
(gdb) x/s $rcx
0x409000:   "57@"        # oops, that is not a string
(gdb) x/xg $rcx
0x409000:   0x0000000000408f95
(gdb) x/s 0x0000000000408f95
0x408f95:   "number-nonblank"
(gdb)
x45,%eax # 0x45 is '-' . . 401b39: 83 f8 54 cmp
(gdb) x/s 0x0000000000408f95+64
0x408fd5:   "show-tabs"
(gdb) 
x54,%eax # 0x54 is '6' . . <more comparisons>
x0,0x80(%rsp) 401aad: 00 401aae: c6 84 24 84 00 00 00 movb q4312078qx0,0x84(%rsp) 401ab5: 00 401ab6: c6 44 24 57 00 movb q4312078qx0,0x57(%rsp) 401abb: 8b 7c 24 58 mov 0x58(%rsp),%edi 401abf: 45 31 c0 xor %r8d,%r8d 401ac2: b9 00 90 40 00 mov q4312078qx409000,%ecx 401ac7: ba 23 8f 40 00 mov q4312078qx408f23,%edx 401acc: 48 89 de mov %rbx,%rsi 401acf: e8 3c fc ff ff callq 401710 <getopt_long@plt> <=== 401ad4: 83 f8 ff cmp q4312078qxffffffff,%eax 401ad7: 0f 84 10 01 00 00 je 401bed <__sprintf_chk@plt+0x1bd> 401add: 83 f8 62 cmp q4312078qx62,%eax 401ae0: 0f 84 f3 00 00 00 je 401bd9 <__sprintf_chk@plt+0x1a9> 401ae6: 7e 2c jle 401b14 <__sprintf_chk@plt+0xe4> 401ae8: 83 f8 73 cmp q4312078qx73,%eax 401aeb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 401af0: 0f 84 d6 00 00 00 je 401bcc <__sprintf_chk@plt+0x19c> 401af6: 7e 6a jle 401b62 <__sprintf_chk@plt+0x132> 401af8: 83 f8 75 cmp q4312078qx75,%eax 401afb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 401b00: 74 b9 je 401abb <__sprintf_chk@plt+0x8b> 401b02: 7c 72 jl 401b76 <__sprintf_chk@plt+0x146> 401b04: 83 f8 76 cmp q4312078qx76,%eax 401b07: 0f 85 d6 00 00 00 jne 401be3 <__sprintf_chk@plt+0x1b3> 401b0d: c6 44 24 53 01 movb q4312078qx1,0x53(%rsp) 401b12: eb a7 jmp 401abb <__sprintf_chk@plt+0x8b> 401b14: 83 f8 41 cmp q4312078qx41,%eax 401b17: 74 34 je 401b4d <__sprintf_chk@plt+0x11d> 401b19: 7f 19 jg 401b34 <__sprintf_chk@plt+0x104> 401b1b: 3d 7d ff ff ff cmp q4312078qxffffff7d,%eax 401b20: 74 64 je 401b86 <__sprintf_chk@plt+0x156> 401b22: 3d 7e ff ff ff cmp q4312078qxffffff7e,%eax 401b27: 0f 85 b6 00 00 00 jne 401be3 <__sprintf_chk@plt+0x1b3> 401b2d: 31 ff xor %edi,%ed


在地址getopt_long()处对401acf的调用之后,将进行跳转表的拆卸,这表示存在switch序列。跳转表要长得多,但是这里显示的内容足以理解这个想法。如果我们更仔细地查看跳转表,我们会发现将类似于ASCII代码的十六进制值与%eax进行了比较,按照惯例,%eax保留了getopt_long()的返回值:

/>该程序将解析命令行参数,并且其执行路径将基于其具有特定条件的任何ASCII字符进行分支。虽然这不会立即产生哪些参数有效和哪些参数无效,但这是一个开始。到目前为止,使用Linux binutils进行静态分析(以及我有限的知识和技能)只能帮助我们。

第2部分:动态分析

有两个因素在动态分析此“神秘”二进制文件以确定有效的命令行参数是什么。

第一个是即使剥离了该二进制文件,getopt_long()也位于共享库中,并且为了动态链接到二进制文件,必须保留有关getopt_long()的某些符号信息。这就是为什么当使用objdump静态分析来自该剥离二进制文件getopt_long()的一些反汇编代码时,仍会使用其名称的原因。这意味着我们在调用getopt_long()的二进制文件中具有确切的地址,这使我们可以在该地址处设置断点,而不必在二进制文件中反复花几个小时。换句话说,我们不需要知道在getopt_long()中调用了哪个函数,无论是在main()中还是在其他函数中。我们可以让程序执行,直到到达该地址的断点为止。快速提醒一下,在地址401acf处调用了getopt_long()

q4312078q

第二个是由所有有效命令行选项组成的字符串在被调用时传递给getopt(),正如我们在其函数原型中所看到的那样。从其man页面的提要中:

q4312078q

在这三个原型中,getopt_long()的原型是此特定情况下的一个有趣的原型。根据原型,向getopt_long()传递了一个指向选项字符串的指针以及一个指向持有长选项的结构的指针。这意味着可以通过在调用程序getopt_long()的点上研究这些指针来发现单字符选项和选项字符串。 gdb可用于执行分析:如此处所示,不必知道main()在哪里,甚至不需要知道程序入口点是什么。

这是调用getopt_long()的地方。是时候研究寄存器了。

q4312078q

%rcx和%rdx看起来很有趣。

q4312078q

选项字符串,由程序使用的所有字符选项组成。选项是-b,-e,-n,-s等。

长选项呢?

q4312078q

所以一个长选项是“ --number-nonblank”。

q4312078q

另一个是“ --show-tabs”。

依此类推。

最后的想法

如果您已经到了这一步并且还没有死于无聊,那就恭喜您。事实证明,这个“神秘”程序实际上是cat,一个著名的Linux实用程序。在这种特殊情况下,可能会发现程序期望使用哪些命令行选项。

重要的是要注意,在本示例分析的整个过程中,从未检查传递给mystery_program / cat的参数/选项。当用户将选项x,y和z传递给某个程序然后对其进行分析时,先进入main()然后查看*argv[]中的字符串是没有用的,因为它们已经众所周知:argv[0]指向程序名称,argv[1]指向到程序名称,x等之后的第一个参数。 />调查main()是为了恢复在执行程序时传递给程序的参数,而不是确定程序实际使用的选项。如示例所示,该信息位于程序的其他位置。

#2 楼

由于我不太了解您的可执行文件,因此我首先会在您的程序中运行字符串以查看是否出现任何问题:如果二进制文件中有描述,则可以完成此工作。
(我现在固定在Windows上,但是字符串似乎在Linux上的工作方式完全相同)。

{ ~ }  » strings.exe /bin/ls.exe  
...
Usage: %s [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
Mandatory arguments to long options are mandatory for short options too.
  -a, --all                  do not ignore entries starting with .
  -A, --almost-all           do not list implied . and ..
      --author               with -l, print the author of each file
  -b, --escape               print C-style escapes for nongraphic characters
      --block-size=SIZE      scale sizes by SIZE before printing them; e.g.,
                               '--block-size=M' prints sizes in units of
                               1,048,576 bytes; see SIZE format below
  -B, --ignore-backups       do not list implied entries ending with ~
...


如果失败了,难道不能只使用一些随机值运行它并运行ltrace(如果要仔细看,则运行strace)并猜测可能的参数是什么?