mvwhyatt mvwhyatt - 2 months ago 6
C Question

Need C compiler options to create to easy-to-reverse executable to teach reversing

I've written a simple C program with the goal to have students use OllyDbg reverse it and patch it to change how it executes. They only have to find a 3 and change it to a 5. The compiled executable is filled with difficult-to-follow startup calls that will thwart efforts for newbies to reverse it in Olly. I'd like to tell the compiler to remove all/most of that.

I'm using Visual Studio 2015 Pro, but I'm not a VS whiz, I don't know where to start.

Answer

As long as your main function is not accessing the standard C library and you only use Windows API calls, you can remove the c-runtime startup code with the following two linker switches:

/entry:<your_main_function> /nodefaultlib

In a full example using a simple console application (which I've assumed you are using) and a main function of "main", you could compile and link your code without the c-runtime library as follows:

cl /c main.c
link /entry:main /nodefaultlib /libpath:<path_to_libs> /subsystem:console main.obj kernel32.lib

Visual Studio has the equivalent options buried in your project dialogs if you don't want to manually run the command line tools.

This should produce a small, yet simple EXE. When your debugging session begins, you should start right inside your main function.

NOTE: The lack of standard IO may make outputting a number (which I assume you are doing) more difficult as you might need to write some routines yourself, such as getting the string length. However the only API calls you'll need to output to the console are the kernel32 GetStdHandle() and WriteConsole() functions. Keep in mind there is a wsprintf() API that is accessible in user32.lib which I'd also suggest using to keep your sample bare-bones but still allow you to build c-style formatted strings. Use wsprintf() to convert your number into an output prompt/string and use WriteConsole to send it to the console.

UPDATE: Below is a sample C program that outputs the results of adding two numbers without needing the standard-C library and thus the CRT startup code. This is a good reverse engineering sample to change one of the numbers to get a different result on the command line.

DISCLAIMER: I agree that wsprintf() is indeed an unsafe function due to its lack of checking buffer size and when mis-used can contribute to a buffer overflow. It is used here ONLY because it is built in to the Win32 API (user32.lib) and is a quick way to convert a number to a string just for this example.

As far as I know, the only C-style formatting functions built in to windows are the ANSI/WIDE versions of this function. The strsafe.lib functions seem to have a dependency on the standard library otherwise I would have used them. If you know any different, please let me know! For that reason, I recommend writing your own number to string conversion function or using someone else's if you do not use the standard-C library functions.

#include <Windows.h>

//quick-and-dirty string-length function
DWORD getStringLen(const char* pszStr)
{
    const char* p = pszStr;
    while(*p) ++p;
    return(p - pszStr);
}

//program entry point - note that int return value does propagate back to
//    ExitProcess() despite main() being the program's actual entry point;
//    the ret at the end of this function returns back to the loader, which
//    in turn calls ExitThread() with EAX; the return value can be
//    checked on the comamnd-line via: echo %ERRORLEVEL%
int main(void)
{
    DWORD dwNum1 = 5;
    DWORD dwNum2 = 3;
    DWORD dwResult = dwNum1+dwNum2;

    //build formatted output string
    //
    // NOTE: wsprintfA() is an unsafe function and is only used as an example
    //       DO NOT USE in production code!
    //
    #pragma warning(disable : 4995) //wsprintf is a depreciated function
    char szTemp[100];
    int iRet = wsprintfA(szTemp,"%u + %u = %u\n",dwNum1,dwNum2,dwResult);

    //get console stdandard output handle
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    if (INVALID_HANDLE_VALUE == hConsole)
    {
        iRet = -1;
    }
    else
    {
        //output the string
        WriteConsoleA(hConsole,szTemp,getStringLen(szTemp),&dwNum1,NULL);
    }

    return(iRet);
}