In this blog entry we are going to talk about the new Appcall feature that was introduced in IDA Pro 5.6.
Briefly, Appcall is a mechanism used to call functions inside the debugged program from the debugger or your script as if it were a built-in function. If you’ve used GDB (call command), VS (Immediate window), or Borland C++ Builder then you’re already familiar with such functionality.
(Screenshot showing how we called three functions (printf, MessageBoxA, GetDesktopWindow) using IDC syntax)
Before diving in, please keep in mind that this blog entry is a short version of the full Appcall reference found here.
To start with, we explain the basic concepts of Appcall using the IDC command line:
It can be called by simply typing:
As you notice, we invoked an Appcall by simply treating _printf as if it were a built-in IDC function.
If you have a function with a mangled name or containing characters that cannot be used as an identifier name in the IDC language:
then issue the Appcall with this syntax:
We use the LocByName function to get the address of the function given its name, then using the address (which is callable) we issue the Appcall. In two steps this can be achieved with:
auto myfunc = LocByName("_my_func@8"); myfunc("hello", "world");
Please note that Appcalls take place in the context of the current thread. If you want to execute in a different thread then switch to the desired thread first.
The Appcall mechanism can be used from IDC through the following function:
// 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, ...);
The Appcall IDC function requires you to pass a function address, function type information and the parameters (if any):
auto p = LocByName("_printf"); auto ret = Appcall(p, GetTinfo(p), "Hello %s\n", "world");
We’ve seen so far how to call a function if it already has type information, now suppose we have a function that does not:
Before calling this function with Appcall() we need first to get the type information (stored in a typeinfo object) by calling ParseType() and then pass the function ea and type to Appcall():
auto p = ParseType("long __stdcall FindWindow(const char *cls, const char *wndname)", 0); Appcall(LocByName("user32_FindWindowA"), p, 0, "Untitled - Notepad");
Note that we used ParseType() function to construct a typeinfo object that we can pass to Appcall(), however it is possible to permanently set the prototype of a function, thus:
SetType(LocByName("user32_FindWindowA"), "long __stdcall FindWindow(const char *cls, const char *wndname)");
To pass function arguments by reference, it suffices to use the & symbol as in the C language.
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); }
We can use this code from 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; }
We need to create a buffer and pass it, thus:
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");
It is possible to Appcall functions with non standard calling conventions, such as routines written in assembler that expect parameters in various registers and so on.
One way is to describe your function with the __usercall calling convention.
Consider this function:
/* C code */ // eax = esi - edi int __declspec(naked) asm1() { __asm { mov eax, esi sub eax, edi ret } }
And from 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);
In 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; }
And in IDC:
auto result = va_altsum(5, 4, 2, 1, 6, 9, 0);
Exceptions may occur during an Appcall. To capture them, you can use the try/catch in IDC:
auto e; try { AppCall(some_func_addr, func_type, arg1, arg2); // Or equally: // some_func_name(arg1, arg2); } catch (e) { // Exception occured ..... }
The exception object “e” will be populated with the following fields:
For example, you could get something like this:
description: "Appcall: The instruction at 0x401F93 referenced memory at 0x5. The memory could not be read" file: "<internal>" func: "___idc0" line: 4 qerrno: 92
In some cases the exception object will contain more information.
Appcall can be configured with SetAppcallOptions(), by passing the following option(s):
It is possible to retrieve the Appcall options, change them and then restore them back. To retrieve the options use the GetAppcallOptions().
Please note that Appcall option is saved in the database so if you set it once it will retain its value as you save and load the database.
So far we’ve seen how to issue an Appcall and capture the result from the script, but what if we only want to setup the environment and manually step through a function?
This can be achieved with manual Appcall. The manual Appcall mechanism can be used to save the current execution context, execute another function in another context and then pop back the previous context and continue debugging from that point. Let us directly illustrate manual Appcall with a real life scenario:
To illustrate, let us take the ref1 function and call it with an invalid pointer:
Directly after doing that, IDA will switch to the function and from that point on we can debug:
When we reach the end of the function:
and trace beyond the return instruction, we expect to see something like this:
This is the control code that we use to determine the end of an Appcall. It is at this point that one should call CleanupAppcall() to return to the previous execution context:
We previously illustrated that we can capture exceptions that occur during an Appcall, but that is not enough if we want to learn more about the nature of the exception from the operating system point of view.
It would be better if we could somehow get the last debug_event_t that occured inside the debugger module. This is possible if we use the APPCALL_DEBEV option. Let us repeat the previous example but with the APPCALL_DEBEV option enabled:
auto e; try { SetAppcallOptions(APPCALL_DEBEV); // Enable debug event capturing ref1(6); } catch (e) { // Exception occured. This time "e" is populated with debug_event_t fields (check idd.hpp) }
And in this case, if we dump the exception object’s contents, we get these attributes:
can_cont: 1 code: C0000005h ea: 401F93h eid: 40h (from idd.hpp: EXCEPTION = 0x00000040 Exception) file: "" func: "___idc0" handled: 1 info: "The instruction at 0x401F93 referenced memory at 0x6. The memory could not be read" line: 4h pid: 123Ch ref: 6h tid: 1164h
The Appcall concept remains the same between IDC and Python, nonetheless Appcall/Python has a different syntax (using references, unicode strings, etc, etc…)
The Appcall mechanism is provided by idaapi module through the Appcall variable. To issue an Appcall:
Appcall.printf("Hello world!\n");
One can take a reference to an Appcall:
printf = Appcall.printf # ...later... printf("Hello world!\n");
findclose = Appcall["__imp__FindClose@4"] getlasterror = Appcall["__imp__GetLastError@0"] setcurdir = Appcall["__imp__SetCurrentDirectoryA@4"]
# pass an address name and Appcall.proto() will resolve it loadlib = Appcall.proto("__imp__LoadLibraryA@4", "int (__stdcall *LoadLibraryA)(const char *lpLibFileName);") # Pass an EA instead of a name freelib = Appcall.proto( LocByName("__imp__FreeLibrary@4"), "int (__stdcall *FreeLibrary)(int hLibModule);")
getmodulehandlew = Appcall.proto("__imp__GetModuleHandleW@4", "int (__stdcall *GetModuleHandleW)(LPCWSTR lpModuleName);") hmod = getmodulehandlew(Appcall.unicode("kernel32.dll"))
# Create a typed object (no address is associated yet) virtualalloc = Appcall.typedobj( "int __stdcall VirtualAlloc(int lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);") # Later we have an address, so we pass it: virtualalloc.ea = LocByName("kernel32_VirtualAlloc") # Now we can Appcall: ptr = virtualalloc(0, Appcall.Consts.MEM_COMMIT, 0x1000, Appcall.Consts.PAGE_EXECUTE_READWRITE)
Before we conclude (if you read so far;)), here’s a small script that can be used to initiate and terminate Appcalls using hotkeys. If you want to have this script load everytime you start IDA then put its contents in idc\ida.idc file.
Here’s a simple scenario where manual Appcalls can be handy:
If you want to temporary start tracing from the current cursor location then use Ctrl-Alt-F4 to start a manual Appcall. Use then Ctrl-Alt-F10 to return to previous execution context.
Remember, Appcall can do more than what is illustrated in this blog entry, make sure you refer to the Appcall manual for other advanced topics.