02.IDENTIFYING MAIN() IN STRIPPED BINARIES

This method is used on programs that doesn't have DEBUG SYMBOLS. When analyzing stripped binaries, it is crucial to identify main() as that's where the program's logic typically starts. Finding main() improves decompiler readability, aids control flow analysis, and helps trace program behavior. It also aligns static analysis with runtime activity.

01.REBASE: IDENTIFY BASE ADDRESS

STEP 1: WINDBG

PS C:\sre> WinDBG
 ...

WinDBG > File > Open Executable > anti-debug.exe

//step 1: list the modules and identify the "base address" of the anti-debug program
WinDBG > Command
 0:000> lm
 Start          End          module_name
 00c00000       00c6b000     anti_debug   (deferred)
 76a70000       76b60000     KERNEL32     (deferred)
 770f0000       7730c000     KERNELBASE   (deferred)
 77320000       774c4000     ntdll        (pdb symbols)     c:\ProgramData\dbg\sym\wntdll.

 * ghidra will be set to the same "base address" found by WinDBG
    - Base Address: 00c00000

STEP 2: GHIDRA

Ghidra must be set to the same "base address" found by WinDBG. When the interesting code parts are found and identified, no address translation will be required, as the addresses in WinDBG and Ghidra will match.

PS C:\sre> ghidra
 ...
 
Ghidra > File > Open > anti-debug.exe > Open in Code Browser
 Analyze: Yes
 Options: Default

Ghidra > Memory Map > Home Icon
 Base Image Address: 00c00000
 
 * ghidra's default base address is 00400000

02.RUN THE PROGRAM AND IDENTIFY PATTERNS

this may not always be accurate

root@sre:~$ .\antidebugging.exe
 Hi, what's your name? Enter as first_last:

03.HUNT MAIN

when you find the function that has similar patterns when the program is run, this could be your clue

#step 1:
ghidra > File > Open > 15_function_args_no_debug.exe
 filter: main
 
 * since there is no debug symbols when the executable was compiled, 
   main won't be found

#step 2:
ghidra > Symbol Tree > Functions > entry
 void entry(void)
 {
   __security_init_cookie();
   __scrt_common_main_seh();
   return;
 }
 
 * this will drop you to where ghidra thinks the main function is
 
#step 3:
ghidra > Symbol Tree > Functions > entry

 * click on "entry" to automatically decompile it
    - entry is NOT main(), but it leads to it.
 
ghidra > decompile ...

 void entry(void)
 {
   __security_init_cookie();
   __scrt_common_main_seh();
   return;
 }
 
 * click __scrt_common_main_seh()
    - This function is responsible for setting up exception handling and eventually calling main().
       - Goal: Find __scrt_common_main();
       
 * __scrt_common_main_seh(); is a function commonly found in Windows executables 
   compiled with Microsoft's C runtime (CRT). It is part of the Microsoft Startup Code 
   and is involved in setting up exception handling for the main program.
    - this function is responsible for several things including...
       - Calling main() or WinMain(): Once the initialization is complete, it calls the
         user-defined main() (for console apps) or WinMain() (for GUI apps).
          - __scrt_common_main_seh(); is just a wrapper around main() or WinMain()
 
 * the __security_init_cookie(); is a function that initializes a global security 
   cookie used to detect stack buffer overflows at runtime. It is part of /GS 
   (Buffer Security Check), a compiler flag that adds stack canary protection to 
   functions that may be vulnerable to buffer overflows.
      
#step 4: continue w/ working in reverse until the main function is identified
 ...
  iVar1 = __cinit(1);
  if (iVar1 != 0){
    __amsg_exit(iVar1);
  }
  _DAT_0040cf34 = DAT_0040cf30;
  iVar1 = FUN_00401010();
  _exit(iVar1);
  __cexit();
  return iVar1;
  
 * if you see __{arbitraryString}...it means that its coming from imports
    - Symbol Tree > Imports > KERNEL32.DLL

#step 5: the first function encountered when working in reverse that DOESN'T start w/ __ should be the main function
 iVar1 = FUN_00401010();
 _exit(iVar1);
 __cexit();
 return iVar1;
  
 * double click the FUN_00401010() in iVar1 = FUN_00401010(); to get to its 
   memory location
 
 undefined4 FUN_00401010(void)
 {
   int iVar1;
   
   iVar1 = FUN_00401000(1, 2, 3);
   _printf(&DAT_0040c000,iVar1);
   return 0;
 }
 
#step 6: rename the function to make it clear to ghidra that this is the main function

 ***********************
 *  FUNCTION           *
 ***********************
 undefined4 __stdcall FUN_00401010(void)    //hit the "l" key on the keyboard or right-click it to rename
  - rename FUN.. to main

Last updated