1

I want to reverse a stripped x86 sample that uses a static library.
So far I could identify the library itself and narrow the version of the library used.

Now I want to use Ghidra's Function ID plugin to identify all the library functions within the sample.
The function signatures depend on the instructions of the function - so for them to match, the instructions need to match.

The instructions of a function depend heavily on the compiler, its version & options that were used.

I have compiled the library myself using gcc and compared the assembly output - and they differ.

Do you have some suggestions on what to try / things to look out for / clues that might lead to the original compiler used?
I have tried compiling the source with some of the compilers listed on godbolt.org, but none of them seem similar to the original disassembly.

For example, here is one of the functions from the original sample and from one of my gcc-compiled version:

Function within the original sample:
Function within my gcc-compiled version of the library:

Relevant source code of the function:

#define MBEDTLS_SSL_TRANSPORT_DATAGRAM          1   /*!< DTLS     */
#define MBEDTLS_SSL_MINOR_VERSION_0             0   /*!< SSL v3.0 */
#define MBEDTLS_SSL_MINOR_VERSION_1             1   /*!< TLS v1.0 */
#define MBEDTLS_SSL_MINOR_VERSION_2             2   /*!< TLS v1.1 */
#define MBEDTLS_SSL_MINOR_VERSION_3             3   /*!< TLS v1.2 */
#define MBEDTLS_SSL_PROTO_DTLS



struct mbedtls_ssl_config
{
    unsigned int transport : 1;
};

struct mbedtls_ssl_context {
    int minor_ver;
    const mbedtls_ssl_config *conf;
};

const char *mbedtls_ssl_get_version( const mbedtls_ssl_context *ssl )
{
#if defined(MBEDTLS_SSL_PROTO_DTLS)
    if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
    {
        switch( ssl->minor_ver )
        {
            case MBEDTLS_SSL_MINOR_VERSION_2:
                return( "DTLSv1.0" );

            case MBEDTLS_SSL_MINOR_VERSION_3:
                return( "DTLSv1.2" );

            default:
                return( "unknown (DTLS)" );
        }
    }
#endif

    switch( ssl->minor_ver )
    {
        case MBEDTLS_SSL_MINOR_VERSION_0:
            return( "SSLv3.0" );

        case MBEDTLS_SSL_MINOR_VERSION_1:
            return( "TLSv1.0" );

        case MBEDTLS_SSL_MINOR_VERSION_2:
            return( "TLSv1.1" );

        case MBEDTLS_SSL_MINOR_VERSION_3:
            return( "TLSv1.2" );

        default:
            return( "unknown" );
    }
}

Any hints and ideas are greatly appreciated, thank you!

  • This type of problem is closely related to compiler toolchain provenance, which is an area of active research. You should read some of the relevant papers published about this topic. – julian Jun 19 at 8:33
  • I'm not aware of any tricks to solve this problem unfortunately. In practice I sometimes compile a dummy binary using all the functions I expect my target to use and then open both and look for similarities. There are just too many parameters you'd have to figure out to get matching binaries. IDA has FLIRT signatures which somewhat try to handle slight differences but compiler output can drastically change. A dummy binary and the source can be enough to spot patterns, but it's still a manual task. – Johann Aydinbas Jun 20 at 15:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.