Extending the IDA Script Language
A First Stab
Written by Quine
wvo_quine@hotmail.com

Introduction

Well, quite a long time ago I promised that I would write some extensions to IDA. Well, here's the first. mammon, who, by the way, knows a lot about IDA and has some great info on his web page, mentioned to me that he was looking for a way to display bitmap resources and the like from within IDA. This was in connection with his resource parser (written in IDC) for IDA. This, in turn, reminded me of an IDC extension I had wanted to do. The goal, then, is to add a command to IDC which lets you run another program. This would solve mammon's problem because IDC gives you the resources to create a bitmap file out of the resource data, but no way to run a viewer app. I want to add a command of the following form:

success Exec (char CommandLine);

This will simply execute the command line as a separate process. Since it's not possible as far as I know to implement this as a straightforward IDC function, we're going to have to hack ida.wll. I am in this essay assuming that you have the demo version with the load and file size cracks I have outlined previously on this site. Cracking the full version would just be a matter of recomputing some offsets. I'll leave that as an exercise to those who have the full version. Ok, here goes.

Tools required
IDA Pro 3.7 Demo
A Hex Editor

Target's URL/FTP
IDA Homepage in Russia (disabled)

Essay

If you look at address 48BDF4 in ida.wll you'll see an array of structures that starts out like this :-

dd offset aSegbyname
dd offset sub_40E914
dd offset unk_48CBD8

Let's have a look in the IDA SDK and see if we can find the declaration for this structure (even though it's pretty easy to figure out). You'll find it in expr.h :-

typedef struct {		/* Element of functions table */
  const char near *name;	/* Name of function */
  error_t (*fp)(value_t *argv,value_t *res); /* Pointer to the Function */
  const char near *args;	/* Type of arguments. Terminated with 0 */
				/* VT_WILD means a function with arbitrary
				   number of arguments. Actual number of
				   arguments will be passed in res->num */
} extfun_t;

Now we need to find out what value_t is :-

typedef struct {	/* Result of expression */
  char vtype;		/* Type			*/
#define  VT_STR		1
#define  VT_LONG	2
#define  VT_FLOAT	3
#define  VT_WILD	4	// used only in function arg type declarations
  union {
    char *str;		/* T_str  	*/
    long num;		/* T_long 	*/
    ushort e[6];	/* T_flt	*/
  };
} value_t;

Ok, so the structure specifies first, a pointer to the IDC command name, second, a pointer to the function that implements the command, and third, a pointer to information about how many what type of arguments the command takes. What we want to do is hijack one of the IDC commands and use it for own purposes. However, doing so would seem to be a problem because any other IDC script that uses the command we hijack will not work. I at first thought that I would just take one that wasn't used that often, but then I had a better idea. We just need to pass Exec one argument, the command line, but what if we take over a command that takes two arguments? For example,

success MakeName        (long ea,char name);

Now, an ea (Effective Address) that will pretty much never be passed to this function is FFFFFFFE (FFFFFFFF probably will be passed by buggy scripts because it is the value of BADADDR). So, for Exec, we'll take over MakeName and pass it FFFFFFFE as the first argument and the command line as the second. To do so, we'll re-route the function pointer in the array of extfun_t's to point to a function we'll add to the end of the code section. Our new function will first check to see if FFFFFFFE is passed as the first argument and if it isn't it'll immediately jump to the real MakeName function. So, now all we have to do is write our new function.

To run another process, the Win32 API provides the CreateProcess function. It takes 9 arguments, two of which are pointers to structures for which the caller has to allocate the memory. This is annoying because it means there's more we have to deal with. I thought about using the Borland run-time library function system, which takes only a command line. Unfortunately, system doesn't return until the new process terminates, so IDA is left hanging while our viewer or whatever is up. Further, when it does return, IDA won't recognize the mouse anymore. Fuck that. CreateProcess it is. I trust that everyone has got some sort of Win32 API reference (you can find them everywhere), so I won't go over CreateProcess, STARTUPINFO, or PROCESS_INFORMATION (these are the two structures we're going to have to create) in detail.

Alright, let's first work out how to get the parameters that are passed to the IDC function. Remember, according to the function definition in extfun_t, each idc function takes two pointers to value_t structures. The first pointer is actually a pointer to an array of value_t structures which has as many elements as there are arguments for the particular command. The second pointer is a pointer to a single value_t struct which will contain the result of the IDC command. Take a look at the real MakeName function.

idc_MakeName    proc near
                push    ebx
                mov     ebx, eax ; eax points to arg. value_t array
                push    esi
                mov     esi, edx ; edx points to result value_t
                mov     edx, [ebx+0Eh] ; the second argument
                mov     eax, [ebx+1] ; the first argument
                call    @set_name$qqrulpxc ; set_name(ulong,char *)
                mov     [esi+1], eax ; the result
                xor     eax, eax
                pop     esi
                pop     ebx
                retn
idc_MakeName    endp

This should be fairly straightforward. The only quirky thing is that every function (just about) in ida.wll uses the fastcall convention (see my first IDA essay) so the first three arguments always come in through registers rather than through the stack. One other thing I should mention is that the length of a value_t object is 13d bytes. One byte for the first member, and then 12d bytes for the union. A union is always as long as its longest member, which in this case is the 6 element array of words used for floating point arguments. That's why the second IDC argument is at offset 0Eh.

OK, the last thing to worry about is the call to CreateProcess. Most of the arguments to it can just be null because we don't want it to do anything fancy. All we're going to do is pass it the command line and two creation flags, CREATE_NEW_PROCESS_GROUP and CREATE_NEW_CONSOLE. The second flag will enable us to run separate console apps. However, we've got to allocate 10h byte for the PROCESS_INFORMATION structure and 44h bytes for the STARTUPINFO struct. This will be done on the stack which is infinitely easier than allocating from the free store. Furthermore, we'll zero out both of them with a call to memset and then all we need to fill in is the length of STARTUPINFO in the first dword of STARTUPINFO. The rest of the members can safely be left at zero. So, here's our code :-

; is it a call to Exec or MakeName?
cmp [eax+1], 0fffffffeh ; 81 7c 20 01 fe ff ff ff
je exec                 ; 74 05
jmp MakeName            ; e9 d3 60 f8 ff
exec:
; set up stack and reserve 54h bytes for our structs
push ebp                ; 55
mov ebp, esp            ; 8b ec
sub esp, 54h            ; 81 c4 ac ff ff ff
; save esi and ebx
push esi                ; 56
push ebx                ; 53
; ebx = *res
mov ebx, edx            ; 89 d3
; esi = *CommandLine
mov esi, [eax+0eh]      ; 8b 70 0e
; zero out the structs
push 54h                ; 6a 54
push 0                  ; 6a 00
lea eax, [ebp-54h]      ; 8d 85 ac ff ff ff
push eax                ; 50
call _memset            ; e8 73 34 fa ff
; clean stack after memset
add esp, 0ch            ; 83 c4 0c
; move size of STARTUPINFO into struct
mov [ebp-44h], 44h      ; c7 85 bc ff ff ff 44 00 00 00
; this is where we start pushing the args to CreateProcess
; ptr to PROCESS_INFORMATION
lea eax, [ebp-54h]      ; 8d 85 ac ff ff ff
push eax                ; 50
; ptr to STARTUPINFO
add eax, 10h            ; 83 c0 10
push eax                ; 50
; unused
push 0                  ; 6a 00
push 0                  ; 6a 00
; our creation flags
push 210h               ; 68 10 02 00 00
; unused
push 0                  ; 6a 00
push 0                  ; 6a 00
push 0                  ; 6a 00
; ptr to CommandLine
push esi                ; 56
; unused
push 0                  ; 6a 00
call j_CreateProcess    ; e8 11 fe ff ff
; stick the return value into res.num
mov [ebx+1], eax        ; 89 43 01
; eax = 0 means function was successful
xor eax, eax            ; 33 c0
; clean up stack and that's it
pop ebx                 ; 5b
pop esi                 ; 5e
mov esp, ebp            ; 8b e5
pop ebp                 ; 5d
ret                     ; c3

The relative references are set up assuming that the code starts at VA 488886h, which is file offset 87E86h, which is right after the code I inserted for the load crack. If you want to put it somewhere else or you want to patch the full version, make sure to correct the one relative jump to MakeName and the calls to memset and CreateProcess.

The last thing to do is to patch the extfun_t array. The pointer to the real MakeName function is at 48BE1C, so put 00488886 into that location (in big endian order, of course).

Now, typing MakeName (0xfffffffe, "notepad.exe") each time we want to use Exec in a script is annoying and makes the code less readable. So, let's use a preprocessor define to fix it up :-

#define Exec(x) MakeName(0xfffffffe, x)

Put this into idc.idc somewhere and you can use Exec as I originally wanted :-

Exec("notepad.exe");

So, let's test it. Create the following IDC script and run it:

#include <idc.idc>
#define Exec(x) MakeName(0xfffffffe, x)

static main () {
Exec("notepad.exe");
}

Look! There's the Notepad. It works. That's it.

One more thing. You can pass whatever you want on the command line, but remember to escape special characters (quotes, backslashes, etc.) with a backslash.

Quine, January 1998