PDA

View Full Version : Practical Appcall examples


Hex Blog
January 18th, 2010, 07:17
Last week we introduced the new Appcall feature ("http://hexblog.com/2010/01/introducing_the_appcall_featur_1.html") in IDA Pro 5.6. Today we will talk a little about how it's implemented and describe some of the uses of Appcall in various scenarios.</p>How Appcall works

Given a function with a correct prototype, the Appcall mechanism works like this:

Save the current thread context
Serialize the parameters (we do not allocate memory for the parameters, we use the debuggee's stack)
Modify the input registers in question
Set the instruction pointer to the beginning of the function to be called
Adjust the return address so it points to a special area where we have a breakpoint (we refer to it as control breakpoint)
Resume the program and wait until we get an exception or the control breakpoint (inserted in the previous step)Deserialize back the input (only for parameters passed by reference) and save the return value
In the case of a manual Appcall, the debugger module will do all but the last two steps, thus giving you a chance to debug interactively the function in question.
When you encounter the control breakpoint:
http://hexblog.com/ida_pro/pix/appcall_manual_control.gif

you can issue the CleanupAppcall() IDC command to restore the previously saved thread context and resume your debugging session. Using the debuggee functions

Sometimes it is useful to call certain functions from inside your debuggee's context:

Functions that you identified as cryptographic functions: encrypt/decrypt/hashing functions
Explicitly call not-so-popular functions: instead of waiting the program to call a certain function, simply call it directly
Change the program logic: by calling certain debuggee functions it is possible to change the logic and the internal state of the program
Extend your program: since Appcall can be used inside the condition expression of a conditional breakpoint, it is possible to extend applications that way
Fuzzing applications: easily fuzz your program on a function level...
Let's take a program that contains a decryption routine that we want to use:

http://hexblog.com/ida_pro/pix/appcall_xdecrypt.gif

In IDC, you can do something like:
Code:
auto s_in = "SomeEncryptedBuffer", s_out = strfill(SizeOfBuffer);
decrypt_buffer(&s_in, &s_out, SizeOfBuffer);
Or in Python:
Code:
# Explicitly create the buffer as a byref object
s_in = Appcall.byref("SomeEncryptedBuffer"
# Buffers are always returned byref
s_out = Appcall.buffer(" ", SizeOfBuffer)
# Call the debuggee
Appcall.decrypt_buffer(s_in, s_out, SizeOfBuffer)
# Print the result
print "decrypted=", s_out.value
Function level fuzzing

Instead of generating input strings and passing them to the application as command line arguments, input files, etc...it is also possible to test the application on a function level using Appcall.
It is sufficient to find the functions we want to test, give them appropriate prototypes and Appcall each one of these functions with the desired set of (malformed) input.
Code:
def fuzz_func1():
"""
Finds functions with one parameter that take a string buffer and tries to see if one
of these functions will crash if a malformed input was passed
"""

# prepare functions search criteria
tps = ['LPCWSTR', 'LPCSTR', 'char *', 'const char *', 'wchar_t *']
tpsf = [1 , 0 , 0 , 0 , 1]
pat = r'\((%s)\s*\w*\)' % "|".join(tps).replace('*', r'\*')

# set Appcall options
old_opt = Appcall.set_appcall_options(Appcall.APPCALL_DEBEV)

# Enumerate all functions
for x in Functions():
# Get the type string
t = GetType(x)
if not t:
continue
# Try to parse its declaration
t = re.search(pat, t)
if not t:
continue

# Check if the parameter is a unicode string or not
is_unicode = tpsf[tps.index(t.group(1))]


# Form the input string: here we can generate mutated input
# and keep on looping until our input pool for this function is exhausted.
# For demonstration purposes only one string is passed to the Appcalled functions
s = "A" * 1000

# Do the Appcall but protect it with try/catch to receive the exceptions
try:
# Create the buffer appropriately
if is_unicode:
buf = Appcall.unicode(s)
else:
buf = Appcall.buffer(s)
print "%x: calling. unicode=%d" % (x, is_unicode)
# Call the function in question
r = Appcall[x](buf)
except OSError, e:
exc_code = idaapi.as_uint32(e.args[0].code)
print "%X: Exception %X occurred @ %X. Info: <%s>\n" % (x,
exc_code, e.args[0].ea, e.args[0].info)
# stop the test
break
except Exception, e:
print "%x: Appcall failed!" % x
break
# Restore Appcall options
Appcall.set_appcall_options(old_opt)
It is important to enable the APPCALL_DEBEV Appcall option in order to retrieve the last exception that occurred during the Appcall.Injecting Libraries in the Debuggee

To inject libraries in the debuggee simply Appcall LoadLibrary():
Code:
loadlib = Appcall.proto("kernel32_LoadLibraryA", "int __stdcall loadlib(const char *fn);"
hmod = loadlib("dll_to_inject.dll"
Set/Get the last error

To retrieve the last error value we can either parse it manually from the TIB or Appcall the GetLastError() API:
Code:
getlasterror = Appcall.proto("kernel32_GetLastError", "DWORD __stdcall GetLastError();"
print "lasterror=", getlasterror()
Similarly we can do the same to set the last error code value:
Code:
setlasterror = Appcall.proto("kernel32_SetLastError", "void __stdcall SetLastError(int dwErrCode);"
setlasterror(5)
Retrieving the command line value

To retrieve the command line of your program we can either parse it from the PEB or Appcall the GetCommandLineA() API:
Code:
getcmdline = Appcall.proto("kernel32_GetCommandLineA", "const char *__stdcall getcmdline();"
print "command line:", getcmdline()
Setting/Resetting events

Sometimes the debugged program may deadlock while waiting on a semaphore or an event. You can manually release the semaphore or signal the event.Killing a thread is possible too:
Code:
releasesem = Appcall.proto("kernel32_ReleaseSemaphore",
"BOOL __stdcall ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount);"

resetevent = Appcall.proto("kernel32_SetEvent",
"BOOL __stdcall SetEvent(HANDLE hEvent);"

termthread = Appcall.proto("kernel32_TerminateThread",
"BOOL __stdcall TerminateThread(HANDLE hThread, DWORD dwExitCode);"
Change the debuggee's virtual memory configuration

It is possible to change a memory page's protection. In the following example we will change the PE header page protection to execute/read/write (normally it is read-only):
Code:
virtprot = Appcall.proto("kernel32_VirtualProtect",
"BOOL __stdcall VirtualProtect(LPVOID addr, DWORD sz, DWORD newprot, PDWORD oldprot);"
r = virtprot(0x400000, 0x1000, Appcall.Consts.PAGE_EXECUTE_READWRITE, Appcall.byref(0));
print "VirtualProtect returned:", r
RefreshDebuggerMemory()
And if you need to allocate a new memory page:
Code:
virtalloc = Appcall.proto("kernel32_VirtualAlloc",
"int __stdcall VirtualAlloc(int addr, SIZE_T sz, DWORD alloctype, DWORD protect);"
m = virtualalloc(0, Appcall.Consts.MEM_COMMIT, 0x1000, Appcall.Consts.PAGE_EXECUTE_READWRITE)
RefreshDebuggerMemory()
Load a library and call an exported function

With Appcall it is also possible to load a library, resolve a function address and call it. Let us illustrate with an example:
Code:
def get_appdata():
hshell32 = loadlib("shell32.dll"
if hshell32 == 0:
print "failed to load shell32.dll"
return False
print "%x: shell32 loaded" % hshell32

# make sure to refresh the debugger memory after loading a new library
RefreshDebuggerMemory()

# resolve the function address
p = getprocaddr(hshell32, "SHGetSpecialFolderPathA"
if p == 0:
print "shell32.SHGetSpecialFolderPathA() not found!"
return False

# create a prototype
shgetspecialfolder = Appcall.proto(p,
"BOOL SHGetSpecialFolderPath(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);"
print "%x: SHGetSpecialFolderPath() resolved..."

# create a buffer
buf = Appcall.buffer("\x00" * 260)

# CSIDL_APPDATA = 0x1A
if not shgetspecialfolder(0, buf, 0x1A, 0):
print "SHGetSpecialFolderPath() failed!"
else:
print "AppData Path: >%s<" % Appcall.cstr(buf.value)
return True
Closing words

Appcall has a variety of applications, hopefully it will be handy while solving your day to day reversing problems.For your convenience, please download this ("http://hexblog.com/ida_pro/files/appcall_prototypes.py") script containing the prototypes of the API functions used in this blog entry.

Please send your suggestions/questions to support@hex-rays.com

http://hexblog.com/2010/01/practical_appcall_examples_1.html