I working through some example in Windows System Programming 4th. Using windbg.exe I'm trying to inspect the parameters passed to a function (GetCurrentDirectoryA). Below is the source.
int _tmain (int argc, LPTSTR argv [])
{
/* Buffer to receive current directory allows for the CR,
LF at the end of the longest possible path. */
TCHAR pwdBuffer [DIRNAME_LEN];
DWORD lenCurDir;
lenCurDir = GetCurrentDirectory (DIRNAME_LEN, pwdBuffer);
if (lenCurDir == 0)
ReportError (_T ("Failure getting pathname."), 1, TRUE);
if (lenCurDir > DIRNAME_LEN)
ReportError (_T ("Pathname is too long."), 2, FALSE);
PrintMsg (GetStdHandle (STD_OUTPUT_HANDLE), pwdBuffer);
return 0;
}
First I dump the local variables using dv -t -v. In this case I'm interested in the pwdBuffer.
0018ff3c int argc = 0n1
0018ff40 char ** argv = 0x00582470
0018fe18 unsigned long lenCurDir = 0x775b994a
0018fe24 char [262] pwdBuffer = char [262] ""
Then I set a breakpoint at Kernel32!GetCurrentDirectoryA. Which yields the following.
00 0018ff34 00428759 00000001 00582470 005824c0 kernel32!GetCurrentDirectoryA
What I don't understand is value of the parameters to the Function. I was expecting to see 0018fe24 as one value representing pwdbuffer.
The next thing I do is gu. Which executes Kernel32!GetCurrentDirectoryA to its end.
Thereafter I dumped the pwdBuffer value that I got initially with the dv -v -t command.
0:000> da 0018fe24
0018fe24 "C:\microsoft_press\WSP4_Examples"
0018fe44 "\Utility_4_dll"
This is what I expect from the buffer. So my question is why didn't I see this 0018fe24 value passed to GetCurrentDirectory?
Try single stepping past the mov ebp, esp instruction at the start of GetCurrentDirectoryA. The numbers you're seeing look like values from your _tmain function, specifically, its frame pointer (EBP), its return address, and its arguments argc and argv (along with the hidden envp parameter). Once EBP is loaded with the correct frame pointer for GetCurrentDirectoryA, windbg may be able to display the function's arguments correctly.
The stack should show the parameters to the function on hitting the break-point not after you single step i just had similar code (without crt window Apis only) and ran it through and windbg works as expected
when analyzing unknown or potentially malware binaries one unthoughtful single step can result in fatal infection. If logic exists don't use lucky charms.
my current directory
:\>echo %cd%
C:\temp\temp\temp\temp
contents of current directory
:\>ls -l
total 12
-rwxrwxrwx 1 Admin 0 197 2015-10-10 16:29 compile.bat
-rw-rw-rw- 1 Admin 0 336 2015-10-10 16:13 getdir.cpp
-rw-rw-rw- 1 Admin 0 145 2015-10-10 16:47 wtf.txt
src code for test
:\>type getdir.cpp
#include <windows.h>
int main (void) {
PCHAR buff=0;int bufflen=0;
bufflen=GetCurrentDirectory(0,NULL);
buff = (PCHAR)VirtualAlloc(NULL,bufflen,MEM_COMMIT,PAGE_READWRITE);
if(buff){
GetCurrentDirectory(bufflen,buff);
MessageBox(NULL,buff,"Current Directory",MB_OK);
VirtualFree(buff,0,MEM_RELEASE);
}
}
compiled with
:\>type compile.bat
#call "C:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86
cl /Zi /EHsc /O2 /nologo /W4 /analyze *.cpp /link /SUBSYSTEM:Windows /RELEASE /E
NTRY:main user32.lib kernel32.lib
pause
:\>compile.bat
Setting environment for using Microsoft Visual Studio 2010 x86 tools.
getdir.cpp
Press any key to continue . . .
executed with
:\>cdb -cf wtf.txt getdir.exe
-cf command line to windbg / cdb takes a file whose content will be executed as if you type them at the prompt
the contents of wtf.txt is
:\>type wtf.txt
bp kernel32!VirtualAlloc
g
gu
? #eax
bc *
bp kernel32!GetCurrentDirectoryA
g
dd #esp l3
r $t0 = poi(#esp+8)
? #$t0
gu
da #$t0;
g
q
on the first system break
set a breakpoint on virtualalloc and run the binary
when the breakpoint is hit goup (we are interested only in the return value from this function ) and inspect eax (return value from function)
clear all breakpoints
set a breakpoint in GetCurrentDirectoryA
execute the binary again with g on hitting the breakpoint inspect the stack
with dd #esp l3
(display three dwords from Stack pointer one return address and two function parameters to the Function GetCurrentDirectoryA()
note the stack will contain the same address we previously inspected at the return of VirtualAlloc using ? #eax
save the address of buffer to a pseudo variable and go up
print the ascii string from the buffer da #$t0
exit the session
the result of this session is as follows note we got 35000 as the allocated memory address of buffer from virtual alloc and that was indeed passed to GetCurrentDirectory and that hold the string Current directory
:\>cdb -cf wtf.txt getdir.exe
0:000> bp kernel32!VirtualAlloc
0:000> g
Breakpoint 0 hit
kernel32!VirtualAlloc:
7c809af1 8bff mov edi,edi
0:000> gu
getdir!main+0x21:
00401021 8bf0 mov esi,eax
0:000> ? #eax
Evaluate expression: 3473408 = 00350000 <--------
0:000> bc *
0:000> bp kernel32!GetCurrentDirectoryA
0:000> g
Breakpoint 0 hit
kernel32!GetCurrentDirectoryA:
7c83502e 8bff mov edi,edi
0:000> dd #esp l3
0013ffac 0040102b 00000017 00350000 <------
0:000> r $t0 = poi(#esp+8)
0:000> ? #$t0
Evaluate expression: 3473408 = 00350000 <----------
0:000> gu
getdir!main+0x2b:
0040102b 6a00 push 0
0:000> da #$t0;
00350000 "C:\temp\temp\temp\temp"
0:000> g
edit all others being same just added a kb command to the script file and executed to show the stacktrace
Related
While following up on some windbg tutorials I have noticed that some callstacks using k command are in this format, specially mine
Child-SP RetAddr Call Site
While other online resources like CodeProject have the k command spit out info in this format
Child-EBP RetAddr Call Site
I am confused to why there is a difference between my output and theirs and to what that truly means.
It depends on what calling convention is being used. Some functions allocate locals and parameters that it will pass to the functions it calls using the base pointer, such as cdecl, but Windows x64 calling convention uses rsp.
Child-SP is the value the stack pointer of that frame, which will be the byte before the return address for all frames except for frame 0, which might have a breakpoint before or during the prologue. If the breakpoint is on the first instruction, then the rsp would have only decreased by 8 on the Child-SP of the previous frame; this rsp value is read from the trap frame. The Child-SP is the address of the frame at the frame number if you do not consider the return address of the callee to be part of the frame (which makes sense in this scenario because the return address column of the frame is showing the return address to the previous frame to be part of this frame).
ChildEBP is the value of ebp when in that frame (not the ebp that is pushed to the frame, but the new value of ebp)
RetAddr is the return address that belongs to the frame, so it is the address it will return to
The call site is the address of the instruction after the call instruction that was called that ended the frame (so basically the callee return address -- call instruction + call instruction length), or the address of the breakpoint or other exception that caused it to break into the debugger (note: the address of the exception, not the instruction after it, so this will be rip in the trap frame) in the case of frame 0 (the frame at the top of the stack). This will indicate the name of the function that owns the frame on this row as long as the function doesn't contain a label, and args to child are the arguments passed to it. Indeed the callsite is the return address of the frame that it calls (the frame above it).
Args to child are the arguments passed to the function that owns the stack frame on the current row, not the arguments passed to the callee function it calls in the allocated space by the prologue. This is almost never accurate on x64, where it shows the first 3 quadwords of the homespace, because the first 4 arguments can be passed in registers, and the callee function may not home these arguments (save them in the homespace) if it is -O0 or not a varargs function, or may put something else in the homespace entirely. 'Child' in 'Args to child' and 'Child-SP' is a false and misleading name which implies the function the frame calls, but it actually refers to the current function.
There is an example stacktrace on this site which shows a breakpoint on notepad!ShowOpenSaveDialog, which will be the first instruction.
0:011> bp notepad!ShowOpenSaveDialog
0:011> g
Breakpoint 0 hit
notepad!ShowOpenSaveDialog:
00007ff7`0307182c 48895c2408 mov qword ptr [rsp+8],
rbx ss:00000073`74d2f310=0000000000000000
0:000> k
# Child-SP RetAddr Call Site
00 00000073`74d2f308 00007ff7`03071aeb notepad!ShowOpenSaveDialog
01 00000073`74d2f310 00007ff7`030721fa notepad!InvokeOpenDialog+0x14f
02 00000073`74d2f370 00007ff7`030738d6 notepad!NPCommand+0x4a2
03 00000073`74d2f6f0 00007fff`664b6d41 notepad!NPWndProc+0x726
04 00000073`74d2f9f0 00007fff`664b6713 USER32!UserCallWinProcCheckWow+0x2c1
05 00000073`74d2fb80 00007ff7`03073bdb USER32!DispatchMessageWorker+0x1c3
06 00000073`74d2fc10 00007ff7`03089333 notepad!WinMain+0x27f
07 00000073`74d2fd10 00007fff`68ea3034 notepad!__mainCRTStartup+0x19f
08 00000073`74d2fdd0 00007fff`69073691 KERNEL32!BaseThreadInitThunk+0x14
09 00000073`74d2fe00 00000000`00000000 ntdll!RtlUserThreadStart+0x21
0:000> r #rcx=0
0:000> r
rax=0000000000000000 rbx=0000000000000000 rcx=0000000000000000
rdx=00000213f5020968 rsi=000000499cb1f338 rdi=00000213f5021c20
rip=00007ff70307182c rsp=000000499cb1f288 rbp=000000499cb1f2d0
r8=00000213f4ffe9d6 r9=000000499cb1f338 r10=00000ffee060e246
r11=0000000000014140 r12=0000000000000000 r13=0000000000000001
r14=000000000004094e r15=00000000ffffffff
iopl=0 nv up ei pl zr na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
notepad!ShowOpenSaveDialog:
00007ff7`0307182c 48895c2408 mov qword ptr [rsp+8],
rbx ss:00000049`9cb1f290=0000000000000000
0:000> ub notepad!InvokeOpenDialog+0x14f
notepad!InvokeOpenDialog+0x133:
00007ff7`03071acf 8bd8 mov ebx,eax
00007ff7`03071ad1 85c0 test eax,eax
00007ff7`03071ad3 782c js notepad!InvokeOpenDialog+0x165 (00007ff7`03071b01)
00007ff7`03071ad5 4c8b05fc080200 mov r8,qword ptr [notepad!szOpenCaption (00007ff7`030923d8)]
00007ff7`03071adc 4c8bce mov r9,rsi
00007ff7`03071adf 488b5538 mov rdx,qword ptr [rbp+38h]
00007ff7`03071ae3 498bce mov rcx,r14
00007ff7`03071ae6 e841fdffff call notepad!ShowOpenSaveDialog (00007ff7`0307182c)
The trap frame created by nt!KiBreakpointTrap is not part of the stack trace, because trap frames are pushed to the threads's kernel stack not the user stack.
If you are kernel debugging then you will see a fusion of the user and kernel stacks if there is a trap frame and the process address space is accessible:
lkd> .process /P fffffa80723296f0
lkd> .reload
lkd> ld *
lkd> !process 3490
Searching for Process with Cid == 3490
Cid handle table at fffff8a00195f000 with 4126 entries in use
PROCESS fffffa80723296f0
SessionId: 1 Cid: 3490 Peb: 7fffffdf000 ParentCid: 1470
DirBase: 403874000 ObjectTable: fffff8a02b3a2ce0 HandleCount: 293.
Image: chrome.exe
VadRoot fffffa805fc816e0 Vads 295 Clone 0 Private 16586. Modified 1536. Locked 0.
DeviceMap fffff8a00208d0b0
Token fffff8a03466d9e0
ElapsedTime 00:23:43.376
UserTime 00:00:00.717
KernelTime 00:00:00.000
QuotaPoolUsage[PagedPool] 0
QuotaPoolUsage[NonPagedPool] 0
Working Set Sizes (now,min,max) (27282, 50, 345) (109128KB, 200KB, 1380KB)
PeakWorkingSetSize 33917
VirtualSize 870 Mb
PeakVirtualSize 891 Mb
PageFaultCount 81452
MemoryPriority BACKGROUND
BasePriority 4
CommitCharge 19316
Job fffffa805f9f46d0
THREAD fffffa802e890b50 Cid 3490.469c Teb: 000007fffffdd000 Win32Thread: fffff900c53a48c0 WAIT: (UserRequest) UserMode Non-Alertable
fffffa8062daf060 SynchronizationEvent
Not impersonating
DeviceMap fffff8a00208d0b0
Owning Process fffffa80723296f0 Image: chrome.exe
Attached Process N/A Image: N/A
Wait Start TickCount 45844297 Ticks: 42 (0:00:00:00.655)
Context Switch Count 21234 LargeStack
UserTime 00:00:07.300
KernelTime 00:00:00.234
Win32 Start Address chrome!IsSandboxedProcess (0x000000013fbcbe90)
Stack Init fffff8802d5bec70 Current fffff8802d5be7c0
Base fffff8802d5bf000 Limit fffff8802d5b7000 Call 0
Priority 4 BasePriority 4 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
fffff880`2d5be800 fffff800`0367ec32 nt!KiSwapContext+0x7a
fffff880`2d5be940 fffff800`0368145f nt!KiCommitThreadWait+0x1d2
fffff880`2d5be9d0 fffff800`0397602e nt!KeWaitForSingleObject+0x19f
fffff880`2d5bea70 fffff800`03678c13 nt!NtWaitForSingleObject+0xde
fffff880`2d5beae0 00000000`7782bd7a nt!KiSystemServiceCopyEnd+0x13 (TrapFrame # fffff880`2d5beae0)
00000000`002eec08 000007fe`fd6110ac ntdll!NtWaitForSingleObject+0xa
00000000`002eec10 000007fe`d97c2107 KERNELBASE!WaitForSingleObjectEx+0x79
00000000`002eecb0 000007fe`d858aeab chrome_child!GetHandleVerifier+0x15d8417
00000000`002eed40 000007fe`d823004d chrome_child!GetHandleVerifier+0x3a11bb
00000000`002eedb0 000007fe`d822fc91 chrome_child!GetHandleVerifier+0x4635d
00000000`002eee10 000007fe`d8217c59 chrome_child!GetHandleVerifier+0x45fa1
00000000`002eee40 000007fe`d82176de chrome_child!GetHandleVerifier+0x2df69
00000000`002ef040 000007fe`d82105d1 chrome_child!GetHandleVerifier+0x2d9ee
00000000`002ef1b0 000007fe`d81e518b chrome_child!GetHandleVerifier+0x268e1
00000000`002ef250 000007fe`d81e4c58 chrome_child!ChromeMain+0x377d
00000000`002ef570 000007fe`d81e1b2e chrome_child!ChromeMain+0x324a
00000000`002ef600 00000001`3faf354c chrome_child!ChromeMain+0x120
00000000`002ef6d0 00000001`3faf1699 chrome+0x354c
00000000`002ef7c0 00000001`3fbcbe33 chrome+0x1699
00000000`002efba0 00000000`775d59cd chrome!IsSandboxedProcess+0x61483
00000000`002efbe0 00000000`7780a561 kernel32!BaseThreadInitThunk+0xd
00000000`002efc10 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
The trap frame is made by nt!KiSystemCall64, which has a label in it called nt!KiSystemServiceCopyEnd, which makes the call to the system service, in this case nt!NtWaitForSingleObject. The trap frame is the first stack frame hence function on the kernel stack and begins at the rsp (Child-SP) of the function nt!KiSystemCall64 and starts with the homespace that it allocates for the next function. The first instruction of nt!KiSystemCall64 is swapgs to swap gs with the gs in IA32_KERNEL_GS_BASE, and then it stores rsp into gs:10h, which is the UserRsp field in the KPCR and then stores gs:1A8h into rsp, which is the RspBase field in the KPRCB, this is clearly swapping the user rsp with the kernel rsp for the thread, which is cached in the hyperthread that currently has that thread running's KPRCB. It then starts building the trap frame, where the rsp is currently pointing to offset 0x190 in the trap frame by definition (the byte after the end of the frame), starting with pushing SS, because the CPU doesn't do this when you use syscall instead of int 0x2e.
When a base pointer is used, the trap frame would be at 8 less than the ebp of the frame above it (subtract ebp and return address, 4 bytes each), so the frame of nt!NtWaitForSingleObject
Exceptions have an exception frame that stores the non-volatile registers using nt!KiExceptionDispatch, after nt!KiBreakpointTrap for instance creates the trap frame (and the trap frame building different to a system call because the processor pushes SS:RSP RFLAGS, CS:RIP and ErrorCode to the stack for a user-mode exception and RFLAGS, CS:RIP and ErrorCode to the stack for a kernel-mode exception, and does not save/load any rsp from the KPCR / KPRCB). nt!KiExceptionDispatch creates an exception frame KEXCEPTION_FRAME beginning at the next rsp in the stack trace and the EXCEPTION_RECORD is immediately after above that on the stack, with a total combined size of 1D8, so you'll see a difference of 1E0 between the stack pointers of nt!KiExceptionDispatch and nt!KiBreakpointTrap when you include the return address to nt!KiBreakpointTrap.
The trap frame stores all registers that can be clobbered by the kernel
during the array of functions that are called in the kernel, called volatile registers, because the volatile registers are guaranteed not to be changed during a syscall or an exception. It does not save non-volatile registers because the functions that are called in the kernel save any non-volatile registers they use, so by the time it returns to this function to perform a sysret, all the nonvolatile registers will be the value they were before. You need an exception frame for exceptions because it needs to know in the function that performs the stack unwinding what the nonvolatile registers contained at the time of the exception so that the stack can be unwound. A CONTEXT structure is created on the stack in nt!KiDispatchException to represent the full context at the time of the exception, which is a combination of the nonvolatile and volatile register state.
You also need another exception frame when swapping thread contexts in order to save the state of the nonvolatile registers at the time the thread is switched out, so this will be the state in nt!KiSwapContext, so this can be restored when the thread is switched back in. The volatile state does not need to be saved, because it's assumed that the call to SwapContext discards all volatile registers. The trap frame created when entering kernel mode in the first place will be used to restore the volatile register context to the thread when it returns to user mode.
Fun fact: for a breakpoint exception INT 3, the CPU pushes the rip starting after the end of the breakpoint instruction and not the breakpoint instruction which means that windows needs to decrement the address so the top of the stack callsite is the rip in the trap frame and not the return address pushed by the CPU.
I have a simple NASM code like below. I want to set the value 43 (which is the +3 offset in trx array) to value 99.
section .data
trx db 25,21,17,43
section .text
global _start
_start:
mov [trx+3], byte 99
last:
mov rax, 60
mov rdi, 0
syscall
When i debug and the _start function passed, it works. The value 43 changed to 99.
(gdb) i var
All defined variables:
Non-debugging symbols:
0x00000000006000c4 trx
0x00000000006000c8 __bss_start
0x00000000006000c8 _edata
0x00000000006000c8 _end
(gdb) x/4b &trx
0x6000c4: 25 21 17 43
(gdb) break _start
Breakpoint 1 at 0x4000b0
(gdb) run
Starting program: /home/hexdemsion/Desktop/asm/exec
Breakpoint 1, 0x00000000004000b0 in _start ()
(gdb) stepi
0x00000000004000b8 in last ()
(gdb) x/4b &trx
0x6000c4: 25 21 17 99
Now how can i set that value directly in GDB ? I have tried this command in GDB, but still doesn't work.
(gdb) set 0x00000000006000c4+3 = 99
Left operand of assignment is not an lvalue.
(gdb) set {int}0x00000000006000c4+3 = 99
Left operand of assignment is not an lvalue.
(gdb) set {b}0x00000000006000c4+3 = 99
No symbol table is loaded. Use the "file" command.
For addition, i don't provide any debug information in assemble time.
nasm -f elf64 -o obj.o source.asm; ld -o exec obj.o
You almost had it; use set {char}(0x00000000006000c4+3) = 99.
Here's a more detailed explanation:
In gdb's set statement, the expression to the left of the = can be a convenience variable, or a register name, or an lvalue corresponding to some object in the target.
An lvalue is an object that has an address, a type, and is assignable.
A literal or computed address such as 0x00000000006000c4 or 0x00000000006000c4+3 isn't an lvalue, but you can cast it to an lvalue using *(type *)(0x00000000006000c4+3) or {type}(0x00000000006000c4+3).
Gdb knows about C primitive types, plus whatever types the executable and libraries you're debugging may contain in their symbol tables or debug sections. In your case, since you want to set a byte, you'd use C's char type.
(gdb) x/4b &trx
0x6000c4: 25 21 17 43
(gdb) set {char}(0x00000000006000c4+3) = 99
(gdb) x/4b &trx
0x6000c4: 25 21 17 99
In the toy program below, I declare a variable in the .text section and writes to it, which gives a segmentation-fault, since the .text section is marked as READ-ONLY:
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: movl $0x2,0x40100a
End of assembler dump.
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x00401000 in start ()
(gdb)
Here is the objdump output:
test.exe: file format pei-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001f 00401000 00401000 00000200 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .idata 00000014 00402000 00402000 00000400 2**2
CONTENTS, ALLOC, LOAD, DATA
However, linking using the --omagic switch (disables READ-ONLY .text section) yields the following results:
ld --omagic -o test.exe test.obj
test.exe: file format pei-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001f 00401000 00401000 000001d0 2**4
CONTENTS, ALLOC, LOAD, CODE
1 .idata 00000014 00402000 00402000 000003d0 2**2
CONTENTS, ALLOC, LOAD, DATA
But debugging this using GDB gives the following (weird) results:
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: dec %ebp
0x00401001 <+1>: pop %edx
0x00401002 <+2>: nop
0x00401003 <+3>: add %al,(%ebx)
0x00401005 <+5>: add %al,(%eax)
0x00401007 <+7>: add %al,(%eax,%eax,1)
End of assembler dump.
(gdb) stepi
0x00401001 in start ()
(gdb) stepi
0x00401002 in start ()
(gdb) stepi
0x00401003 in start ()
(gdb) stepi
0x00401005 in start ()
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x00401005 in start ()
(gdb)
First of all, I still get a segmentation fault, but the assembly code has also changed structure?
How can I link the .text section as writable on Windows 10 x64?
Toy program:
BITS 32
section .text
global _start
_start:
mov [var], dword 2
var: dd 0
ret
For some reason, ld completely changes the PE executable linked using the --omagic option.
A quick comparison of the files using the cmp utility shows:
137 177 222
141 0 320
142 6 5
213 0 320
214 2 1
217 142 205
218 154 353
397 0 320
398 2 1
437 0 320
438 4 3
465 0 307
...
So lots of differences, although ld should in principle only change the sections flags of the section header (.text), i.e. set the flag IMAGE_SCN_MEM_WRITE.
Changing the flags manually using HxD, i.e. setting byte at offset 0x19F to 0xE0 solves the issue...
A trial run of the program with interchanged order of var and ret (otherwise the program crash):
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: movl $0x2,0x40100b
0x0040100a <+10>: ret
End of assembler dump.
(gdb) stepi
0x0040100a in start ()
(gdb) disassemble
Dump of assembler code for function start:
0x00401000 <+0>: movl $0x2,0x40100b
=> 0x0040100a <+10>: ret
End of assembler dump.
(gdb) x/wx var
0x40100b <var>: 0x00000002
(gdb)
and we see things work as expected.
My conclusion is that ld somehow generates a badly formatted PE executable, and I see that #RossRidge has the answer to this (ld doesn't respect the file alignment of sections).
The --omagic flag is causing the GNU linker to generate a bad PECOFF executable. Sections must aligned in the file with a minimum file alignment of 512 bytes, but the linker puts the .text section at file offset of 0x1d0.
Instead of using the --omagic flag, generate your executable normally and then use objcopy to change the flags in the section header:
ld -o test-tmp.exe test.obj
$(OBJCOPY) --set-section-flags .text=code,data,alloc,contents,load test-tmp.exe test.exe
I would like to attach to a running process using WinDbg, and modify a certain function's code to simply return on invocation (for educational purposes).
I have used the following commands:
uf dll!name
This gives me a disassembly of the function.
I have picked a specific address at a certain location and modified it to ret:
ew addr c3
This crashes every time, what am i doing wrong?
You need to make sure you do the appropriate clean up so the stack is left in a proper state. Depending on the calling convention the method usually pushes stuff on the stack as part of the prologue. This must be undone as part of the epilogue.
Here's an example of changing a JIT compiled method using WinDbg.
The code:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Message();
Console.ReadLine();
Message();
Console.WriteLine("done");
}
private static void Message()
{
Console.WriteLine("message");
}
}
}
I compiled this as Debug to prevent the compiler from inlining the calls to Message.
Then I ran the executable and attached the debugger at the call to ReadLine.
For managed code I need to use SOS.dll to locate the JIT compiled code. So I loaded SOS and found the address for the code as follows.
0:004> .loadby sos clr
0:004> !name2ee *!ConsoleApplication1.Program
Module: 04a11000
Assembly: mscorlib.dll
--------------------------------------
Module: 001b2e94
Assembly: ConsoleApplication1.exe
Token: 02000002
MethodTable: 001b37b4
EEClass: 001b125c
Name: ConsoleApplication1.Program
0:004> !dumpmt -md 001b37b4
EEClass: 001b125c
Module: 001b2e94
Name: ConsoleApplication1.Program
mdToken: 02000002
File: c:\temp\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
BaseSize: 0xc
ComponentSize: 0x0
Slots in VTable: 7
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
Entry MethodDe JIT Name
04d14960 04a16728 PreJIT System.Object.ToString()
04d08790 04a16730 PreJIT System.Object.Equals(System.Object)
04d08360 04a16750 PreJIT System.Object.GetHashCode()
04d016f0 04a16764 PreJIT System.Object.Finalize()
001bc019 001b37ac NONE ConsoleApplication1.Program..ctor()
002a0050 001b3794 JIT ConsoleApplication1.Program.Main(System.String[])
002a00a8 001b37a0 JIT ConsoleApplication1.Program.Message()
0:004> !u 001b37a0
Normal JIT generated code
ConsoleApplication1.Program.Message()
Begin 002a00a8, size 21
*** WARNING: Unable to verify checksum for c:\temp\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
c:\temp\ConsoleApplication1\ConsoleApplication1\Program.cs # 20:
002a00a8 55 push ebp <-- prologue
002a00a9 8bec mov ebp,esp
002a00ab 833d60311b0000 cmp dword ptr ds:[1B3160h],0 <-- start of method
002a00b2 7405 je ConsoleApplication1!ConsoleApplication1.Program.Message()+0x11 (002a00b9)
002a00b4 e8fb6ff570 call clr!JIT_DbgIsJustMyCode (711f70b4)
002a00b9 90 nop
c:\temp\ConsoleApplication1\ConsoleApplication1\Program.cs # 21:
002a00ba 8b0d34217403 mov ecx,dword ptr ds:[3742134h] ("message")
002a00c0 e82bd3ad04 call mscorlib_ni!System.Console.WriteLine(System.String) (04d7d3f0)
002a00c5 90 nop
c:\temp\ConsoleApplication1\ConsoleApplication1\Program.cs # 22:
002a00c6 90 nop
002a00c7 5d pop ebp <-- epilogue
002a00c8 c3 ret
Then I opened the Memory window and pointed it to 002a00ab which is the first part of the actual method body of Message and changed the two opcodes to 5d and c3 for pop edb and ret respectively. If I skipped the pop edb part the stack would be messed up and I would get an exception.
I hit Go and the application continued without printing "message" a second time.
Is it possible to inspect the return value of a function in gdb assuming the return value is not assigned to a variable?
I imagine there are better ways to do it, but the finish command executes until the current stack frame is popped off and prints the return value -- given the program
int fun() {
return 42;
}
int main( int argc, char *v[] ) {
fun();
return 0;
}
You can debug it as such --
(gdb) r
Starting program: /usr/home/hark/a.out
Breakpoint 1, fun () at test.c:2
2 return 42;
(gdb) finish
Run till exit from #0 fun () at test.c:2
main () at test.c:7
7 return 0;
Value returned is $1 = 42
(gdb)
The finish command can be abbreviated as fin. Do NOT use the f, which is abbreviation of frame command!
Yes, just examine the EAX register by typing print $eax. For most functions, the return value is stored in that register, even if it's not used.
The exceptions to this are functions returning types larger than 32 bits, specifically 64-bit integers (long long), doubles, and structs or classes.
The other exception is if you're not running on an Intel architecture. In that case, you'll have to figure out which register is used, if any.
Here's how todo this with no symbols.
gdb ls
This GDB was configured as "ppc64-yellowdog-linux-gnu"...
(no debugging symbols found)
Using host libthread_db library "/lib64/libthread_db.so.1".
(gdb) break __libc_start_main
Breakpoint 1 at 0x10013cb0
(gdb) r
Starting program: /bin/ls
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Breakpoint 1 at 0xfdfed3c
(no debugging symbols found)
[Thread debugging using libthread_db enabled]
[New Thread 4160418656 (LWP 10650)]
(no debugging symbols found)
(no debugging symbols found)
[Switching to Thread 4160418656 (LWP 10650)]
Breakpoint 1, 0x0fdfed3c in __libc_start_main () from /lib/libc.so.6
(gdb) info frame
Stack level 0, frame at 0xffd719a0:
pc = 0xfdfed3c in __libc_start_main; saved pc 0x0
called by frame at 0x0
Arglist at 0xffd71970, args:
Locals at 0xffd71970, Previous frame's sp is 0xffd719a0
Saved registers:
r24 at 0xffd71980, r25 at 0xffd71984, r26 at 0xffd71988, r27 at 0xffd7198c,
r28 at 0xffd71990, r29 at 0xffd71994, r30 at 0xffd71998, r31 at 0xffd7199c,
pc at 0xffd719a4, lr at 0xffd719a4
(gdb) frame 0
#0 0x0fdfed3c in __libc_start_main () from /lib/libc.so.6
(gdb) info fr
Stack level 0, frame at 0xffd719a0:
pc = 0xfdfed3c in __libc_start_main; saved pc 0x0
called by frame at 0x0
Arglist at 0xffd71970, args:
Locals at 0xffd71970, Previous frame's sp is 0xffd719a0
Saved registers:
r24 at 0xffd71980, r25 at 0xffd71984, r26 at 0xffd71988, r27 at 0xffd7198c,
r28 at 0xffd71990, r29 at 0xffd71994, r30 at 0xffd71998, r31 at 0xffd7199c,
pc at 0xffd719a4, lr at 0xffd719a4
Formatting kinda messed up there, note the use of "info frame" to inspect frames, and "frame #" to navigate your context to another context (up and down the stack)
bt also show's an abbreviated stack to help out.