如何用汇编实现C语言函数调用 20
请教一个模拟C语言函数调用的过程:现在我们知道C函数的入口地址,知道函数参数的起始地址和长度,但不知道这个参数列表本身,我们要调用这个C函数,并把返回值放在指定的返回值起...
请教一个模拟C语言函数调用的过程:
现在我们知道C函数的入口地址,知道函数参数的起始地址和长度,但不知道这个
参数列表本身,我们要调用这个C函数,并把返回值放在指定的返回值起始地址中
,已知这个返回值的长度,但并不知道返回类型
体系是Intel 64位机,编译器是gcc。
多谢各位大侠了!
#include <stdio.h>
struct args {
int a;
float b;
};
int func(int a, float b) {
printf("a = %d, b = %f\n", a, b);
return a;
}
int main() {
// Now we know a function pointer 'func' but we don't know the argument
list
// And we have a pointer of struct args but we don't know it's member
either
// Q: How can we call 'func' ?
struct args arg_list;
arg_list.a = 5;
arg_list.b = 6.5;
void *arg_list_p = &arg_list;
size_t arg_list_len = sizeof(struct args);
int ret;
size_t ret_sz = sizeof(int);
void *ret_p = &ret;
// You have: the function pointer "func", the start address of arguments
"arg_list_p",
// the size of the arguments "arg_list_len", the return address of
"ret_p"
// the size of the return value "ret_sz"
// Add your code here
}
感谢 下一个往站ex 网友的回答。但我没有描述清楚我的问题。 我的目的是,用汇编实现调用,而不是func本身。
我实现了一段 AT&T 格式的汇编码,但是一直有segment fault:
__asm__ __volatile__(
"push %%rbp;"
"mov %%rsp, %%rbp;"
"push %%rsi;"
"push %%rdi;"
"sub %3, %%rsp;"
"mov %%rsp, %%rdi;"
"mov %4, %%rsi;"
"mov %3, %%rcx;"
"rep movsb;"
"call *%2;"
"mov %%rax, %1;"
"add %3, %%rsp;"
"pop %%rdi;"
"pop %%rsi;"
"mov %%rbp, %%rsp;"
"pop %%rbp;"
: "=r"(ret)
: "r"(ret), "r"(func_ptr), "r"(arg_list_len), "r"(arg_list_p)
: "memory"
);
出错原因可能是SP赋值不正确 展开
现在我们知道C函数的入口地址,知道函数参数的起始地址和长度,但不知道这个
参数列表本身,我们要调用这个C函数,并把返回值放在指定的返回值起始地址中
,已知这个返回值的长度,但并不知道返回类型
体系是Intel 64位机,编译器是gcc。
多谢各位大侠了!
#include <stdio.h>
struct args {
int a;
float b;
};
int func(int a, float b) {
printf("a = %d, b = %f\n", a, b);
return a;
}
int main() {
// Now we know a function pointer 'func' but we don't know the argument
list
// And we have a pointer of struct args but we don't know it's member
either
// Q: How can we call 'func' ?
struct args arg_list;
arg_list.a = 5;
arg_list.b = 6.5;
void *arg_list_p = &arg_list;
size_t arg_list_len = sizeof(struct args);
int ret;
size_t ret_sz = sizeof(int);
void *ret_p = &ret;
// You have: the function pointer "func", the start address of arguments
"arg_list_p",
// the size of the arguments "arg_list_len", the return address of
"ret_p"
// the size of the return value "ret_sz"
// Add your code here
}
感谢 下一个往站ex 网友的回答。但我没有描述清楚我的问题。 我的目的是,用汇编实现调用,而不是func本身。
我实现了一段 AT&T 格式的汇编码,但是一直有segment fault:
__asm__ __volatile__(
"push %%rbp;"
"mov %%rsp, %%rbp;"
"push %%rsi;"
"push %%rdi;"
"sub %3, %%rsp;"
"mov %%rsp, %%rdi;"
"mov %4, %%rsi;"
"mov %3, %%rcx;"
"rep movsb;"
"call *%2;"
"mov %%rax, %1;"
"add %3, %%rsp;"
"pop %%rdi;"
"pop %%rsi;"
"mov %%rbp, %%rsp;"
"pop %%rbp;"
: "=r"(ret)
: "r"(ret), "r"(func_ptr), "r"(arg_list_len), "r"(arg_list_p)
: "memory"
);
出错原因可能是SP赋值不正确 展开
2个回答
展开全部
1。对于“汇编调用”:
我知道你要调用func,而不是它本身,但如果这个函数比较复杂时是必须用逆向先分析func这个函数,然后再确定参数列表和返回值的……
2。对于你的内联汇编的代码:
这里到底要不要用add %3, %%rsp;还是一个问题,因为要看函数使用的是什么调用标准,有标准C的,VB的,Pascal的,包括fastcall,stdcall,cdecl等……
3。对于“知道函数参数的起始地址和长度”:
这个的话,除了参数中有字符数组和直接结构体什么的,所有的基本变量基本都是每8字节(64位)一个,并且Intel一般都用bigendian的,也就是说,在内存中 01 02 03 04 05 06 07 08 读入寄存器后会变为: 0x0807060504030201
所以说对于简单的函数,用8字节一个参数来做就好了……
而对于有字符数组什么的就必须用逆向分析了……
这个……只能进行逆向分析了……
反正你知道了函数的地址和长度……
就是你把编译为机器码的程序用反编译工具翻译成汇编,然后分析一下就好了,C语言的汇编还是比较简单……
比如这个函数:
int func(int a, int* b) {
// float要用到CPU的FPU,指令记不得,要查下
// 为了简单就改为int*
printf("a = %d, b = %d\n", a, *b);
return a;
}
编译成机器码后,反编译,如果不加优化,一般都会这样:
(假设函数入口地址为0400000h)
sub_0400000:
push rbp
mov rbp,rsp ; C函数参数度取使用堆栈式
; 参数在内存中这样: |...| a | b | ... |
; 由于是64位,故8字节对齐
mov rax,[rbp+8] ; rax = *(rbp+8) // 这里就是 rax = a
push rsi
mov rsi,[rbp+16] ; rsi = *(rbp+16) // rsi = b
; 调用C函数都是这样堆栈式,最后一个参数最先入栈
push [rsi]
push rax
push "a = %d, b = %d\n" ; 这里是便于理解,实际上是push这个字符串常量的指针
call printf ; printf("a = %d, b = %d\n",rax,*rsi)
add rsp,24 ; 平衡堆栈,用了3个参数,要还原3*8=24字节,但根据函数类型的不同去平衡,像调用VB的函数就不需要平衡堆栈……
; 还原数据
mov rsp,rbp
pop rsi
pop rbp
; 一般返回数据都用rax装载
mov rax,[rbp+8] ;rax=a
ret ; return rax
想调用未知参数列表的函数就是把以上过程倒过来,看着汇编把C的代码写出来……
破解注册码什么也是这样玩的……
实际上你可以直接用反编译的软件,比如IDA,直接自动分析,它反编译的虽然是汇编,但参数列表还是大部分都显示的……
但是,当编译器加优化大部分情况就必须自己分析了,因为:
int func(int a, int* b) {
printf("a = %d, b = %d\n", a, *b);
return a;
}
在优化情况下可能为(直接用寄存器传递数据):
sub_040000:
push rdx
mov rdx,rax
push rax
push rbx
push "a = %d, b = %d\n"
call printf
mov rax,rdx
pop rdx
ret
其实像www.pediy.com看雪学院有不少这方面的教程……
我知道你要调用func,而不是它本身,但如果这个函数比较复杂时是必须用逆向先分析func这个函数,然后再确定参数列表和返回值的……
2。对于你的内联汇编的代码:
这里到底要不要用add %3, %%rsp;还是一个问题,因为要看函数使用的是什么调用标准,有标准C的,VB的,Pascal的,包括fastcall,stdcall,cdecl等……
3。对于“知道函数参数的起始地址和长度”:
这个的话,除了参数中有字符数组和直接结构体什么的,所有的基本变量基本都是每8字节(64位)一个,并且Intel一般都用bigendian的,也就是说,在内存中 01 02 03 04 05 06 07 08 读入寄存器后会变为: 0x0807060504030201
所以说对于简单的函数,用8字节一个参数来做就好了……
而对于有字符数组什么的就必须用逆向分析了……
这个……只能进行逆向分析了……
反正你知道了函数的地址和长度……
就是你把编译为机器码的程序用反编译工具翻译成汇编,然后分析一下就好了,C语言的汇编还是比较简单……
比如这个函数:
int func(int a, int* b) {
// float要用到CPU的FPU,指令记不得,要查下
// 为了简单就改为int*
printf("a = %d, b = %d\n", a, *b);
return a;
}
编译成机器码后,反编译,如果不加优化,一般都会这样:
(假设函数入口地址为0400000h)
sub_0400000:
push rbp
mov rbp,rsp ; C函数参数度取使用堆栈式
; 参数在内存中这样: |...| a | b | ... |
; 由于是64位,故8字节对齐
mov rax,[rbp+8] ; rax = *(rbp+8) // 这里就是 rax = a
push rsi
mov rsi,[rbp+16] ; rsi = *(rbp+16) // rsi = b
; 调用C函数都是这样堆栈式,最后一个参数最先入栈
push [rsi]
push rax
push "a = %d, b = %d\n" ; 这里是便于理解,实际上是push这个字符串常量的指针
call printf ; printf("a = %d, b = %d\n",rax,*rsi)
add rsp,24 ; 平衡堆栈,用了3个参数,要还原3*8=24字节,但根据函数类型的不同去平衡,像调用VB的函数就不需要平衡堆栈……
; 还原数据
mov rsp,rbp
pop rsi
pop rbp
; 一般返回数据都用rax装载
mov rax,[rbp+8] ;rax=a
ret ; return rax
想调用未知参数列表的函数就是把以上过程倒过来,看着汇编把C的代码写出来……
破解注册码什么也是这样玩的……
实际上你可以直接用反编译的软件,比如IDA,直接自动分析,它反编译的虽然是汇编,但参数列表还是大部分都显示的……
但是,当编译器加优化大部分情况就必须自己分析了,因为:
int func(int a, int* b) {
printf("a = %d, b = %d\n", a, *b);
return a;
}
在优化情况下可能为(直接用寄存器传递数据):
sub_040000:
push rdx
mov rdx,rax
push rax
push rbx
push "a = %d, b = %d\n"
call printf
mov rax,rdx
pop rdx
ret
其实像www.pediy.com看雪学院有不少这方面的教程……
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询