您的位置:首页 > 编程语言 > C语言/C++

C语言内存中执行外部代码实例-windows篇

2013-11-24 20:30 465 查看
本文采用的源代码和参考链接来自于http://www.hoverlees.com/blog/?p=1014

        最近在研究二进制代码在内存中的运行,无意间看到上述博客,于是乎就把源代码从网上拷贝下来,在Windows的VS2013运行,一运行,崩溃了,于是乎将过程和解决方法总结下来,以备以后参考。

       需要的工具:VS编译环境,笔者在VS2013上编译,其他版本估计也是同样的问题;另外需要IDA反汇编器,方便我们对代码进行提取和反汇编。

       1、第一步,编译代码。在VS2013(VC6也可以啦),新建一个Win32 Console Application,将下面代码直接拷贝到main函数所在的文件,将原有的main函数代码注释掉,,代码见下方。编译选用release模式,等你看完本文,也可以选择debug模式,这里为了生成的代码简单些,

#include <stdio.h>

#include <stdlib.h>

#include <sys/stat.h>

typedef int(*__stat)(const char *path, struct stat *buffer);

typedef FILE* (*__fopen)(const char *filename, const char *mode);

typedef void * (*__malloc)(size_t size);

typedef size_t(*__fread)(void *buffer, size_t size, size_t count, FILE *stream);

typedef int(*__fclose)(FILE *stream);

int get_number_line(const char* filename, void **p){
FILE* fp = NULL;
char* buffer;
struct stat st;
int num = 0;
int i;
__stat _stat;
__fread _fread;
__fopen _fopen;
__malloc _malloc;
__fclose _fclose;
const char* fmode;

_stat = (__stat)p[0];
_fopen = (__fopen)p[1];
_malloc = (__malloc)p[2];
_fread = (__fread)p[3];
_fclose = (__fclose)p[4];
fmode = (const char*)p[5];

 

  fp = _fopen(filename, fmode);
if (fp == NULL) return -1;
_stat(filename, &st);
buffer = (char*)_malloc(st.st_size);
if (buffer == NULL) return -1;
_fread(buffer, st.st_size, 1, fp);
for (i = 0; i < st.st_size; i++){
if (buffer[i] == '\n') num++;
}
_fclose(fp);
return num;

}

void main(int argc, char* argv[]){
void* p[6];
int i;
p[0] = stat;
p[1] = fopen;
p[2] = malloc;
p[3] = fread;
p[4] = fclose;
p[5] = "rb";
i = sizeof(struct stat);
i = get_number_line("./src.c", p);
printf("%x  %d\n", get_number_line, i);

}

第二步,提取get_number_line的二进制代码

将IDA Pro打开,我使用的时候6.4版本的,直接将上面生成的exe程序拖动到IDA打开的界面上,找到get_number_line函数对应的二进制代码,很容易的,注意第一行代码要找push命令开头的那个,而不要找jmp命令的,如果是jmp命令,直接点击jmp后面的函数符号,自动跳转到函数第一条指令。

这里记住get_number_line的起始地址,关于结束怎么找,IDA也有很强大的功能,直接可以找类似?get_number_line@@YAHPBDPAPAX@Z endp的字样,这就是结束地址,然后转到hex view右键将二进制代码另存到文件,注意开头一定是55 8B哦。

第三步,将保存的二进制代码用下面的源代码进行调用,运行,代码如下:

#define _IN_WINDOWS 1

#include <stdio.h>

#include <sys/stat.h>

#include <stdlib.h>

#include <string.h>

#ifdef _IN_WINDOWS

#include <windows.h>

#else

#include <sys/mman.h>

#endif

typedef int(*_get_number_line)(const char* filename, void** p);

#ifdef _IN_WINDOWS

/**

* 将代码文件安装到本进程虚拟地址空间中 windows版

* @param mem 保存函数指令的内存

* @param len 内存长度

* @return 安装好后的函数指针

*/

void* install_function(void* mem, int len){
void* function;
function = VirtualAlloc(NULL, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (function == NULL) return NULL;
memcpy(function, mem, len);
VirtualProtect(function, len, PAGE_EXECUTE_READ, NULL);
return function;

}

#else

/**

* 将代码文件安装到本进程虚拟地址空间中 linux版

* @param mem 保存函数指令的内存

* @param len 内存长度

* @return 安装好后的函数指针

*/

void* install_function(void* mem, int len){
void* function;
function = mmap(NULL, len, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (function == -1) return NULL;
memcpy(function, mem, len);
return function;

}

#endif

void* mallocex(size_t size)

{
return malloc(size);

}

void main(){
FILE* fp;
unsigned char fmem[1024];
int memLen;
void* p[6];
_get_number_line get_number_line;
//初始化本地地址,供载入的函数用
p[0] = stat;
p[1] = (void*)fopen;
p[2] = mallocex;
p[3] = fread;
p[4] = fclose;
p[5] = "rb";
//从文件读取要执行的指令
fp = fopen("./get_line_number.function", "rb");
memLen = fread(fmem, 1, 1024, fp);
fclose(fp);
//如果这个文件是加密的,需要在这里解密
//...
//注入指令到本进程虚拟地址空间中
get_number_line = (_get_number_line)install_function(fmem, memLen);
if (get_number_line == NULL){
printf("install function fail.");
return;
}
//调用注入的代码
int a = get_number_line("./test.txt", p);
printf("total line:%d\n", a);

}

哎呀,运行崩溃了。

第四步,解决运行崩溃问题。

这里我使用了二分法,将get_number_line进行了逐行注释,对比能运行的代码发现反汇编多了这么一条指令

[b] E8 C5 00 00 00                          call    @__security_check_cookie@4 ;[/b]

这是一个函数调用,从IDA看到其实这个函数调用的二进制码没有复制到原有函数中去,那么估计是这段函数造成的,运行时崩溃,上网搜索一下,知道这个函数是编译器生成的保护性代码产生的,要想不生成这段代码,需要在编译选项里设置一下。

在VS2013里,需要设置project properties->C/C++->Code Generation->Security Check修改为Disable Security Check(/GS-)重新编译提取代码即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: