The reason the you are unable to locate
text1 on the program runtime stack is that during runtime
text1 is in the
data segment of the process running in virtual memory, not the stack. In order for a reference to
text1 to be written to the stack
text1 must be passed as an argument to a function which is called.
When a function is called and a new stack frame is created on the runtime stack for that function, memory is allocated on the stack for any local variables declared in that function as well. However,
target are global variables declared outside of any function. A direct consequence of this is that memory will not be allocated for
target on the stack. Instead,
text2 will be in the process's
data segment and
target will be in the
bss segment. In order to understand why, familiarity with the ELF and the System V Application Binary Interface is essential.
x86 Linux Process Layout in Virtual Memory
For some context, here is a diagram of a process's layout in virtual memory on an x86 Linux system from Gustavo Duarte's article titled "Anatomy of a Program in Memory":
A look at this diagram will help clarify the significance of the memory addresses you are seeing. On an x86 Linux system, the stack is high in virtual memory and grows downward. This is why when the stack is examined one sees memory addresses such as
0xffffccb0. The location in virtual memory of global variables
target will be more proximate to the program entry point since the
bss segments are adjacent to the
text segment, which is low in memory. In light of this, a memory address of
text1 makes sense.
ELF and the System V ABI
From Section 4 (Object Files) of the ABI:
An executable file holds a program suitable for execution; the file specifies how the function
exec creates a program's process image.
Created by an assembler and link editor, object files are binary representations of programs intended to execute directly on a processor.
Once the program is compiled, assembled and linked, it is essentially a description of what it will look like as a process. This means that an executable binary can be statically analyzed to get an idea of how things will look when the program is running. When the binary constructed from the source code provided above is analyzed it is observed that the values
text2 are in the
$ readelf -x .rodata <ELF BINARY NAME>
Hex dump of section '.rodata':
0x08048538 03000000 01000200 41414141 41414141 ........AAAAAAAA
0x08048548 41414141 41004242 42424242 42424242 AAAAA.BBBBBBBBBB
0x08048558 42424200 796f7520 68617665 206d6f64 BBB.you have mod
0x08048568 69666965 64207468 65207461 72676574 ified the target
0x08048578 203a2900 50726f67 72616d20 6e616d65 :).Program name
0x08048588 3a202573 00 : %s.
According to the ABI (4-19), the
.rodata section holds read-only data that typically contributes to a non-writable segment in the process image. Examples of non-writable process image segments are the
data segments mentioned above. The implication of this is that
text2 will be located near where the program instructions are, namely the
text segment, when the program is loaded into memory. The instruction memory addresses will look much more similar to the memory addresses of
text2 than memory addresses on the stack.
target variable is an uninitialized global variable, so its data will be held in the
Arguments to Functions are Written to the Stack
If you want pointers to these variables to appear on the runtime stack they must be passed as arguments to a function that is called at some point throughout the course of process execution, as a function's arguments are typically written to the stack in the caller's argument build area, as seen in the diagram below.
Stack layout with multiple frames (from CSAPP):
For example, instead of passing
argv as an argument to the
vuln function, the global variable
text1 can be passed instead. A pointer to
text1 would then be saved on the stack prior to
vuln being called.
Alternatively, instead of hardcoding 'A' as a value for a global variable, you can pass an arbitrary number of 'A's (or any other ASCII characters) as an argument on the command line when executing your program in the shell. This will result in whatever values you pass being stored in
argv which is the argument to
It should be noted that due to their global scope,
data can be referenced in any function without being passed as an argument, but in the context of format string vulnerabilities and
that is not particularly useful to know.
For More Information
For more information on how
printf behaves in a x86 Linux environment, one can take a look at the answer to the following question on stackoverflow in which a user is calling
printf in a non-standard fashion: "ELF32 binary, little endian or not?"
Section 3.7 (titled "Procedures") in "Computer Systems: A Programmer's Perspective" covers function calls and the stack on an assembly level and has several helpful diagrams.