Right now, we are going to implement our own gets() procedure, which would, in fact, forward calls to fgets() just as our PE patch did. Unfortunately, Flat Assembler's support for ELF does not allow us to create shared objects in a simple way yet; therefore, we will create an object file and later link it with GCC as a shared object for a 32-bit system.
The source code is, as usual, quite simple and intuitive:
; First the formatter directive to tell
; the assembler to generate ELF object file
format ELF
; We want to export our procedure under
; the name "gets"
public gets as 'gets'
; And we need the following symbols to be
; imported from libc
; As you may notice, unlike Windows, the
; "stdin" is exported by libc
extrn fgets
extrn stdin
; As we want to create a shared object
; we better create our own PLT (Procedure
; Linkage Table)
section '.idata' writeable
_fgets dd fgets
_stdin dd stdin
section '.text' executable
; At last, the procedure
gets:
; Standard cdecl prolog
push ebp
mov ebp, esp
; Forward the call to fgets()
mov eax, [_stdin]
push dword [eax] ; FILE*
push 127 ; len
push dword [ebp + 8] ; Buff*
call [_fgets]
add esp, 12
; Standard cdecl epilog
mov esp, ebp
pop ebp
ret
Save the preceding code as fgets_patch.asm and compile it with fasm or fasm.x64; this will result in the fgets_patch.o object file. Building a shared object out of this object file is as simple as running one of the following commands in the terminal:
# On a 32-bit system
gcc -o fgets_patch.so fgets_patch.o -shared
# and on a 64-bit system
gcc -o fgets_patch.so fgets_patch.o -shared -m32
Let's now test and run the legacy executable without the patch and feed it with a long string (140 bytes). Here is the result:

As we can see, the stack was corrupted, which caused a segmentation fault (invalid memory access). Now we may try to run the same executable but set the LD_PRELOAD environment variable to "./fgets_patch.so", thus forcing our shared object to be loaded before anything else when launching the legacy executable. The command line would then be as follows:
LD_PRELOAD=./fgets_patch.so ./legacy
This time, we get the output just as expected, --truncated to 127 characters, --meaning that our implementation of gets() was linked by the dynamic linking process:
