我们使用LocByName函数通过传入函数的名称来获取函数的地址(该地址是可以被调用的)。上面的代码同样可以通过下面的多行代码来实现同样的效果:
auto myfunc = LocByName("_my_func@8");
myfunc("hello", "world");
需要注意Appcall只在当前进程空间中有效,如果你想在另外的一个进程中执行,那么首先要切换到目标进程中。
Appcall 和 IDC
Appcall机制可以通过下面的函数在IDC中进行调用:
// Call application function
// ea - address to call
// type - type of the function to call. can be specified as:
// - declaration string. example: "int func(void);"
// - typeinfo object. example: GetTinfo(ea)
// - zero: the type will be retrieved from the idb
// ... - arguments of the function to call
// Returns: the result of the function call
// If the call fails because of an access violation or other exception,
// a runtime error will be generated (it can be caught with try/catch)
// In fact there is rarely any need to call this function explicitly.
// IDC tries to resolve any unknown function name using the application labels
// and in the case of success, will call the function. For example:
// _printf("hello\n")
// will call the application function _printf provided that there is
// no IDC function with the same name.
anyvalue Appcall(ea, type, ...);
Appcall IDC函数需要你传递一个函数的地址,函数类型信息和参数(如果有)。
auto p = LocByName("_printf");
auto ret = Appcall(p, GetTinfo(p), "Hello %s\n", "world");
现在我们已经知道如何来调用一个已经知道函数类型的函数,现在假设有另外的一个函数我们不知道函数类型以及参数类型:
通过引用传递参数(Passing arguments by reference)
为了通过引用传递参数,只需要使用C语言中的&符号进行即可。
例如调用下面的函数:
void ref1(int *a)
{
if (a == NULL)
return;
int o = *a;
int n = o + 1;
*a = n;
printf("called with %d and returning %d\n", o, n);
}
我们可以在IDC中使用下面的代码实现:
auto a = 5;
Message("a=%d", a);
ref1(&a);
Message(", after the call=%d\n", a);
调用一个携带了一个字符串参数并且进行修改的函数:
/* C code */
int ref2(char *buf)
{
if (buf == NULL)
return -1;
printf("called with: %s\n", buf);
char *p = buf + strlen(buf);
*p++ = '.';
*p = '\0';
printf("returned with: %s\n", buf);
int n=0;
for (;p!=buf;p--)
n += *p;
return n;
}
我们需要创建一个缓冲区并且将它传递给被调用的函数,因为代码如下所示:
auto s = strfill('\x00', 20); // create a buffer of 20 characters
s[0:5] = "hello"; // initialize the buffer
ref2(&s); // call the function and pass the string by reference
if (s[5] != ".")
Message("not dot\n");
else
Message("dot\n");
__usercall调用约定
Appcall函数可能没有一个标准的调用约定,例如由汇编语言编写的子函数可能需要通过各种寄存器来传递参数等等。
一种方法是通过_usercall调用约定来描述你的函数:
参考如下的函数原型:
/* C code */
// eax = esi - edi
int __declspec(naked) asm1()
{
__asm
{
mov eax, esi
sub eax, edi
ret
}
}
从IDC函数中调用将会是下面的代码:
auto p = ParseType("int __usercall asm1<eax>(int a<esi>, int b<edi>);", 0);
auto r = Appcall(LocByName("_asm1"), p, 5, 2);
Message("The result is: %d\n", r);
不定参数函数(Variable argument functions)
C代码:
int va_altsum(int n1, ...)
{
va_list va;
va_start(va, n1);
int r = n1;
int alt = 1;
while ( (n1 = va_arg(va, int)) != 0 )
{
r += n1*alt;
alt *= -1;
}
va_end(va);
return r;
}
IDC代码:
auto result = va_altsum(5, 4, 2, 1, 6, 9, 0);
调用函数可能引发异常
在使用Appcall的过程中可能会触发异常。为了捕获这些异常你可以在IDC程序中使用try/catch结构:
auto e;
try
{
AppCall(some_func_addr, func_type, arg1, arg2);
// Or equally:
// some_func_name(arg1, arg2);
}
catch (e)
{
// Exception occured .....
}
异常结构“e”将会包含如下的数据区段:
描述(description):在调试器执行Appcall的过程中产生的异常描述信息;
函数(func):发生异常的IDC函数的名称;
行号(line):在脚本程序中发生错误行的行号;
错误编号(qerrno):最后一个错误的内部编号。
例如你可能会得到如下的信息:
description: "Appcall: The instruction at 0x401F93 referenced memory at 0x5.
The memory could not be read"
file: "<internal>"
func: "___idc0"
line: 4
qerrno: 92
在一些条件下异常结构有可能会包含更多的信息。