从C程序加载原始代码

11

我正在编写一个从文件加载和执行代码的程序。 但是我遇到了一个问题:"write"系统调用不起作用。 程序成功加载并执行,但在屏幕上没有显示任何文本。

加载代码的程序:

#include < stdio.h >
#include < stdlib.h >

int main(int argc,char* argv[])
{
    unsigned int f_size = 0;
    unsigned char* code_buf = NULL;
    void (*func_call)(void) = NULL;

    if(argc < 2) 
    {
        printf("Usage: %s <FILE>\n",argv[0]);
        return 1;
    }

    FILE* fp = fopen(argv[1],"rb");
    if(!fp)
    {
        printf("Error while opening this file: %s\n",argv[1]);
        return 1;
    }

    unsigned int fsize = 0;
    fseek(fp,0,SEEK_END);
    fsize = ftell(fp);
    fseek(fp,0,SEEK_SET);
    if(fsize < 4)
    {
        printf("Code size must be > 4 bytes\n");
        return 1;
    }

    code_buf = (unsigned char*) malloc(sizeof(unsigned char)*fsize);
    if(fread(code_buf,fsize,1,fp)<1)
    {
        printf("Error while reading file: %s\n",argv[1]);
        free(code_buf);
        return 1;
    }
    func_call = (void (*)(void)) code_buf;

    printf("[EXEC] Binary is loaded\n"
           "\tFirst 2 bytes: 0x%x 0x%x\n"
           "\tLast 2 bytes: 0x%x 0x%x\n",
           code_buf[0],code_buf[1],
           code_buf[fsize-2],code_buf[fsize-1]);
    printf("[EXEC] Starting code...\n");
    (*func_call)();
    printf("[EXEC] Code executed!\n");

    free(code_buf);

    return 0;
}

我试图通过这个程序(test.s)执行的代码:

.text
    movl    $4, %eax
    movl    $1, %ebx
    movl    $str, %ecx
    movl    $5, %edx
    int     $0x80
    jmp end
    str:
        .string "test\n"
    end:
    ret

这是我的编译方式:

 gcc -c test.s
 objcopy -O binary test.o test.bin

问题已解决,感谢 @Christoph 的帮助。

以下是可行的代码:

.text
    call start
    str:
        .string "test\n"
    start:
    movl    $4, %eax
    movl    $1, %ebx
    pop     %ecx
    movl    $5, %edx
    int     $0x80
    ret

1
当你说没有文本显示时,是指它没有打印出“test”,还是连“[EXEC]”行也没有打印出来? - AShelly
2
+1 有趣的问题,但我希望这不会被用于任何恶意行为。 - BlueRaja - Danny Pflughoeft
在调用 objcopy 时添加 -S 参数有帮助吗? - Christoph
“-S” 不会有所帮助 - 代码存在缺陷... - Christoph
当您说没有显示文本时,是指它没有打印出“test”,还是连“[EXEC]”行也没有打印出来?在“[EXEC]”之间必须有“test”。 - Alexey
4个回答

9
你的方法行不通:shellcode必须是位置无关的,但你的代码引用了绝对地址 str。无条件跳转也可以是相对或绝对的:确保你获取相对版本(在x86上操作码为EB和E9)。
更多信息请参见编写可移植Shell Code技术

1
是的,当您通常链接和加载代码(例如,使用dlopen)时,加载程序要做的事情之一是修复代码中的地址引用,以匹配它们实际加载到内存中的位置。手动加载和执行代码时,您将不得不手动执行这些修复操作,或者避免使用绝对地址。 - bdk
@Alexey:你可以使用call将指令指针推入栈中,以此方法获取你的 shellcode 的地址(请参见链接文档中的清单11.1)。 - Christoph
链接失效了,先生 :( - Febriyanto Nugroho

3
你没有具体说明你的CPU详情,但你可能会遇到NX bit的问题。我预计你的代码会SEGFAULT而不是运行完成。
这正是在我的电脑上发生的情况(Linux 2.6.32-28-generic #55-Ubuntu SMP Mon Jan 10 23:42:43 UTC 2011 x86_64 GNU/Linux),它运行在Intel Xeon E5410上。

1
我在内核中禁用了NX位。代码总是成功执行的,我运行了大约30次(我还在gdb中检查了它)。 - Alexey

2

有一件事:你应该以二进制方式打开文件。

FILE* fp = fopen(argv[1],"rb"); 

3
在POSIX系统上没有关系:“字符'b'没有影响,但允许符合ISO C标准。” - Christoph

1

我想学习如何手动加载代码,只是为了好玩(也许将来用于Shellcode测试)。 - Alexey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接