Linux Shellcode“你好,世界!" [英] Linux Shellcode "Hello, World!"

查看:20
本文介绍了Linux Shellcode“你好,世界!"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下有效的 NASM 代码:

全局 _start节.text_开始:移动 eax, 0x4移动 ebx, 0x1mov ecx, 消息移动 edx, 0xF整数 0x80移动 eax, 0x1移动 ebx, 0x0整数 0x80.data 节消息:db "Hello, World!", 0dh, 0ah

将Hello, World! "打印到屏幕上.我还有以下 C 包装器,其中包含以前的 NASM 对象代码:

字符代码[] ="xb8x04x00x00x00""xbbx01x00x00x00""xb9x00x00x00x00""xbax0fx00x00x00""xcdx80xb8x01x00""x00x00xbbx00x00""x00x00xcdx80";int main(void){(*(void(*)())code)();}

然而,当我运行代码时,似乎没有执行汇编代码,但程序退出正常.有什么想法吗?

谢谢

解决方案

当你注入这个 shellcode 时,你不知道 message 是什么:

mov ecx, 消息

在注入的过程中,它可以是任何东西,但不会是 "Hello world! " 因为它在数据部分,而您只转储文本部分.你可以看到你的shellcode没有"Hello world! ":

xb8x04x00x00x00"xbbx01x00x00x00"xb9x00x00x00x00"xbax0fx00x00x00"xcdx80xb8x01x00"x00x00xbbx00x00"x00x00xcdx80";

这是shellcode开发中的常见问题,解决方法如下:

全局 _start节.text_开始:jmp 消息;1) 让我们跳到 MESSAGE回去:移动 eax, 0x4移动 ebx, 0x1流行 ecx ;3)我们正在进入`ecx`,现在我们有了;Hello, World!
"的地址移动 edx, 0xF整数 0x80移动 eax, 0x1移动 ebx, 0x0整数 0x80信息:调用GOBACK ;2)我们要回去了,因为我们使用了`call`,这意味着;返回地址,在这种情况下是地址;Hello, World!
",被推入堆栈.db "Hello, World!", 0dh, 0ah.data 节

现在转储文本部分:

$ nasm -f elf shellcode.asm$ ld shellcode.o -o shellcode$ ./shellcode你好世界!$ objdump -d shellcodeshellcode:文件格式 elf32-i386.text 节的反汇编:08048060 <_开始>:8048060: e9 1e 00 00 00 jmp 8048083 <MESSAGE>08048065 <GOBACK>:8048065: b8 04 00 00 00 mov $0x4,%eax804806a:bb 01 00 00 00 mov $0x1,%ebx804806f:59 流行%ecx8048070:ba 0f 00 00 00 mov $0xf,%edx8048075:cd 80 int $0x808048077: b8 01 00 00 00 mov $0x1,%eax804807c:bb 00 00 00 00 mov $0x0,%ebx8048081:cd 80 int $0x8008048083 <留言>:8048083:e8 dd ff ff ff 呼叫 8048065 <GOBACK>8048088:48 dec %eax <-+8048089:65 克 |804808a: 6c insb (%dx),%es:(%edi) |804808b: 6c insb (%dx),%es:(%edi) |804808c: 6f outsl %ds:(%esi),(%dx) |804808d: 2c 20 sub $0x20,%al |804808f:57 推%edi |8048090: 6f outsl %ds:(%esi),(%dx) |8048091: 72 6c jb 80480ff <MESSAGE+0x7c>|8048093:64 秒 |8048094: 21 .byte 0x21 |8048095: 0d .byte 0xd |8048096: 0a .byte 0xa <-+$

我标记的行是我们的"Hello, World! " 字符串:

$ printf "x48x65x6cx6cx6fx2cx20x57x6fx72x6cx64x21x0dx0a"你好世界!$

所以我们的 C 包装器将是:

字符代码[] =xe9x1ex00x00x00"//jmp(相对)xb8x04x00x00x00"//移动 $0x4,%eaxxbbx01x00x00x00"//移动 $0x1,%ebxx59"//弹出 %ecxxbax0fx00x00x00"//移动 $0xf,%edxxcdx80"//整数 $0x80xb8x01x00x00x00"//移动 $0x1,%eaxxbbx00x00x00x00"//移动 $0x0,%ebxxcdx80"//整数 $0x80xe8xddxffxffxff"//调用(相对)你好世界!
";//或 x48x65x6cx6cx6fx2cx20x57";//"x6fx72x6cx64x21x0dx0a";int main(int argc, char **argv){(*(void(*)())code)();返回0;}

让我们测试一下,使用 -z execstack 启用 read-implies-exec (process-宽,尽管名称中有堆栈"),所以我们可以在 .data.rodata 部分执行代码:

$ gcc -m32 test.c -z execstack -o test$ ./测试你好世界!

它有效.(-m32 在 64 位系统上也是必需的.int $0x80 32 位 ABI 不适用于像 这样的 64 位地址.Rodata 在 PIE 可执行文件中.另外,机器代码是为 32 位汇编的.碰巧的是,相同的字节序列会在 64 位模式下解码为等效的指令,但情况并非总是如此.)>

现代 GNU ld.rodata 放在与 .text 不同的段中,因此它可以是不可执行的.过去使用 const char code[] 将可执行代码放入一页只读数据中就足够了.至少对于不想修改自己的shellcode.

I have the following working NASM code:

global _start

section .text

_start:
    mov eax, 0x4
    mov ebx, 0x1
    mov ecx, message
    mov edx, 0xF
    int 0x80

    mov eax, 0x1
    mov ebx, 0x0
    int 0x80

section .data
    message: db "Hello, World!", 0dh, 0ah

which prints "Hello, World! " to the screen. I also have the following C wrapper which contains the previous NASM object code:

char code[] =
"xb8x04x00x00x00"
"xbbx01x00x00x00"
"xb9x00x00x00x00"
"xbax0fx00x00x00"
"xcdx80xb8x01x00"
"x00x00xbbx00x00"
"x00x00xcdx80";

int main(void)
{
    (*(void(*)())code)();
}

However when I run the code, it seems like the assembler code isn't executed, but the program exits fine. Any ideas?

Thanks

解决方案

When you inject this shellcode, you don't know what is at message:

mov ecx, message

in the injected process, it can be anything but it will not be "Hello world! " since it is in the data section while you are dumping only the text section. You can see that your shellcode doesn't have "Hello world! ":

"xb8x04x00x00x00"
"xbbx01x00x00x00"
"xb9x00x00x00x00"
"xbax0fx00x00x00"
"xcdx80xb8x01x00"
"x00x00xbbx00x00"
"x00x00xcdx80";

This is common problem in shellcode development, the way to work around it is this way:

global _start

section .text

_start:
    jmp MESSAGE      ; 1) lets jump to MESSAGE

GOBACK:
    mov eax, 0x4
    mov ebx, 0x1
    pop ecx          ; 3) we are poping into `ecx`, now we have the
                     ; address of "Hello, World!
" 
    mov edx, 0xF
    int 0x80

    mov eax, 0x1
    mov ebx, 0x0
    int 0x80

MESSAGE:
    call GOBACK       ; 2) we are going back, since we used `call`, that means
                      ; the return address, which is in this case the address 
                      ; of "Hello, World!
", is pushed into the stack.
    db "Hello, World!", 0dh, 0ah

section .data

Now dump the text section:

$ nasm -f elf shellcode.asm
$ ld shellcode.o -o shellcode
$ ./shellcode 
Hello, World!
$ objdump -d shellcode

shellcode:     file format elf32-i386


Disassembly of section .text:

08048060 <_start>:
 8048060:   e9 1e 00 00 00   jmp    8048083 <MESSAGE>

08048065 <GOBACK>:
 8048065:   b8 04 00 00 00   mov    $0x4,%eax
 804806a:   bb 01 00 00 00   mov    $0x1,%ebx
 804806f:   59               pop    %ecx
 8048070:   ba 0f 00 00 00   mov    $0xf,%edx
 8048075:   cd 80            int    $0x80
 8048077:   b8 01 00 00 00   mov    $0x1,%eax
 804807c:   bb 00 00 00 00   mov    $0x0,%ebx
 8048081:   cd 80            int    $0x80

08048083 <MESSAGE>:
 8048083:   e8 dd ff ff ff   call   8048065 <GOBACK>
 8048088:   48               dec    %eax                    <-+
 8048089:   65               gs                               |
 804808a:   6c               insb   (%dx),%es:(%edi)          |
 804808b:   6c               insb   (%dx),%es:(%edi)          |
 804808c:   6f               outsl  %ds:(%esi),(%dx)          |
 804808d:   2c 20            sub    $0x20,%al                 |
 804808f:   57               push   %edi                      |
 8048090:   6f               outsl  %ds:(%esi),(%dx)          |
 8048091:   72 6c            jb     80480ff <MESSAGE+0x7c>    |
 8048093:   64               fs                               |
 8048094:   21               .byte 0x21                       |
 8048095:   0d               .byte 0xd                        |
 8048096:   0a               .byte 0xa                      <-+

$

The lines I marked are our "Hello, World! " string:

$ printf "x48x65x6cx6cx6fx2cx20x57x6fx72x6cx64x21x0dx0a"
Hello, World!

$ 

So our C wrapper will be:

char code[] = 

    "xe9x1ex00x00x00"  //          jmp    (relative) <MESSAGE>
    "xb8x04x00x00x00"  //          mov    $0x4,%eax
    "xbbx01x00x00x00"  //          mov    $0x1,%ebx
    "x59"                  //          pop    %ecx
    "xbax0fx00x00x00"  //          mov    $0xf,%edx
    "xcdx80"              //          int    $0x80
    "xb8x01x00x00x00"  //          mov    $0x1,%eax
    "xbbx00x00x00x00"  //          mov    $0x0,%ebx
    "xcdx80"              //          int    $0x80
    "xe8xddxffxffxff"  //          call   (relative) <GOBACK>
    "Hello wolrd!
";     // OR       "x48x65x6cx6cx6fx2cx20x57"
                            //          "x6fx72x6cx64x21x0dx0a"


int main(int argc, char **argv)
{
    (*(void(*)())code)();

    return 0;
}

Lets test it, using -z execstack to enable read-implies-exec (process-wide, despite "stack" in the name) so we can executed code in the .data or .rodata sections:

$ gcc -m32 test.c -z execstack -o test
$ ./test 
Hello wolrd!

It works. (-m32 is necessary, too, on 64-bit systems. The int $0x80 32-bit ABI doesn't work with 64-bit addresses like .rodata in a PIE executable. Also, the machine code was assembled for 32-bit. It happens that the same sequence of bytes would decode to equivalent instructions in 64-bit mode but that's not always the case.)

Modern GNU ld puts .rodata in a separate segment from .text, so it can be non-executable. It used to be sufficient to use const char code[] to put executable code in a page of read-only data. At least for shellcode that doesn't want to modify itself.

这篇关于Linux Shellcode“你好,世界!"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆