Binary modification is a complex subject and requires some understanding of the system's application binary interface (ABI), process layout in virtual memory, the runtime environment, and the compilation process. Background information will be discussed here in order to provide context for the answer to the question.
1. Code Injection vs. Static Binary Modification (Patching)
From the question:
Code injection into binary is the process of injecting some malicious code into a legitimate binary.
A lot of code injection projects that I have seen will find a large
.text section in order to inject their shellcode. They then change the address of the entry point in order to point to the injected shellcode. The injected shell code will then point to the beginning of the original code after finishing.
Code injection usually refers to code included in input entered at program runtime, which differs from the process described above, in which a binary is statically modified prior to program execution in order to alter its runtime behavior.
From MITRE's Common Weakness Enumeration (CWE) entry regarding code injection (emphasis mine):
The software constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
When software allows a user's input to contain code syntax, it might be possible for an attacker to craft the code in such a way that it will alter the intended control flow of the software. Such an alteration could lead to arbitrary code execution.
Injection problems encompass a wide variety of issues -- all mitigated in very different ways. For this reason, the most effective way to discuss these weaknesses is to note the distinct features which classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed. The most classic instantiations of this category of weakness are SQL injection and format string vulnerabilities.
One implication here is that code injection is a technique employed at program runtime in order to alter program behavior. A second implication is that code injection includes executable code in input whose origin is external to the binary itself, and code injection does not alter the structure of the binary.
Altering the structure of an executable binary prior to execution can be referred to as static binary instrumentation, but is more commonly referred to as patching.
From PEBIL: Efficient Static Binary Instrumentation for Linux (emphasis mine):
There are two main approaches to binary instrumentation: static and dynamic. Static binary instrumentation inserts additional code and data into an executable and generates a persistent modified executable whereas the dynamic instrumentation inserts additional code and data during execution without making any permanent modifications to the executable.
The distinction between runtime process modification and static modification is important, because their technical implications are quite different.
2. Sections, Segments, Linking and Relocation
From the question:
Sometimes, there is not enough available
\x00 bytes into the
.text section in order to perform this action. So, the project will not be able to perform that injection.
Why don't they just increase the size of the ".text" section segment?
There is an important distinction between sections and segments in ELF binaries:
Sections hold the bulk of object file information for the linking
view: instructions, data, symbol table, relocation information, and so on1
Sections and their associated information are used by
ld to perform relocation and symbol resolution.
- segments and their associated information are used by the program loader in the kernel to map portions of the binary file on disk into virtual memory at the correct offsets
Here is a diagram from the System V ABI contrasting sections with segments:
Object files participate in program linking (building a program) and program execution (running a program). For convenience and efficiency, the object file format provides parallel views of a file’s contents, reflecting the differing needs of these activities.1
Why does this matter? The address ranges of the a program's sections, including the
.text section, as well as the address range of the
.text segment, are both determined by the linker which is invoked on the object file at after preprocessing, compilation and assembly. From Advanced C and C++ Compiling page 26:
• The ultimate intention behind the idea of building the program is that the sections obtained by compiling individual source files be combined (tiled) together into the single binary executable file. Such binary file would contain the sections of the same type (.text, .data, .bss, . . . ) obtained
by tiling together the sections from the individual files. Figuratively speaking, an object file can be viewed as a simple tile waiting to find its place in the giant mosaic of the process
• The inner structure of the object file does not, however, suggest where the individual sections will ultimately reside in the program memory map. For that reason, the address ranges of each section in each of the object files is tentatively set to start from a zero value.
The actual address range at which a section from an object file will ultimately reside in the program map will be determined in the subsequent stages (linking) of program building process.
• In the process of tiling object files’ sections into the resultant program memory map, the only truly important parameter is the length of its sections, or to say it more precisely, its address range.
It should also be noted that an executable ELF binary's
text segment consists of multiple sections in addition to linked
$ readelf -SW /bin/ls
There are 29 section headers, starting at offset 0x1e738:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 000254 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 000298 0000c0 00 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000400358 000358 000cd8 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000401030 001030 0005dc 00 A 0 0 1
[ 7] .gnu.version VERSYM 000000000040160c 00160c 000112 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000401720 001720 000070 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000401790 001790 0000a8 18 A 5 0 8
 .rela.plt RELA 0000000000401838 001838 000a80 18 AI 5 24 8
 .init PROGBITS 00000000004022b8 0022b8 00001a 00 AX 0 0 4
 .plt PROGBITS 00000000004022e0 0022e0 000710 10 AX 0 0 16
 .plt.got PROGBITS 00000000004029f0 0029f0 000008 00 AX 0 0 8
 .text PROGBITS 0000000000402a00 002a00 011289 00 AX 0 0 16
 .fini PROGBITS 0000000000413c8c 013c8c 000009 00 AX 0 0 4
 .rodata PROGBITS 0000000000413ca0 013ca0 0069b4 00 A 0 0 32
 .eh_frame_hdr PROGBITS 000000000041a654 01a654 00080c 00 A 0 0 4
 .eh_frame PROGBITS 000000000041ae60 01ae60 002c84 00 A 0 0 8
 .init_array INIT_ARRAY 000000000061de00 01de00 000008 00 WA 0 0 8
 .fini_array FINI_ARRAY 000000000061de08 01de08 000008 00 WA 0 0 8
 .jcr PROGBITS 000000000061de10 01de10 000008 00 WA 0 0 8
 .dynamic DYNAMIC 000000000061de18 01de18 0001e0 10 WA 6 0 8
 .got PROGBITS 000000000061dff8 01dff8 000008 08 WA 0 0 8
 .got.plt PROGBITS 000000000061e000 01e000 000398 08 WA 0 0 8
 .data PROGBITS 000000000061e3a0 01e3a0 000260 00 WA 0 0 32
 .bss NOBITS 000000000061e600 01e600 000d68 00 WA 0 0 32
 .gnu_debuglink PROGBITS 0000000000000000 01e600 000034 00 0 0 1
 .shstrtab STRTAB 0000000000000000 01e634 000102 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
segments and section-to-segment mappings:
$ readelf -l /bin/ls
Elf file type is EXEC (Executable file)
Entry point 0x4049a0
There are 9 program headers, starting at offset 64
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000001dae4 0x000000000001dae4 R E 200000
LOAD 0x000000000001de00 0x000000000061de00 0x000000000061de00
0x0000000000000800 0x0000000000001568 RW 200000
DYNAMIC 0x000000000001de18 0x000000000061de18 0x000000000061de18
0x00000000000001e0 0x00000000000001e0 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000001a654 0x000000000041a654 0x000000000041a654
0x000000000000080c 0x000000000000080c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x000000000001de00 0x000000000061de00 0x000000000061de00
0x0000000000000200 0x0000000000000200 R 1
Section to Segment mapping:
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
05 .note.ABI-tag .note.gnu.build-id
08 .init_array .fini_array .jcr .dynamic .got
The third segment (segment
02) is of type
LOAD, has execute permissions, and corresponds to the process
text segment in virtual memory. Here, this segment consists not only of the
.text section but also several others, some of which consist of data rather than instructions, such as the
In light of the information of sections and segments shown above, it is important to note the at linker controls the following:
- section definitions
The most frequently used statement in the SECTIONS command is the section definition, which specifies the properties of an output section: its location, alignment, contents, fill pattern, and target memory region. Most of these specifications are optional
- section attributes
- section placement
In a section definition, you can specify the contents of an output section by listing particular input files, by listing particular input-file sections, or by a combination of the two. You can also place arbitrary data in the section, and define symbols relative to the beginning of the section.
- memory layout
The linker's default configuration permits allocation of all available memory. You can override this configuration by using the MEMORY command. The MEMORY command describes the location and size of blocks of memory in the target. By using it carefully, you can describe which memory regions may be used by the linker, and which memory regions it must avoid. The linker does not shuffle sections to fit into the available regions, but does move the requested sections into the correct regions and issue errors when the regions become too full. A command file may contain at most one use of the MEMORY command; however, you can define as many blocks of memory within it as you wish.
Looking at a sample output from a link map shows in a concrete manner that it is the linker that controls program memory layout through the assignment of memory addresses to sections and symbols:
.text 0x0000000008048074 0xa
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
.text 0x0000000008048074 0xa test.o
[!provide] PROVIDE (__etext, .)
[!provide] PROVIDE (_etext, .)
[!provide] PROVIDE (etext, .)
*(.rodata .rodata.* .gnu.linkonce.r.*)
.eh_frame 0x0000000008048080 0x38
.eh_frame 0x0000000008048080 0x38 test.o
3. Direct Answer to Question
Why don't they just increase the size of the ".text" section segment? Is it because of security reasons?
The address range of the
.text section and the layout of the
text segment in memory are determined by the linker at binary build time. In order for any changes to these to be made, access to the source code of the binary is required, since the binary would have to be rebuilt (compiled -> assembled -> linked) with a new memory layout, which is controlled by the linker.
Patching is performed if rebuilding the binary (which implies access to the source code) cannot be done.
1. SYSTEM V APPLICATION BINARY INTERFACE Edition 4.1