Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
354 views
in Technique[技术] by (71.8m points)

c - Linux Shellcode "Hello, World!"

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...