Bugtation No.123
Wednesday, August 11th, 2010The whole code is a series of balanced defects. Fix one and the program crashes.
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -
The whole code is a series of balanced defects. Fix one and the program crashes.
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -
assert (*p + *p == 2 * (*p) + 1); // for sufficiently fast processorsThe reinterpretation of 1 + 1 = 3, Dmitry Vostokov
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -
When we have a software trace we read it in two directions. The first one is to deconstruct it into a linear ordered source code based on PLOT fragments. The second direction is to construct an interpretation that serve as an explanation for reported software behaviour. During the interpretive reading we remove irrelevant information, compress relevant activity regions and construct the new fictional software trace based on discovered patterns and our problem description.

- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -
Befind every trace and its messages is source code:

Borrowing the acronym PLOT (Program Lines of Trace) we now try to discern basic source code patterns that give rise to simple message patterns in software traces. There are only a few distinct PLOTs and the ability to mentally map trace statements to source code is crucial to software trace reading and comprehension. More about that in subsequent parts. More complex message patterns (for example, specific message blocks or correlated messages) arise from supportable and maintainable realizations of architectural, design and implementation patterns and will be covered in another post series.
I was thinking about acronym SLOT (Source Lines of Trace) but decided to use PLOT because it metaphorically bijects into literary theory and narrative plots.
Forthcoming CDF and ETW Software Trace Analysis: Practical Foundations
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org
OpenTask to offer first 3 volumes of Memory Dump Analysis Anthology in one set:

The set is available exclusively from OpenTask e-Commerce web site starting from June. Individual volumes are also available from Amazon, Barnes & Noble and other bookstores worldwide.
Product information:
Information about individual volumes:
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -
Plan to start providing training and seminars in my free time. If you are interested please answer these questions (you can either respond here in comments or use this form for private communication http://www.dumpanalysis.org/contact):
Additional topics of expertise that can be integrated into training include Source Code Reading and Analysis, Debugging, Windows Architecture, Device Drivers, Troubleshooting Tools Design and Implementation, Multithreading, Deep Down C and C++, x86 and x64 Assembly Language Reading.
Looking forward to your responses. Any suggestions are welcome.
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -
As soon as I dug out the 3rd edition of this book to download samples for my internal projects I found that the new edition was published this month! I read all of them and now ordering the 4th edition:
Windows System Programming (4th Edition)
Actually I re-read the 2nd edition of Johnson M. Hart’s book when looking for a job in 2003 and coupled with timely reading of John Robbings’ book Debugging Applications (1st 2000 edition) secured my landing in Dublin East Point Business Park.
This book is an essential reading for Windows memory dump analysts, software maintenance and escalation engineers, software defect researchers and software tool developers. It lucidly describes and succinctly illustrates user-land Windows API with practical console mode samples in plain C. This book is especially valuable for software engineers coming from UNIX background because the author draws various parallels and provides maps between UNIX / Pthreads and Win32 / 64 APIs. Highly recommended! Plan to post an Amazon review when I get the copy of the 4th edition.
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -
This is a revised, edited, cross-referenced and thematically organized volume of selected DumpAnalysis.org blog posts about crash dump analysis and debugging written in July 2009 - January 2010 for software engineers developing and maintaining products on Windows platforms, quality assurance engineers testing software on Windows platforms and technical support and escalation engineers dealing with complex software issues. The fourth volume features:
- 13 new crash dump analysis patterns
- 13 new pattern interaction case studies
- 10 new trace analysis patterns
- 6 new Debugware patterns and case study
- Workaround patterns
- Updated checklist
- Fully cross-referenced with Volume 1, Volume 2 and Volume 3
- New appendixes
Product information:

Back cover features memory space art image: Internal Process Combustion.
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -
See the greeting card on the portal together with New Year’s Eve code analysis puzzle:
DumpAnalysis.org Wishes Happy New Year 7DA!
- Dmitry Vostokov @ DumpAnalysis.org -
When looking at crash dumps it is good to keep an eye on new API that might surface on stack traces and in component relationships. Plan to order this book tomorrow and put my reading notes on Software Generalist blog:
Introducing Windows® 7 for Developers
- Dmitry Vostokov @ DumpAnalysis.org -
“Memory dumps are facts.”
I’m very excited to announce that Volume 3 is available in paperback, hardcover and digital editions:
Memory Dump Analysis Anthology, Volume 3
In two weeks paperback edition should also appear on Amazon and other bookstores. Amazon hardcover edition is planned to be available in January 2010.
The amount of information was so voluminous that I had to split the originally planned volume into two. Volume 4 should appear by the middle of February together with Color Supplement for Volumes 1-4.
- Dmitry Vostokov @ DumpAnalysis.org -
What prompted me to found this discipline (that is supposed to be a sister discipline of software criminology, software security, secure code construction and software defect construction) is understanding that some software components are innocent victims of other component coding mistakes or deliberate subversion and some start as a part of crimeware but eventually become victims themselves (they crash, hang, spike, leak, are dumped, subverted, etc.). I would also like to borrow and reuse the neglected term victimware here in a broad sense. More on this later as I have to switch to software trace analysis patterns.
- Dmitry Vostokov @ DumpAnalysis.org -
This is a revised, edited, cross-referenced and thematically organized volume of selected DumpAnalysis.org blog posts about crash dump analysis and debugging written in October 2008 - June 2009 for software engineers developing and maintaining products on Windows platforms, quality assurance engineers testing software on Windows platforms and technical support and escalation engineers dealing with complex software issues. The third volume features:
- 15 new crash dump analysis patterns
- 29 new pattern interaction case studies
- Trace analysis patterns
- Updated checklist
- Fully cross-referenced with Volume 1 and Volume 2
- New appendixes
Product information:

Back cover features 3D computer memory visualization image.
- Dmitry Vostokov @ DumpAnalysis.org -
Having discussed dereference fixpoints we come back to the quiz code and see what happens when we execute it after compilation as default Debug target with Debug Information Format set to Program Database to avoid extra stack space allocation:
int _tmain(int argc, _TCHAR* argv[])
{
char c;
char* pc = &c;
while(1)
{
*pc = 0;
pc++;
}
return 0;
}
Expecting crashes I created the following key HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ Windows Error Reporting \ LocalDumps with the following values: DumpFolder (REG_EXPAND_SZ) and DumpType (2, Full).
When running the compiled program I noticed that it crashed according to my expectations. The saved dump StackErasure.exe.2096.dmp confirmed that the crash was due to the stack underflow when it hit the base address:
0:000> r
eax=002c0000 ebx=7efde000 ecx=00000001 edx=002c0000 esi=00000000 edi=00000000
eip=00e11039 esp=002bf7c4 ebp=002bf7d4 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
StackErasure!wmain+0x29:
00e11039 c60200 mov byte ptr [edx],0 ds:002b:002c0000=??
0:000> !teb
TEB at 7efdd000
ExceptionList: 002bf810
StackBase: 002c0000
StackLimit: 002be000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7efdd000
EnvironmentPointer: 00000000
ClientId: 00000830 . 00000a78
RpcHandle: 00000000
Tls Storage: 7efdd02c
PEB Address: 7efde000
LastErrorValue: 0
LastStatusValue: 0
Count Owned Locks: 0
HardErrorMode: 0
The loop from source code is highlighted in blue:
0:000> uf wmain
StackErasure!wmain:
00e11010 push ebp
00e11011 mov ebp,esp
00e11013 sub esp,10h
00e11016 mov eax,0CCCCCCCCh
00e1101b mov dword ptr [ebp-10h],eax
00e1101e mov dword ptr [ebp-0Ch],eax
00e11021 mov dword ptr [ebp-8],eax
00e11024 mov dword ptr [ebp-4],eax
00e11027 lea eax,[ebp-5]
00e1102a mov dword ptr [ebp-10h],eax
StackErasure!wmain+0x1d:
00e1102d mov ecx,1
00e11032 test ecx,ecx
00e11034 je StackErasure!wmain+0x37 (00e11047)
StackErasure!wmain+0x26:
00e11036 mov edx,dword ptr [ebp-10h]
00e11039 mov byte ptr [edx],0
00e1103c mov eax,dword ptr [ebp-10h]
00e1103f add eax,1
00e11042 mov dword ptr [ebp-10h],eax
00e11045 jmp StackErasure!wmain+0x1d (00e1102d)
StackErasure!wmain+0x37:
00e11047 xor eax,eax
00e11049 push edx
00e1104a mov ecx,ebp
00e1104c push eax
00e1104d lea edx,[StackErasure!wmainCRTStartup+0x10 (00e11060)]
00e11053 call StackErasure!__tmainCRTStartup+0x50 (00e110c0)
00e11058 pop eax
00e11059 pop edx
00e1105a mov esp,ebp
00e1105c pop ebp
00e1105d ret
We see that our char variable ‘c’ is located at EBP-5 and the pointer ‘pc’ is located at EBP-10 (in another words ‘c’ follows ‘pc’ in memory):
00e11027 lea eax,[ebp-5]
00e1102a mov dword ptr [ebp-10h],eax
Both locations were initialized to 0xCCCCCCCC:
00e11016 mov eax,0CCCCCCCCh
00e1101b mov dword ptr [ebp-10h],eax
00e1101e mov dword ptr [ebp-0Ch],eax
00e11021 mov dword ptr [ebp-8],eax ; this ends with EBP-5
00e11024 mov dword ptr [ebp-4],eax
The memory layout before the start of the loop is depicted on the following diagram in the style of Windows Debugging: Practical Foundations book:

At the crash point we have the following final memory layout:

We can see it from the raw stack:
0:000> db esp
002bf7c4 00 00 2c 00 cc cc cc cc-cc cc cc 00 00 00 00 00
002bf7d4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
002bf7e4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
002bf7f4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
002bf804 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
002bf814 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
002bf824 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
002bf834 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
or in pointer-sized (double word) values where we can see little endian effects (compare 00 00 2c 00 with 002c0000):
0:000> dp esp
002bf7c4 002c0000 cccccccc 00cccccc 00000000
002bf7d4 00000000 00000000 00000000 00000000
002bf7e4 00000000 00000000 00000000 00000000
002bf7f4 00000000 00000000 00000000 00000000
002bf804 00000000 00000000 00000000 00000000
002bf814 00000000 00000000 00000000 00000000
002bf824 00000000 00000000 00000000 00000000
002bf834 00000000 00000000 00000000 00000000
The loop code erases stack starting from EBP-5 until it hits the base address.
Now we change Basic Runtime Checks in Code Generation properties to Default, recompile and launch the project. Suddenly it no longer crashes but loops infinitely (shown in blue):
0:000> bp wmain
0:000> g
[...]
0:000> uf wmain
StackErasure!wmain:
00d01010 push ebp
00d01011 mov ebp,esp
00d01013 sub esp,8
00d01016 lea eax,[ebp-5]
00d01019 mov dword ptr [ebp-4],eax
StackErasure!wmain+0xc:
00d0101c mov ecx,1
00d01021 test ecx,ecx
00d01023 je StackErasure!wmain+0x26 (00d01036)
StackErasure!wmain+0x15:
00d01025 mov edx,dword ptr [ebp-4]
00d01028 mov byte ptr [edx],0
00d0102b mov eax,dword ptr [ebp-4]
00d0102e add eax,1
00d01031 mov dword ptr [ebp-4],eax
00d01034 jmp StackErasure!wmain+0xc (00d0101c)
StackErasure!wmain+0x26:
00d01036 xor eax,eax
00d01038 mov esp,ebp
00d0103a pop ebp
00d0103b ret
At first it looks strange but if we look at the stack memory layout we would see that ‘pc’ pointer follows ‘c’ (the opposite to the memory layout discussed above):
00d01016 lea eax,[ebp-5]
00d01019 mov dword ptr [ebp-4],eax

0:000> dp esp
002dfb90 00d014ee 002dfb93 002dfbe4 00d01186
002dfba0 00000001 00081d70 00081df8 5a16a657
002dfbb0 00000000 00000000 7ffdb000 00000000
002dfbc0 00000000 00000000 00000000 002dfbac
002dfbd0 000001bb 002dfc28 00d06e00 5aed06eb
002dfbe0 00000000 002dfbec 00d0105f 002dfbf8
002dfbf0 77844911 7ffdb000 002dfc38 7791e4b6
002dfc00 7ffdb000 705b3701 00000000 00000000
Therefore, when the pointer at EBP-4 is incremented by 1 during the first loop iteration it becomes a dereference fixpoint:
0:000> bp 00d0101c
0:000> g
Breakpoint 1 hit
eax=002dfb93 ebx=7ffdb000 ecx=00000001 edx=00081df8 esi=00000000 edi=00000000
eip=00d0101c esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
StackErasure!wmain+0xc:
00d0101c b901000000 mov ecx,1
0:000> t
eax=002dfb93 ebx=7ffdb000 ecx=00000001 edx=00081df8 esi=00000000 edi=00000000
eip=00d01021 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
StackErasure!wmain+0x11:
00d01021 85c9 test ecx,ecx
0:000> t
eax=002dfb93 ebx=7ffdb000 ecx=00000001 edx=00081df8 esi=00000000 edi=00000000
eip=00d01023 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x13:
00d01023 7411 je StackErasure!wmain+0x26 (00d01036) [br=0]
0:000> t
eax=002dfb93 ebx=7ffdb000 ecx=00000001 edx=00081df8 esi=00000000 edi=00000000
eip=00d01025 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x15:
00d01025 8b55fc mov edx,dword ptr [ebp-4] ss:0023:002dfb94=002dfb93
0:000> t
eax=002dfb93 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d01028 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x18:
00d01028 c60200 mov byte ptr [edx],0 ds:0023:002dfb93=00
0:000> t
eax=002dfb93 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d0102b esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x1b:
00d0102b 8b45fc mov eax,dword ptr [ebp-4] ss:0023:002dfb94=002dfb93
0:000> t
eax=002dfb93 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d0102e esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x1e:
00d0102e 83c001 add eax,1
0:000> t
eax=002dfb94 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d01031 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x21:
00d01031 8945fc mov dword ptr [ebp-4],eax ss:0023:002dfb94=002dfb93
0:000> t
eax=002dfb94 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d01034 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x24:
00d01034 ebe6 jmp StackErasure!wmain+0xc (00d0101c)
0:000> dp ebp-4 l1
002dfb94 002dfb94

During the second iteration the assignment of zero to ‘*pc’ clears the first byte of ‘pc’:
0:000> t
Breakpoint 1 hit
eax=002dfb94 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d0101c esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0xc:
00d0101c b901000000 mov ecx,1
0:000> t
eax=002dfb94 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d01021 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x11:
00d01021 85c9 test ecx,ecx
0:000> t
eax=002dfb94 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d01023 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x13:
00d01023 7411 je StackErasure!wmain+0x26 (00d01036) [br=0]
0:000> t
eax=002dfb94 ebx=7ffdb000 ecx=00000001 edx=002dfb93 esi=00000000 edi=00000000
eip=00d01025 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x15:
00d01025 8b55fc mov edx,dword ptr [ebp-4] ss:0023:002dfb94=002dfb94
0:000> t
eax=002dfb94 ebx=7ffdb000 ecx=00000001 edx=002dfb94 esi=00000000 edi=00000000
eip=00d01028 esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x18:
00d01028 c60200 mov byte ptr [edx],0 ds:0023:002dfb94=94
0:000> t
eax=002dfb94 ebx=7ffdb000 ecx=00000001 edx=002dfb94 esi=00000000 edi=00000000
eip=00d0102b esp=002dfb90 ebp=002dfb98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
StackErasure!wmain+0x1b:
00d0102b 8b45fc mov eax,dword ptr [ebp-4] ss:0023:002dfb94=002dfb00
0:000> dp esp
002dfb90 00d014ee 002dfb00 002dfbe4 00d01186
002dfba0 00000001 00081d70 00081df8 5a16a657
002dfbb0 00000000 00000000 7ffdb000 00000000
002dfbc0 00000000 00000000 00000000 002dfbac
002dfbd0 000001bb 002dfc28 00d06e00 5aed06eb
002dfbe0 00000000 002dfbec 00d0105f 002dfbf8
002dfbf0 77844911 7ffdb000 002dfc38 7791e4b6
002dfc00 7ffdb000 705b3701 00000000 00000000

The new ‘pc’ pointer points to the following region of the stack:
0:000> dp 002dfb00 l100/4
002dfb00 002dfb0c 00000004 00000000 5c008ede
002dfb10 002dfb28 00d0634a 0008128c 5aed018b
002dfb20 000807f8 7790fb66 00000000 7ffdb000
002dfb30 00000000 002dfb40 00d089a6 00d68ab8
002dfb40 002dfb4c 00d019bc 00000008 002dfb84
002dfb50 00d07520 00d07519 5a16a637 00000000
002dfb60 00000000 7ffdb000 00d02b10 00000004
002dfb70 00000002 002dfbd4 00d06e00 5aed007b
002dfb80 fffffffe 002dfb90 00d0769e 002dfba0
002dfb90 00d014ee 002dfb00 002dfbe4 00d01186
002dfba0 00000001 00081d70 00081df8 5a16a657
002dfbb0 00000000 00000000 7ffdb000 00000000
002dfbc0 00000000 00000000 00000000 002dfbac
002dfbd0 000001bb 002dfc28 00d06e00 5aed06eb
002dfbe0 00000000 002dfbec 00d0105f 002dfbf8
002dfbf0 77844911 7ffdb000 002dfc38 7791e4b6
The loop code now starts clearing this region until the pointer becomes the fixpoint again after successive increments and therefore continues to loop indefinitely:
0:000> bc 0 1
0:000> g
(1238.c9c): Break instruction exception - code 80000003 (first chance)
eax=7ffde000 ebx=00000000 ecx=00000000 edx=7796d094 esi=00000000 edi=00000000
eip=77927dfe esp=00a4ff30 ebp=00a4ff5c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
77927dfe cc int 3
0:001> dp 002dfb00 l100/4
002dfb00 0000000c 00000000 00000000 00000000
002dfb10 00000000 00000000 00000000 00000000
002dfb20 00000000 00000000 00000000 00000000
002dfb30 00000000 00000000 00000000 00000000
002dfb40 00000000 00000000 00000000 00000000
002dfb50 00000000 00000000 00000000 00000000
002dfb60 00000000 00000000 00000000 00000000
002dfb70 00000000 00000000 00000000 00000000
002dfb80 00000000 00000000 00000000 00000000
002dfb90 00000000 002dfb1f 002dfbe4 00d01186
002dfba0 00000001 00081d70 00081df8 5a16a657
002dfbb0 00000000 00000000 7ffdb000 00000000
002dfbc0 00000000 00000000 00000000 002dfbac
002dfbd0 000001bb 002dfc28 00d06e00 5aed06eb
002dfbe0 00000000 002dfbec 00d0105f 002dfbf8
002dfbf0 77844911 7ffdb000 002dfc38 7791e4b6
StackErasure that loops indefinitely is available for download.
- Dmitry Vostokov @ DumpAnalysis.org -
Imagine we have the following arrangements in memory:
address: value
where value == address, so we have effectively:
address: address
So when we dereference the address we get the address value. If we name the dereference function as p(address) we get
p(address) = address
That gave me an idea to name after the mathematical notion of a function fixpoint (fixed point).
In C++ we can write the following code to initialize a fixpoint:
void *pc = &pc;
in assembly language:
lea eax, [pc]
mov dword ptr [pc], eax
or using local variables:
lea eax, [ebp-4]
mov dword ptr [ebp-4], eax
Dereference of a fixpoint pointer gives us the same value as its address, for example, using old style conversion:
int *pc = (int *)&pc;
if (pc == (int *)*pc) {
// TRUE
or for C++ purists:
int *pc = reinterpret_cast<int *>(&pc);
if (pc == reinterpret_cast<int *>(*pc)) {
// TRUE
In x86 assembly language we have this comparison:
mov eax,dword ptr [pc]
mov ecx,dword ptr [pc]
cmp ecx,dword ptr [eax]
or using local variables:
mov eax,dword ptr [ebp-4]
mov ecx,dword ptr [ebp-4]
cmp ecx,dword ptr [eax]
Now, having discussed fixpoints, let me ask the question to ponder over this weekend. What would this code do?
int _tmain(int argc, _TCHAR* argv[])
{
char c;
char* pc = &c;
while(1)
{
*pc = 0;
pc++;
}
return 0;
}
Would it produce stack overflow with an exception, or stack underflow with an exception or loop indefinitely? The C++ Standard answer of compiler and platform dependence is not acceptable. I plan to elaborate on this topic on Monday.
The notion of counterfactual debugging (”what if” debugging) was inspired by the so called counterfactual history.
- Dmitry Vostokov @ DumpAnalysis.org -
Although the first 2 parameters are passed via registers RCX and RDX they are saved on a stack as the part of a function prolog (as can be seen in many examples from my book x64 Windows Debugging: Practical Foundations):
0:000> uf arithmetic
FunctionParameters!arithmetic [c:\dumps\wdpf-x64\functionparameters\arithmetic.cpp @ 2]:
2 00000001`40001020 mov dword ptr [rsp+10h],edx
2 00000001`40001024 mov dword ptr [rsp+8],ecx
2 00000001`40001028 push rdi
3 00000001`40001029 mov eax,dword ptr [rsp+10h]
3 00000001`4000102d mov ecx,dword ptr [rsp+18h]
3 00000001`40001031 add ecx,eax
3 00000001`40001033 mov eax,ecx
3 00000001`40001035 mov dword ptr [rsp+18h],eax
4 00000001`40001039 mov eax,dword ptr [rsp+10h]
4 00000001`4000103d add eax,1
4 00000001`40001040 mov dword ptr [rsp+10h],eax
5 00000001`40001044 mov eax,dword ptr [rsp+10h]
5 00000001`40001048 imul eax,dword ptr [rsp+18h]
5 00000001`4000104d mov dword ptr [rsp+18h],eax
7 00000001`40001051 mov eax,dword ptr [rsp+18h]
8 00000001`40001055 pop rdi
8 00000001`40001056 ret
Notice that RDI is saved too. This helps us later in a more complex case. If we put a breakpoint at arithmetic entry we see that WinDbg is not able to get parameters from RCX and RDX:
0:000> bp 00000001`40001020
0:000> g
ModLoad: 000007fe`ff4d0000 000007fe`ff5d8000 C:\Windows\system32\ADVAPI32.DLL
ModLoad: 000007fe`fef80000 000007fe`ff0c3000 C:\Windows\system32\RPCRT4.dll
Breakpoint 0 hit
FunctionParameters!arithmetic:
00000001`40001020 89542410 mov dword ptr [rsp+10h],edx ss:00000000`0012fe88=cccccccc
0:000> kv
Child-SP RetAddr : Args to Child : Call Site
00000000`0012fe78 00000001`400010a5 : cccccccc`cccccccc cccccccc`cccccccc cccccccc`cccccccc cccccccc`cccccccc : FunctionParameters!arithmetic
00000000`0012fe80 00000001`4000137c : 00000000`00000001 00000000`00282960 00000000`00000000 00000000`00000000 : FunctionParameters!main+0×35
00000000`0012fec0 00000001`4000114e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : FunctionParameters!__tmainCRTStartup+0×21c
00000000`0012ff30 00000000`7776be3d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : FunctionParameters!mainCRTStartup+0xe
00000000`0012ff60 00000000`778a6a51 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`0012ff90 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0×1d
This seems correct approach in general because at the time of any other breakpoint in the middle of the code parameter passing registers could be already overwritten, for example, RCX at 0000000140001031. However, as soon as we execute the first two MOV instruction one by one, parameters appear on kv output one by one too:
0:000> t
ModLoad: 000007fe`fd810000 000007fe`fd845000 C:\Windows\system32\apphelp.dll
FunctionParameters!arithmetic+0x4:
00000001`40001024 894c2408 mov dword ptr [rsp+8],ecx ss:00000000`0012fe80=cccccccc
0:000> kv
Child-SP RetAddr : Args to Child : Call Site
00000000`0012fe78 00000001`400010a5 : cccccccc`cccccccc cccccccc`00000001 cccccccc`cccccccc cccccccc`cccccccc : FunctionParameters!arithmetic+0×4
00000000`0012fe80 00000001`4000137c : 00000000`00000001 00000000`00282960 00000000`00000000 00000000`00000000 : FunctionParameters!main+0×35
00000000`0012fec0 00000001`4000114e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : FunctionParameters!__tmainCRTStartup+0×21c
00000000`0012ff30 00000000`7776be3d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : FunctionParameters!mainCRTStartup+0xe
00000000`0012ff60 00000000`778a6a51 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`0012ff90 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0×1d
0:000> t
FunctionParameters!arithmetic+0x8:
00000001`40001028 57 push rdi
0:000> kv
Child-SP RetAddr : Args to Child : Call Site
00000000`0012fe78 00000001`400010a5 : cccccccc`00000001 cccccccc`00000001 cccccccc`cccccccc cccccccc`cccccccc : FunctionParameters!arithmetic+0×8
00000000`0012fe80 00000001`4000137c : 00000000`00000001 00000000`00282960 00000000`00000000 00000000`00000000 : FunctionParameters!main+0×35
00000000`0012fec0 00000001`4000114e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : FunctionParameters!__tmainCRTStartup+0×21c
00000000`0012ff30 00000000`7776be3d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : FunctionParameters!mainCRTStartup+0xe
00000000`0012ff60 00000000`778a6a51 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`0012ff90 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0×1d
Now we come to the more complex example:
1: kd> kv
*** Stack trace for last set context - .thread/.cxr resets it
Child-SP RetAddr : Args to Child : Call Site
fffffa60`166a7020 fffffa60`07e9dbf2 : fffffa80`1dbd8820 00000000`00000000 fffffa80`1ec3b7a8 fffffa60`166a7278 : Driver!DeviceWrite+0xae
fffffa60`166a7050 fffffa60`062ae7cb : 00000000`00000000 fffffa80`1dbd8820 fffffa60`166a7340 fffffa80`1df4f520 : Driver!RawWrite+0×8a
[…]
1: kd> r
Last set context:
rax=000000000083a03b rbx=fffffa801ec3b800 rcx=fffffa8018cdc000
rdx=0000000000000004 rsi=fffffa801ec3b9f0 rdi=0000000005040000
rip=fffffa6007ea006e rsp=fffffa60166a7020 rbp=fffffa801ec3b7a8
r8=fffff6fd400f61e0 r9=000000000083a03b r10=fffffa801ec3b9f8
r11=fffffa801ec3b9f8 r12=fffffa801e7c9000 r13=0000000000000000
r14=000000000038011b r15=fffffa8019891670
iopl=0 nv up ei ng nz na po cy
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010287
Driver!DeviceWrite+0xae:
fffffa60`07ea006e mov rax,qword ptr [rdi+10h] ds:002b:00000000`05040010=????????????????
We know that the first parameter to Write function is a pointer to some structure we want to explore because we see from the disassembly that some member from that structure was used ([rcx+320h]) and it was used as a pointer (assigned to RDI) that was trapping ([rdi+10h]):
1: kd> .asm no_code_bytes
Assembly options: no_code_bytes
1: kd> u Driver!DeviceWrite Driver!DeviceWrite+0xae+10
Driver!DeviceWrite:
fffffa60`07e9ffc0 mov qword ptr [rsp+8],rbx
fffffa60`07e9ffc5 mov qword ptr [rsp+10h],rbp
fffffa60`07e9ffca mov qword ptr [rsp+18h],rsi
fffffa60`07e9ffcf push rdi
fffffa60`07e9ffd0 sub rsp,20h
fffffa60`07e9ffd4 mov rdi,qword ptr [rcx+320h]
[…]
fffffa60`07ea006e mov rax,qword ptr [rdi+10h] ; TRAP
[…]
Unfortunately RCX was not saved on the stack and fffffa80`1dbd8820 from kv was just the value of the saved RBX. This can be double-checked by verifying that parameter+320 doesn’t point to RDI value (05040000) at the time of the trap:
1: kd> dq fffffa80`1dbd8820+320 l1
fffffa80`1dbd8b40 00000000`00020000
Looking at DeviceWrite caller we see that RCX was initialized from RDI:
1: kd> ub fffffa60`07e9dbf2
Driver!RawWrite+0x66:
fffffa60`07e9dbce mov rax,qword ptr [rdi+258h]
fffffa60`07e9dbd5 mov qword ptr [rcx-18h],rax
fffffa60`07e9dbd9 mov rax,qword ptr [rdi+260h]
fffffa60`07e9dbe0 mov qword ptr [rcx-20h],rax
fffffa60`07e9dbe4 mov dword ptr [rdx+10h],ebp
fffffa60`07e9dbe7 mov rdx,rsi
fffffa60`07e9dbea mov rcx,rdi
fffffa60`07e9dbed call Driver!DeviceWrite (fffffa60`07e9ffc0)
We also see that RDI was saved at the function prolog so we can get our real first parameter from the raw stack bearing in mind that 0×20 was subtracted from RSP too:
1: kd> dq esp
fffffa60`166a7020 fffffa80`1ec3b800 00000000`05040000 ; SUB RSP, 20H
fffffa60`166a7030 fffffa60`07edc5dd fffffa60`07ee6f8f ;
fffffa60`166a7040 fffffa80`1ec3b520 fffffa60`07e9dbf2 ; Saved RDI - Return Address
fffffa60`166a7050 fffffa80`1dbd8820 00000000`00000000 ; Saved RBX and RBP
fffffa60`166a7060 fffffa80`1ec3b7a8 fffffa60`166a7278 ; Saved RSI
fffffa60`166a7070 fffffa60`166a7250 fffffa60`01ab5180
fffffa60`166a7080 fffffa80`1e2937c8 fffff800`018a928a
fffffa60`166a7090 00000003`0004000d 00000026`0024000d
We see that saved RDI value +320 points to the right expected address:
1: kd> dq fffffa80`1ec3b520+320 l1
fffffa80`1ec3b840 00000000`05040000
Now we can investigate the structure but this is beyound the scope of this post.
- Dmitry Vostokov @ DumpAnalysis.org -
The digital version of the book is finally available:
x64 Windows Debugging: Practical Foundations
Paperback should be available in 1-2 weeks on Amazon and other stores. When working on the book I fixed errors in the previous x86 version. Errata file for it should be available tomorrow.
- Dmitry Vostokov @ DumpAnalysis.org -
As soon as I wrote my review of the 2nd edition I found out that the 3rd edition was recently published and immediately bought it. I intend to read it from cover to cover again and publish my notes and comments in my reading notebook on Software Generalist blog. The new edition is also bundled with a companion CD.
Programming Language Pragmatics, Third Edition
Hope in one of subsequent editions the author includes my Riemann Programming Language :-)
- Dmitry Vostokov @ DumpAnalysis.org -
On the great divide in modern software factories:
“We have in fact, two kinds of” engineers, “side by side: one that” design, “but do not” code, “and another that” code, “but seldom” design.
Bertrand Russell, Sceptical Essays
- Dmitry Vostokov @ DumpAnalysis.org -
“It is easier to know” programming “in general than to understand one” program “in particular.”
François de La Rochefoucauld, Maxims
- Dmitry Vostokov @ DumpAnalysis.org -