1

要在此处获取操作码作者,请执行以下操作:

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

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

  • <_开始>

  • <启动器>

  • <安德>

我试图以相同的方式获取十六进制操作码,但不能ld正确。当然,我可以生成.o和 prog 文件,例如:

gcc main.o -o prog -g

然而当

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

要查看带有注释和符号的完整代码,我还有许多其他部分,例如:

  • 。在里面

  • .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”,就像三个部分一样?

4

2 回答 2

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 下也是可能的)。不支持延迟链接。

于 2013-08-27T05:51:45.650 回答
1

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

于 2013-08-27T02:39:37.000 回答