I know by overwriting the return address in vulnerable program we can change the offset of next instruction and make it to point to our injected buffer. but this buffer is in stack segment and the offset (ip) is calculated for "code segment" so how is it possible to make the program execute the code in the data segment? (Suppose near call.)


In most (if not all) modern operating systems that run on x86 hardware, segments are ignored. The segment registers are set to begin at address 0 and to extend for the whole 32-bit address space; thus, offsets computed relatively to one segment are valid for all segments, since they all start at the same place and exactly overlap each other. In practice, application code does not fiddle with segment registers at all; it just assumes that they are already set as described above, and forgets about them.

Consequently, in a modern OS, there are no "far calls". All calls, all data accesses are "near". The notion of "modern OS" includes here all Windows versions since Windows 95 (and also the older Windows 3.1 when the "win32s" extensions are installed), and all versions of Linux since day 1.

Some finer details:

  • When the machine is 64-bit able, and the OS kernel runs in 64-bit mode, then it can still run code in 32-bit mode, but that's only an "emulation". In that emulated mode, segments are really fixed at address 0, and cannot be changed by the kernel. This means that the "let's forget about segment registers" stance has become firmly entrenched in the hardware as well.

  • Some operating system will make the stack (and other data elements) "non-executable" so as to make life harder for the attacker; this is called Data Execution Prevention. One possible trick for that is, indeed, the use of segment registers: for instance, the CS segment will cover addresses 0 to 1073741823, and the OS will arrange for non-executable elements (in particular the stack) to be located at an offset above 1073741824. In that case, any attempt at jumping into code that is in the stack will make the CPU try to read opcodes (thus relatively to CS) beyond the limit of that segment, which then triggers an exception. However, data accesses will be made with DS or SS, whose upper limit is kept at 4294967295, and will thus be unhindered.

    As a DEP mechanism, segment registers are not very flexible, since they require a clear separation between areas that can be executed, and areas that cannot. JIT compilers, in particular, are not very happy with that, since they need to allocate RAM, write code in it, then make the area executable, which cannot be done if it was not allocated in the "executable space" (the area covered by CS) in the first place. Moreover, as said above, segment-based tricks don't work when the kernel is in 64-bit mode. This is why modern OS, when doing DEP, prefer to do it through the MMU, which allows for allowing or preventing execution on a per-page basis, and to dynamically change execution rights for each page.

    (Also, DEP is only an attempt at hindering the attacker, but workarounds exist. For security, it is much better to not allow buffers to be overflown, rather than trying to recover more or less cleanly from an overflown buffer.)

  • The GS register is special; its base address is not the address 0, and it still works when the kernel is in 64-bit mode. It is used for efficient thread-local storage.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.