A common technique for blackbox penetration testing of a binary application is intercepting function calls. This technique helps the pentester to properly understand how the application works and to manipulate application data.
In most cases, it is pretty easy to intercept a function call: the application calls a function from a shared library (DLL) and you just need to find its address in the DLL’s export address table and breakpoint on it.
But it may happen that your target function is from a statically linked library, which means that you cannot find its address by name in the export table. So how to find the target function’s address in this situation?
In our case, we have a Windows executable statically linked with OpenSSL and we want to intercept and modify the TLS encrypted traffic which is handled by the SSL_write function from OpenSSL.
However, the same idea can be applied for other operating systems and libraries.
Even if it may sound complicated, it is a lot easier to find the function code by searching the address space for a “signature” of that function. This means to search the memory for a sequence of bytes that will match your function.
We used Immunity Debugger but you can use any other debugger.
Steps to find the target function’s bytes:
1. Compile or download the compiled library. We downloaded the compiled OpenSSL library from this link: http://slproweb.com/products/Win32OpenSSL.html
2. Load the DLL (ssleay32.dll) or an executable that uses that library (openssl.exe) in the debugger and run to reach the entry point – to make sure imported libraries are loaded into memory:
3. Press Alt + E (for Immunity Debugger) to view the “Executable modules” window, find and click “ssleay32”, right click it and click “View names” or press Ctrl + N to see the imported and exported functions:
4. In the opened window, type “SSL_write” to search the function:
5. Double click it and you will get the function code:
Creating the signature
This is the difficult part. From the function body you must choose a sequence of bytes from the function code that:
- Is long enough to find it only one time or maximum a few times (in the application that statically links the library)
- Is exactly the same, independent of memory locations
If you select a simple instruction like this, you may find it in too many places:
10023D40 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
If you try to find a “call” or a “push offset” you will fail because the offsets are now located in the address space of the executable:
10023D4F 68 C8550310 PUSH SSLEAY32.100355C8 ; ASCII ".\ssl\ssl_lib.c" 10023D60 E8 ABDA0000 CALL <JMP.&LIBEAY32.#252_ERR_put_error>
So you need to find a good sequence. A good example would be:
10023D54 68 14010000 PUSH 114 10023D59 68 D0000000 PUSH 0D0 10023D5E 6A 14 PUSH 14
It is ok because it is long enough, it does not use offsets, only immediate values and it is pretty difficult to find this code in other places because the probability to push exactly these values on the stack, in this order, is really small.
So, the signature will be the selected bytes:
68 14 01 00 00 68 D0 00 00 00 6A 14
Finding the target function
Now that we have the function signature is easy to find the function in the statically linked library in our application, just make sure you search in your executable module.
Right click > Search for > Binary string and type function signature
Or an easier way:
Right click > Search for > Sequence of commands
PUSH 114 PUSH 0D0 PUSH 14
You will see a function that looks exactly the same or it is very close to your original function. Note that it may happen to find other functions, make sure you found the function you were looking for.
Breakpointing at the start of the function (SSL_write) will reveal the value of its parameters, respectively the plain text string before encryption:
In this article we showed how to find the address of a statically linked function inside an executable with the purpose of intercepting it in the debugger.