linux process inject

业余花了几周时间,终于把代码inject到linux runtime process,做为10几年工作经验的JAVA程序员,期间犯了不少错误,记录一下。

首先是C语言编写,按原来写法, 先定变量,再赋值,导致代码可读性太差。编译时指定 -std=gnu99 ,使得C可以像JAVA一样使用时定义变量。

编译器选择上,用clang比gcc好太多了,clang编译时查找语法错误比gcc多太多了,gcc已老。

其次在处理linux incline asm时,标准写法 asm("asm语句块":输出constrains:输入constrains:受影响寄存器);   输出、输入constrains通常有r表示寄存器,m表示内存,i或者n表示integer操作数,先期常用r,导致asm语句块中使用的寄存器与输出、输入使用的寄存器冲突,改用g就好了,使用任意的registor、memory、integer等。asm语句块使用";"或者"\n"分隔

做为JAVA程序员,处理coredump时有些问题,把代码inject到程序头8个字节后,通常是ELF文件header开始,这时候是没有调用堆栈的,gdb exec corefile后,用where/bt显示不了call stack,想看汇编代码disassemble也没用,刚开始无从下手,后面发现既然自己inject code到target 指定address,尝试使用x/40i address打印内存inject code,发现gdb有指向当前运行指令,通过p $rip也能应证。问题点找到了,自然发现不少JAVA程序员没有考虑的事情,第一个事情就是在注入的shellcode中使用字符常量,大bug,通常字符常量放.data段,并不是.text段,inject 目的地在elf header + .text段时,自然找不到字段常量。

在落地版本的shellcode(尝试使用,实际存活概率肯定很多),dynamic so load时,可通过__attribute__((constructor))指定load 代码,有点类似于windows下很多driver注册函数了。

最后贴上代码,JAVA程序员写C代码巨丑

#include <unistd.h>

#include <stdio.h>

#include <sys/user.h>

#include <signal.h>

#include <sys/mman.h>

#include <sys/types.h>

#include <dlfcn.h>

#include <alloca.h>

#include <string.h>

#include <getopt.h>

#include <dirent.h>

#include <sys/user.h>

#include <sys/stat.h>

#include <stdlib.h>

#include <limits.h>

#include "ptrace.h"

u_int64_t shellcode_size = 0;

/**

* r11: shellcode fullfile path size

* r12: __libc_dlopen_mode

*/

void inject() {

int64_t prot = PROT_EXEC | PROT_READ | PROT_WRITE;

int64_t flags = MAP_SHARED | MAP_ANONYMOUS;

int64_t fd = 0;

off_t offset = 0;

long ret_addr;

//call mmap syscall allocate memory

//void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

asm("mov  $0,%%rdi \n"

"mov %%r11,%%rsi \n"

"mov  %0,%%rdx \n"

"mov  %1,%%r10 \n"

"mov  %2,%%r8  \n"

"mov  %3,%%r9  \n"

"mov  $9,%%rax  \n"

"syscall \n"

"int $3 \n"

:

:"g"(prot),"g"(flags),"g"(fd),"g"

""(offset)

:"%rax");

asm("mov %%rax,%0":"=m"(ret_addr));

//call __libc_dlopen_mode

asm("mov %%rax,%%rdi \n"

"mov $1,%%rsi \n"

"callq *%%r12 \n"

:

);

// call munmap

asm("mov %%rax,%%rdi \n"

"mov %%r11,%%rsi \n"

"mov $11,%%rax \n"

"syscall \n"

:

);

}

void inject_end() {

}

unsigned long get_start_addr(pid_t pid) {

size_t filename_max_len = 256;

char proc_file[filename_max_len];

FILE *fd;

int max_line_limit = 65535;

char *line = alloca(max_line_limit);

long addr;

char perm[5];

snprintf(proc_file, filename_max_len, "/proc/%d/maps", pid);

fd = fopen(proc_file, "r");

if (fd == NULL) {

perror("fopen");

return 1;

}

while (fgets(line, max_line_limit, fd) != NULL) {

sscanf(line, "%lx-%*lx %s %*d %*s %*s", &addr, perm);

if (strstr(perm, "x") > 0) {

return addr;

}

}

fclose(fd);

return 2;

}

unsigned long getlibc(pid_t pid) {

size_t filename_max_len = 256;

char proc_file[filename_max_len];

FILE *fd;

int max_line_limit = 65535;

char *line = alloca(max_line_limit);

long addr;

char perm[5];

snprintf(proc_file, filename_max_len, "/proc/%d/maps", pid);

fd = fopen(proc_file, "r");

if (fd == NULL) {

perror("fopen");

return 1;

}

while (fgets(line, max_line_limit, fd) != NULL) {

sscanf(line, "%lx-%*lx %*s %*d %*s %*s", &addr);

if (strstr(line, "libc") > 0) {

return addr;

}

}

fclose(fd);

return 2;

}

long get_dyn_func_addr(char *func) {

void *handle;

void *addr;

if ((handle = dlopen("libc.so.6", RTLD_LAZY)) == NULL) {

perror("dlopen");

}

if ((addr = dlsym(handle, func)) == NULL) {

perror("dlsym");

}

printf("function %s addr is 0x%lx\n", func, (long) addr);

return (long) addr;

}

void * read_shellcode(char* path) {

FILE *fd;

struct stat stat;

void *buffer;

size_t read_count = 0;

fd = fopen(path, "rb");

if (fstat(fd->_fileno, &stat) == -1) {

perror("fstat");

fclose(fd);

return NULL;

}

shellcode_size = stat.st_size;

buffer = malloc(shellcode_size);

if ((read_count = fread(buffer, 1, shellcode_size, fd)) < shellcode_size) {

printf("read shellcode error:%lu is less %lu\n", read_count,

shellcode_size);

}

fclose(fd);

return buffer;

}

int main(int argc, char **argv) {

int opt;

pid_t pid;

char *exe_file;

int inj_f = 0;

char *shellcode;

char fullpath_exe_file[PATH_MAX + 1];

char fullpath_shellcode[PATH_MAX + 1];

unsigned long addr;

void *old_code;

struct REG_TYPE old_regs;

struct REG_TYPE new_regs;

long len;

pid_t this_pid = getpid();

unsigned long mylibc_addr;

unsigned long mydlopen_addr;

unsigned long dlopen_offset;

unsigned long libc_addr;

unsigned long dlopen_addr;

unsigned long long shellcode_fullpath_buffer;

void *shellcode_buffer;

while ((opt = getopt(argc, argv, "p:f:s:")) != -1) {

switch (opt) {

case 'p':

pid = atoi(optarg);

break;

case 'f':

exe_file = optarg;

inj_f = 1;

break;

case 's':

shellcode = optarg;

break;

}

}

printf("shellcode is %s\n", shellcode);

if (realpath(shellcode, fullpath_shellcode) == NULL) {

perror("realpath shellcode");

exit(1);

}

if (inj_f) {

if (realpath(exe_file, fullpath_exe_file) == NULL) {

perror("realpath exe_file");

exit(1);

}

switch (fork()) {

case -1:

perror("fork");

exit(2);

// child code path

case 0:

if (execl(fullpath_exe_file, NULL) == -1) {

perror("execl");

}

pid = getpid();

break;

default:

printf("fork call");

}

}

// start to modify code

ptrace_attach(pid);

memset(&old_regs, 0, sizeof(struct REG_TYPE));

memset(&new_regs, 0, sizeof(struct REG_TYPE));

ptrace_getregs(pid, &old_regs);

memcpy(&new_regs, &old_regs, sizeof(struct REG_TYPE));

addr = get_start_addr(pid) + sizeof(long);

printf("inject start 0x%lx\n", addr);

len = inject_end - inject;

old_code = malloc(len * sizeof(char));

ptrace_read(pid, addr, old_code, len);

ptrace_write(pid, addr, inject, len);

mylibc_addr = getlibc(this_pid);

mydlopen_addr = get_dyn_func_addr("__libc_dlopen_mode");

dlopen_offset = mydlopen_addr - mylibc_addr;

libc_addr = getlibc(pid);

dlopen_addr = libc_addr + dlopen_offset;

new_regs.rip = addr + 2;

new_regs.r11 = strlen(fullpath_shellcode) + 1;

new_regs.r12 = dlopen_addr;

ptrace_setregs(pid, &new_regs);

//start mmap

ptrace_cont(pid);

//target stopped at mmap

ptrace_getregs(pid, &new_regs);

shellcode_fullpath_buffer = new_regs.rax;

printf("mmap return address is %llx\n",shellcode_fullpath_buffer);

if (shellcode_fullpath_buffer == -1){

printf("mmap failed");

exit(-1);

}

// inject shellcode

ptrace_write(pid, shellcode_fullpath_buffer, fullpath_shellcode, strlen(fullpath_shellcode) + 1);

// run dlopen shellcode so at target

ptrace_cont(pid);

free(shellcode_buffer);

free(old_code);

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354