Skip to content

Instantly share code, notes, and snippets.

@weliveindetail
Created September 27, 2023 15:00
Show Gist options
  • Save weliveindetail/d7140fbe7f6b35af9bb99746c35a69d7 to your computer and use it in GitHub Desktop.
Save weliveindetail/d7140fbe7f6b35af9bb99746c35a69d7 to your computer and use it in GitHub Desktop.
WinDbg SymBuilder demo

Minimal example:

> cat app.c
extern char lafc(int argc, char *argv[]);
int main(int argc, char *argv[]) {
  return lafc(argc, argv);
}
> cat lafc.c
char lafc(int argc, char *argv[]) {
  char ec = argv[argc - 1][0];
  return ec;
}

The idea is to build lafc.c ("Last Arg First Char") without debug info and instead synthesize them in the plugin:

> clang -O0 -g0 -c lafc.c -o lafc.obj
> clang -O0 -g app.c lafc.obj -o app.exe
> dumpbin /DISASM app.exe | grep -A15 -B14 "lafc:"
main:
  0000000140007130: 48 83 EC 38        sub         rsp,38h
  0000000140007134: C7 44 24 34 00 00  mov         dword ptr [rsp+34h],0
                    00 00
  000000014000713C: 48 89 54 24 28     mov         qword ptr [rsp+28h],rdx
  0000000140007141: 89 4C 24 24        mov         dword ptr [rsp+24h],ecx
  0000000140007145: 48 8B 54 24 28     mov         rdx,qword ptr [rsp+28h]
  000000014000714A: 8B 4C 24 24        mov         ecx,dword ptr [rsp+24h]
  000000014000714E: E8 B4 BA FF FF     call        @ILT+7170(lafc)
  0000000140007153: 0F BE C0           movsx       eax,al
  0000000140007156: 48 83 C4 38        add         rsp,38h
  000000014000715A: C3                 ret
  000000014000715B: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC  ................
  000000014000716B: CC CC CC CC CC                                   .....
lafc:
  0000000140007170: 48 83 EC 10        sub         rsp,10h
  0000000140007174: 48 89 54 24 08     mov         qword ptr [rsp+8],rdx
  0000000140007179: 89 4C 24 04        mov         dword ptr [rsp+4],ecx
  000000014000717D: 48 8B 44 24 08     mov         rax,qword ptr [rsp+8]
  0000000140007182: 8B 4C 24 04        mov         ecx,dword ptr [rsp+4]
  0000000140007186: 83 E9 01           sub         ecx,1
  0000000140007189: 48 63 C9           movsxd      rcx,ecx
  000000014000718C: 48 8B 04 C8        mov         rax,qword ptr [rax+rcx*8]
  0000000140007190: 8A 00              mov         al,byte ptr [rax]
  0000000140007192: 88 44 24 03        mov         byte ptr [rsp+3],al
  0000000140007196: 8A 44 24 03        mov         al,byte ptr [rsp+3]
  000000014000719A: 48 83 C4 10        add         rsp,10h
  000000014000719E: C3                 ret
  000000014000719F: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC  ................
  00000001400071AF: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC  ................

Register the lafc function and set a regular breakpoint:

0:000> !load SymbolBuilderComposition.dll
0:000> dx @$s = Debugger.Utility.SymbolBuilder.CreateSymbols("app", new { AutoImportSymbols = true })
@$s = Debugger.Utility.SymbolBuilder.CreateSymbols("app", new { AutoImportSymbols = true })
0:000> .reload
0:000> lmm app
Browse full module list
start             end                 module name
00007ff7`b8d80000 00007ff7`b8e22000   app      C (service symbols: Symbol Builder Symbols (with demand import from PDB))
0:000> dx @$lafc = @$s.Functions.Create("lafc", "char", 0x7170, 0x2e)
@$lafc = @$s.Functions.Create("lafc", "char", 0x7170, 0x2e)                 : Function: lafc
    Name             : lafc
    QualifiedName    : lafc
    AddressRanges    : [7170, 719e)
    LocalVariables
    Parameters       : ()
    ReturnType       : Basic Type: char ( size = 1, align = 1 )
0:000> bp lafc

Register function parameters argc and argv:

0:000> dx @$lafc.Parameters.Add("argc", "int")
@$lafc.Parameters.Add("argc", "int")                 : int argc
    Name             : argc
    QualifiedName    : argc
    Parent           : Function: lafc
    Type             : Basic Type: int ( size = 4, align = 4 )
    LiveRanges
    Delete           [Delete() - Deletes the variable (parameter or local)]
0:000> dx @$lafc.Parameters[0].LiveRanges.Add(0, 0x2e, "[@rsp+4]")
@$lafc.Parameters[0].LiveRanges.Add(0, 0x2e, "[@rsp+4]")                 : [0, 2e): argc = [@rsp + 4]
    Offset           : 0x0
    Size             : 0x2e
    Location         : [@rsp + 4]
0:000> dx @$lafc.Parameters.Add("argv", "char *[]")
@$lafc.Parameters.Add("argv", "char *[]")                 : char *[0] argv
    Name             : argv
    QualifiedName    : argv
    Parent           : Function: lafc
    Type             : Array: char *[0] ( size = 0, align = 8 )
    LiveRanges
    Delete           [Delete() - Deletes the variable (parameter or local)]
0:000> dx @$lafc.Parameters[1].LiveRanges.Add(0, 0x2e, "[@rsp+8]")
@$lafc.Parameters[1].LiveRanges.Add(0, 0x2e, "[@rsp+8]")                 : [0, 2e): argv = [@rsp + 8]
    Offset           : 0x0
    Size             : 0x2e
    Location         : [@rsp + 8]

Register local variable ec:

0:000> dx @$ec = @$lafc.LocalVariables.Add("ec", "char")
@$ec = @$lafc.LocalVariables.Add("ec", "char")                 : char ec
    Name             : ec
    QualifiedName    : ec
    Parent           : Function: lafc
    Type             : Basic Type: char ( size = 1, align = 1 )
    LiveRanges
    Delete           [Delete() - Deletes the variable (parameter or local)]
0:000> dx @$ec.LiveRanges.Add(0, 0x2e, "[@rsp+3]")
@$ec.LiveRanges.Add(0, 0x2e, "[@rsp+3]")                 : [0, 2e): ec = [@rsp + 3]
    Offset           : 0x0
    Size             : 0x2e
    Location         : [@rsp + 3]

Resume execution, step and dump values:

0:000> g
Breakpoint 0 hit
app!lafc:
00007ff7`b8d87170 4883ec10        sub     rsp,10h
0:000> t
app!lafc+0x4:
00007ff7`b8d87174 4889542408      mov     qword ptr [rsp+8],rdx ss:00000093`0d0ff890=00100800000a0671
0:000> dv
           argc = 0n0
           argv = char *[0]
             ec = 0n0 ''
0:000> t
app!lafc+0x9:
00007ff7`b8d87179 894c2404        mov     dword ptr [rsp+4],ecx ss:000000f8`1d79fbec=00000000
0:000> t
app!lafc+0xd:
00007ff7`b8d8717d 488b442408      mov     rax,qword ptr [rsp+8] ss:000000f8`1d79fbf0=00000289e4563950
0:000> dv
           argc = 0n1
           argv = char *[0]
             ec = 0n0 ''

If we move past 0x140007190 we also see ec getting a non-zero value. It's the first character of the last command line argument. Voila!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment