fdbg.uefi.x64.0001
uefi asm level debugger for EFI_IMAGE_MACHINE_x64
author: Feryno


Any questions, improvements, found misbehaviour, ... please report them into FASM forum, section Projects and ideas, thread fdbg


----------------------------------------------------------------


version 0001
2012-jul-22
[+] you can type the executable name (no need to compile your program with fdbg engine as in version 0000 anymore)


version 0000
2012-mar-11
[ ] initial version
[-] you need to compile your FASM source code with the fdbg engine into 1 executable
[+] enabling features (if present) as YMM registers, debug control (for AMD as well various Intel models)
[!] warning !!! you can't debug some UEFI system calls which are used by fdbg, e.g. you can't put breakpoint inside OutputString !!! either WaitForKey !!! this can be solved if fdbg writes pixels directly into video card memory framebuffer and watches keyboard I/O port (hypervisor may do it silently and transparently for UEFI)


----------------------------------------------------------------


you can run the program from UEFI shell
or you can copy the program into USB flash drive into \efi\boot\bootx64.efi and boot from the flash drive under UEFI mode (booting drive using BIOS emulation never loads you program, but loads bootsector of the drive)

fdbg prompts you to type the path and name of your program
the executable must be in the same device and partition as fdbg and you must type full path with executable name
here samples (sample 0 and 1 are the same, 2 and 3 are equivalent)
test.efi
\test.efi
asm\prog\test.efi
\asm\prog\test.efi
then fdbg tries to load the file you entered (fdbg exits if that fails, e.g. typo error, file doesn't exist, file is not uefi x64 executable), then fdbg determines its base and entrypoint, writes breakpoint at entrypoint, starts the executable, then the executable hits breakpoint which is captured by fdbg and the executable is halted at its entry point
fdbg waits for commands
these commands are implemented:
h
q
g
s
t
u
v
w
r
x
y
b
a
c
d
e
m


[00]
command h prints help

if you want to print help about command r then the syntax is:
h r

if you want to know syntax/usage of m command, the do this:
h m


[01]
q command erases all breakpoints you put into program (during debugging) and runs it


[02]
g command runs the programm


[03]
s command executes single step (sets RFLAGS.TF=1 and runs the program, then the execution of the first instruction causes #DB and the program is halted after executing the first instruction/its first iteration)


[04]
t command executes trace over for instructions CALL, LOOP, REP (for all other instructions it falls back into command s single step)
N.B. it tries to use hardware breakpoint (debug registers), if all 4 are used then it uses software breakpoint (writing int3 breakpoint into memory)


[05]
u command tries to exctract (from the stack) the return address from procedure, then puts breakpoint at that address and runs the program, so the program should stop after returning from procedure (if something else doesn't halt it earlier)

[06]
v command does single step on branching instruction (sets DebugCtl.BTF and does the same as the command s, refer to the AMD/Intel manuals or search internet for this feature if you don't know it yet)


[07]
w command prints last branch from/to MSRs registers (fdbg always enables DebugCtl.LBR and captures branches)
on AMD there are 1 pair last branch from/to and 1 pair last exception from/to
when exception occures (as debug, breakpoint, etc), last branch from/to are copied to last exception from/to, last branch from holds the address causing exception (debug, breakpoint, bug) and is the same as the value of RIP register, last branch to is useless because holds the address of exception handler, last exception from/to holds usefull values of branching instructions which occured before exception (debug, breakpoint, bug)
on Intel, there are upto 16 pairs last branch from/to
I'll be happy if somebody could test fdbg on Intel and report that it works correctly (I have only AMD CPU at UEFI platform, no Intel and not yet plane to have Intel UEFI)
Because there are maybe 100 various Intel CPU models with 3 different settings of MSRs for LBF/LBT/TOS, I'll developed some scanning method for determining these MSRs (don't have time to implement checking of 100 various CPU models and assign correct MSRs to them and update the list every time intel releases new model, so went different way and simple scanned MSRs whether they exist)
I'll be extremely happy if the detection passes on Intel CPUs and report correct MSRs (can't test that, hope that implemented procedures are stable and work correctly)


[08]
r command prints/changes register(s)
bare command r without any param prints 16 GPRs, RIP, RFLAGS
r
if you want to print only 1 register, then use commands like:
r rax
r rip
r st7
r mm5
r xmm14
r xmm15
if you want to change register then use commands like:
r rax=0
r r15=FFFFFFFFFFFFFFFF
r rip=rip-2
r rbp=rsp+80
r zf=1
r cf=0
r st0=2.578
r ymm15=5.1 -2.4 7.8 1*10^7 -5*10^-8 0.2 -0.4 4000.
for more samples type this command:
h r
N.B. for floating points, fdbg calculates the count of decimal dots and decides whether you want to set single precison / double precision / hexa value into register


[09]
x command shows / sets hardware breakpoint(s) = breakpoint(s) using debug registers (they don't change memory, they use only registers, but there are only 4 registers available)
without params it shows hardware breakpoints:
x
if there isn't any then it shows nothing of course
to set hw bp use commands like
x p e 265152
x p r b 266015
x t w w 40201a
x t w q 402018
x t r d 402034
x t i d cf8
x p i b 1F7
t means temporary (one-shot, erased immediately after it hits), p means permanent (breakpoint stays after it hits)
e means executable, r means read+WRITE memory (not only read but WRITE also !!!), w means write memory (but not read), i means I/O port access
b=byte, w=word, d=dword, q=qword
for executable hw bp the size is always byte and you cannot set the size, for all other types you must set the size of breakpoint area
for size word, dword, qword the address must be aligned at the size boundary
executable hw bp hits before the instruction is executed, readwrite/write/io breakpoints generate debug exception after they complete (so RIP is pointing after the instruction which caused the breakpoint)
type this command for more
h x
even better, read AMD/Intel manual(s)
N.B. fdbg tries to enable I/O breakpoints also (the feature seems to be present on all new CPUs) so you may capture accesses to I/O ports also


[0A]
y command shows / erases hardware breakpoint
without any param it shows all hardware breakpoints (if there is any)
if you want to erase one of 4 hw bp, use commands like
y 0
y 1
y 2
y 3


[0B]
b command shows / sets software breakpoint(s) (writes int3 into memory), advantage is that the count of these breakpoints is unlimited (there are only 4 hw bp), disadvantages are that it modifies memory so then some CRC may report wrong result, they can't capture I/O ports / memory accesses but are only executable, they are only temporary = one-shot (I don't have enough time to implement permanent software breakpoints, use hw bp where it is possible)
without any param it shows all software breakpoints (if there is any), which is command:
b
to set breakpoint at instruction at address 2b280f0 use command:
b 2b280f0
N.B. that the address must be at the address where the instruction starts (not somewhere in the middle of more-bytes instruction !!!)


[0C]
a command shows / erases software breakpoint(s)
without any param it shows all software breakpoints (if there is any), which is command:
a
to delete breakpoint at address 2b280f0, use command like
a 2b280f0
N.B. that software breakpoint is also erased immediatelly after it hits (because they are implemented only as temporary = one shot)

[0D]
c command prints disassembled code from current RIP or from the last address where previous command c finished, the start address is updated at every exception (debug, breakpoint, bug)
without any param, it starts to print instructions from current RIP, this is command
c
with address it starts to print instructions from the address, this is command like
c 2b69528
if you want to print more instructions after the last one printed, then continue with bare command c (without address), that is:
c
once you type the command c, you can just press ENTER to repeat it until you want


[0E]
d command dumps data from address, this is something like:
d 1234560
to dump more, type bare command
d
end press ENTER until you like


[0F]
e dumps stack from current RSP (bare command e) or from address (command like e 10008510)
N.B. that the format is different than format in command d, here the data is displayed as qwords


[10]
m command modifies memory, you must specify whether to write byte, word, dword, qword, ascii string, unicode string
samples:
m a [302521]='string'
m u [1000015EF]="text"
m a [rsi]="isn't"
m u [rax-3]="'"Hello!"'"
m b [281021]=Fd
m w [rsp-5C]=1234
m d [rip+512a]=AB91f50e
m q [r14-14]=2000000000000051
N.B. inline assembler is not implemented, you can change instruction only using hexa encodings
for printing memory use commands c, d, e


[11]
pressing ENTER repeats previous command (if there is any valid command), so if you want to do 20 single steps, then once type s and then 20-times hit ENTER


fdbg tries to enable all features at the CPU like I/O hardware breakpoints, debug branches MSRs, YMM registers (note that you can work with YMM registers but disasm procedure doesn't support them so you'll see some strange disassembled instructions, but you can single step / trace through them, CPU interprets them correctly, only disasm not, I don't have time to upgrade disasm engine, If you miss that much you must improve the engine by yourself, fdbg source code is available and will be forever)

N.B. that commands are case sensitive and there is allowed only 1 space for separating strings, no spaces at the end of command are allowed, any violation causes to abort the command and message about bad syntax is printed


WARNING !!!
Never try to put breakpoints inside of SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString, EFI_BOOT_SERVICES.WaitForEvent, EFI_SYSTEM_TABLE.ReadKeyStroke.
This is because fdbg uses these UEFI services to print to screen and get input from keyboard. Interrupting this by breakpoint halts the PC (only reset/power button helps).


----------------------------------------------------------------


documentation of debugger design


[0] assumption(s):
- only one CPU is executing even in SMP systems (bootstrap CPU bit 8. of MSR 0000001Bh MSR_APIC_BASE.BSP=1, application CPUs are halted)
  this ensures hooked interrupts on the CPU will work (else the only way is to send IPI to all CPUs so every CPU installs hook)
- no one exception handler is used by UEFI (as int 08h was used in BIOS for IRQ0 system timer and int 09h for IRQ1 keyboard interrupt)
- debugger as well the debuggee share the same virtual memory (have the same virtual memory translation tables, the same base from CR3)
- executables are running at CPL0

[1] debugger does these tasks in this order:
loads_executable
puts_breakpoint at executable entrypoint
hook_exceptions (int 00h - int 1Fh)
starts the executable
unhook_exceptions (int 00h - int 1Fh)

[2] accessing memory (e.g. doing memory inspection, writing int3 breakpoint):
hook these exceptions: #UD Invalid opcode, #NP Segment not present, #SS Stack, #GP General protection, #PF Page fault, #AC Alignment check
try to access the memory
if no hook hit then the access was successfull, else the memory is not present, in case of partial transfer calculate the count of bytes transferred and return it
unhook exceptions

[3] exception handler:
save all registers
enable interrupts
wait for command done by user
disable interrupts
restore all registers
IRETQ to the interrupted debuggee

