如何获得c程序的最小可执行操作码?

时间:2013-08-27 02:32:53

标签: c gcc assembly elf objdump

获取作者here的操作码如下:

[bodo@bakawali testbed8]$ as testshell2.s -o testshell2.o
[bodo@bakawali testbed8]$ ld testshell2.o -o testshell2
[bodo@bakawali testbed8]$ objdump -d testshell2

然后他得到三个部分(或仅提到这三个部分):

  • < _start>

  • <起动机>

  • <安德>

我试图以相同的方式获取十六进制操作码但不能正确ld。当然我可以用例如:

生成.o和编译文件
gcc main.o -o prog -g

然而

objdump --prefix-addresses --show-raw-insn -Srl prog

查看带注释和符号的完整代码,I have many additional sections there,例如:

  • 的.init

  • .PLT

  • .text(是的,我知道,主要在这里)[这里很多部分:_start(),call_gmon_start(),__ do_global_dtors_aux(),frame_dummy(),main(),__ libc_csu_init(),__ libc_csu_fini() ,__ do_global_ctors_aux()]

  • 调用.fini

我认为这些是gcc链接到运行时库引入的附加内容。我想我不需要这些所有部分从c代码调用操作码(作者只使用那3个部分)但是我的问题是我不知道我可能丢弃哪个,哪些是必要的。我想这样使用它:

#include <unistd.h>

char code[] = "\x31\xed\x49\x89\x...x00\x00";

int main(int argc, char **argv)
{
/*creating a function pointer*/
int (*func)();
func = (int (*)()) code;
(int)(*func)();

return 0;
} 

所以我创造了这个:

#include <unistd.h>
/*
 * 
 */
int main() {

    char *shell[2];

    shell[0] = "/bin/sh";
    shell[1] = NULL;
    execve(shell[0], shell, NULL);

    return 0;
}

我正如我所描述的那样进行了反汇编。我尝试使用.text main()中的操作码,这给了我分段错误,然后是.text main()+另外.text _start(),结果相同。

那么,从上面的部分中选择什么,或者如何仅生成与三个部分一样最小化的“prog”?

2 个答案:

答案 0 :(得分:2)

  

字符代码[] =“\ x31 \ xed \ x49 \ x89 \ x ... x00 \ x00”;

这不起作用。

原因:代码肯定包含地址。主要是函数execve()的地址和字符串常量“/ bin / sh”的地址。

使用“code []”方法的可执行文件根本不包含字符串常量“/ bin / sh”,并且函数execve()的地址将不同(如果函数将链接到可执行文件中)全部)。

因此,对“execve()”函数的“call”指令将使用“code []”方法跳转到可执行文件中的任何位置。

关于可执行文件的一些理论 - 仅供参考:

可执行文件有两种可能性:

  • 静态链接:这些可执行文件包含所有必需的代码。因此,他们不会访问像“libc.so”这样的动态库
  • 动态链接:这些可执行文件不包含经常使用的代码。此类代码存储在所有可执行文件共有的文件中:动态库(例如“libc.so”)

当使用相同的C代码时,静态链接的可执行文件比动态链接的可执行文件大得多因为所有C函数(例如“printf”,“execve”,...)必须捆绑到可执行文件。

当不使用任何这些库函数时,静态链接的可执行文件更简单,因此更容易理解。

静态链接的可执行行为

操作系统将静态链接的可执行文件加载到内存中(当使用execve()启动时)。可执行文件包含入口点地址。该地址存储在可执行文件的文件头中。你可以使用“objdump -h ...”看到它。

操作系统跳转到该地址,以便程序从该地址开始执行。地址通常是函数“_start”,但是当使用“ld”链接时,可以使用命令行选项更改。

“_start”处的代码将准备可执行文件(例如初始化变量,计算“argc”和“argv”的值,...)并调用“main()”函数。当“main()”返回时,“_ start”函数会将“main()”返回的值传递给“_exit()”函数。

动态链接的可执行行为

此类可执行文件包含两个附加部分。第一部分包含动态链接器的文件名(可能是“/lib/ld-linux.so.1”)。然后,操作系统将加载可执行文件和动态链接器,并跳转到动态链接器的入口点(而不是可执行文件的入口点)。

动态链接器将读取第二个附加部分:它包含有关可执行文件所需的动态库(例如“libc.so”)的信息。它将加载所有这些库并初始化许多变量。然后它调用所有库和可执行文件的初始化函数(“_init()”)。

请注意,操作系统和动态链接器都会忽略函数和节名称!入口点的地址取自文件头,“_ init()”函数的地址取自附加部分 - 函数的名称可能不同!

完成所有这些操作后,动态链接器将跳转到可执行文件的入口点(“_start”)。

关于“GOT”,“PLT”,......部分:

这些部分包含有关链接器加载动态库的地址的信息。 “PLT”部分包含包含跳转到动态库的包装代码。这意味着:“PLT”部分将包含一个函数“printf()”,它实际上什么都不做,只是跳转到“libc.so”中的“printf()”函数。这样做是因为直接从C代码调用动态库中的函数会使链接变得更加困难,因此C代码不会直接调用动态库中的函数。这种实现的另一个优点是“延迟链接”是可能的。

关于Windows的一些话

Windows只知道动态链接的可执行文件。 Windows XP甚至拒绝加载不需要DLL的可执行文件。 “动态链接器”已集成到操作系统中,而不是单独的文件。还有一个相当于“PLT”的部分。然而,许多编译器支持“直接”从C代码调用DLL代码而不首先调用PLT部分中的代码(理论上这在Linux下也是可能的)。不支持延迟链接。

答案 1 :(得分:1)

您应该阅读这篇文章:http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html。 它解释了创建非常小的程序所需的所有内容。