/*    
    ---------------------------------------------------------------------------

    Got bored with a certain video game and wanted to mess with some numbers, 
    wrote this to do it. Just uses ptrace to scan all writable memory segments 
    listed in /proc for the target buffer and replaces it. I am sure there are 
    a few entertainment uses for it, also works as a nice way to patch banners 
    on the fly in various banners, mess with the guy who left his screen 
    unlocked, etc... Its really rough, based it off the ssh/telnet hijacker, 
    since the memread/memwrite functions weren't half bad. Faster than gdb and
    only looks at the memory segments which are writable and not inside the 
    executable or loaded libraries (i was using it to change heap/stack data).
    
    Not really interesting for people already familiar with the ptrace api...
    
    ---------------------------------------------------------------------------

    name:   memrep.c
    
    author: hdm@digitaloffense.net
    
    what:   find and replace memory in a running process with a single command
        
    rips:   much code taken from onelove.c by xenion, done quick and dirty
    
    usage:
            replace the pat and rep buffers with your search/replace data
            they must be the same size, almost no checks are done now to
            verify this before using them.

            this example changes ABCD to TEST
*/


#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/user.h>
#include <signal.h>
#include <asm/unistd.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <sys/stat.h>


#define WORD_SIZE 4
#define BUFLEN 4096

int memread(pid_t, unsigned char *, unsigned char *, long, long);
int memwrite(pid_t, unsigned char *, unsigned char *, long, long);

int main(int argc, char **argv)
{
    FILE *maps;
    char maps_path[256];
    char maps_line[256];
    char *mptr;
    
    unsigned char buf[BUFLEN];
    int i, pid;
    unsigned char *beg;
    unsigned char *end;
    unsigned char *cur;
    unsigned char *tmp;

    /* change these */        
    unsigned char pat[4];
    unsigned char rep[4];
    
    pat[0] = 0x41;
    pat[1] = 0x42;
    pat[2] = 0x43;
    pat[3] = 0x44;
    
    rep[0] = 0x54;
    rep[1] = 0x45;
    rep[2] = 0x53;
    rep[3] = 0x54;   

 
    setvbuf(stdout, (char *)NULL, _IONBF, 0);
    
    if (argc < 2)
    {
        fprintf(stderr, "usage: %s <pid>\n", argv[0]);
        exit(0);   
    }
    
    pid = atoi(argv[1]);
    
    snprintf(maps_path, 255, "/proc/%d/maps", pid);
    maps = fopen(maps_path, "r");
    
    if (maps == NULL)
    {
        fprintf(stderr, "error: could not access process memory map.\n");
        perror("fopen");
        exit(0);   
    }
    
    if (ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
    {
           fprintf(stderr, "cant ptrace!\n");
           exit(0);
    }
    ptrace(PTRACE_SYSCALL, pid, 0, 0);
    wait(&i);
 
    while (fgets(maps_line, 255, maps) != NULL)
    {
        if ( strstr(maps_line, "/")  == NULL && strstr(maps_line, "rw") != NULL)
        {
            mptr = strstr(maps_line, "-");
            if (mptr != NULL)
            {
                *mptr = (char) "\0";
                mptr++;
                
                beg = (char *) strtoll(maps_line, NULL, 16);
                end = (char *) strtoll(mptr, NULL, 16);

                fprintf(stderr, "[*] scanning 0x%.8x -> 0x%.8x\n", beg, end);

                /* start the search replace */ 
                cur = beg;
                i++;
                while (cur < end)
                {
                    i = memread(pid, buf, cur, sizeof(buf), sizeof(buf));

                    if (i >= 0)
                    {
                        tmp = buf;
                        while (tmp < (buf + sizeof(buf)))
                        {
                            
                            /* printf("[x] 0x%.8x: %.2x %.2x %.2x %.2x\n", cur + (tmp - buf), tmp[0], tmp[1], tmp[2], tmp[3]); */
                            if (memcmp(tmp, pat, sizeof(pat)) == 0)
                            {
                                printf("[*] 0x%.8x: %.2x %.2x %.2x %.2x\n", cur + (tmp - buf), tmp[0], tmp[1], tmp[2], tmp[3]);
                                i = memwrite(pid, cur + (tmp - buf), rep, sizeof(rep), sizeof(rep));
                                if(!i) perror("memwrite");
                            }
                            tmp += sizeof(pat);
                        }
                    } else {
                        perror("memread");   
                    }
                    cur += sizeof(buf);
                }
            }
        }
    }
    
    ptrace(PTRACE_DETACH, pid, 0, 0);
    kill(pid, SIGCONT);
    
    return 0;
}

int
memread(pid_t pid, unsigned char *dest, unsigned char *src, long count,	long len)
{
    long            off;
    long            res;
   
    if (count < 0 || len < 0)
	    return (-1);

    if (len < count)
	    return -1;

    for (off = 0; off < count; off += WORD_SIZE) 
    {
        
	    res = ptrace(PTRACE_PEEKTEXT, pid, src + off, 0);
        
	    if (errno > 0)
        {
            printf("couldnt read 0x%.8x\n", src + off);
            perror("peek");
	        return count;
        }
	    else
        {
	        memcpy(dest + off, &res, WORD_SIZE);
        }
    }
    return count;
}


int
memwrite(pid_t pid, unsigned char *dest, unsigned char *src, long count, long len)
{
    long            off;
    long            res;

    if (count < 0 || len < 0)
	return (-1);

    if (len < count)
	return -1;

    for (off = 0; off < count; off += WORD_SIZE) {
	memcpy(&res, src + off, WORD_SIZE);
	if (ptrace(PTRACE_POKETEXT, pid, dest + off, res) < 0)
	    return -1;
    }

    return count;
}
