Trying to set breakpoint to class member, but I get syntax error or error saying "could not resolve".
Target variable is abc of class pointer xyz.
0:000> ??##c++(xyz->abc)
short 0n812
0:000> dt xyz
Local var # rbx Type Prop*
+0x000 __VFN_table : 0x00007ffd`b9229510
+0x058 abc : 0n0147
Attempts:
0:000> bu ***!***::function+0x56 ".if (##c++(xyz->abc))==147) {.echo 'hit'} .else {gc}"
^ Syntax error in '.if ....'
I want to set breakpoint when pProp->ydu value is equal to 147.
source
#include <iostream>
using namespace std;
class Rectangle {
int width, height;
public:
Rectangle (int x ,int y) : width(x) , height(y) {}
int area (void) {return (width*height);}
};
void CalcArea(int i,int j,Rectangle *rect) {
cout << "Area for Rect("<<i<<","<<j<<") = "<<rect->area()<< endl;
}
int main () {
int i,j;
for(i=10,j=10; (i<100 && j<100); i+=5,j+=10){
Rectangle rect (i,j);
CalcArea(i,j,&rect);
}
return 0;
}
compiled with vs 2017 community cmd prompt
cl /EHsc /W4 /analyze /Zi /Od classy.cpp /link /release
executed
classy.exe
Area for Rect(10,10) = 100
Area for Rect(15,20) = 300
Area for Rect(20,30) = 600
Area for Rect(25,40) = 1000
Area for Rect(30,50) = 1500
Area for Rect(35,60) = 2100
Area for Rect(40,70) = 2800
Area for Rect(45,80) = 3600
Area for Rect(50,90) = 4500
loaded in windbg and set a conditional break point and run
:\>cdb classy.exe
Microsoft (R) Windows Debugger Version 10.0.16299.15 X86
0:000> bu classy!CalcArea ".if(((##c++(rect->width))==0n40)){ .echo \"hit\" } .else{gc}"
0:000> bl
0 e 00841100 0001 (0001) 0:****
classy!CalcArea ".if( ((##c++(rect->width))==0n40) ) { .echo \"hit\" } .else {gc}"
0:000> g
Area for Rect(10,10) = 100
Area for Rect(15,20) = 300
Area for Rect(20,30) = 600
Area for Rect(25,40) = 1000
Area for Rect(30,50) = 1500
Area for Rect(35,60) = 2100
hit <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
eax=00000028 ebx=7ffdf000 ecx=002dff08 edx=00000046 esi=008c9bf0 edi=000d8b28
eip=00841100 esp=002dfef8 ebp=002dff18 iopl=0 nv up ei ng nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000287
classy!CalcArea:
00841100 55 push ebp
0:000> ?? rect
class Rectangle * 0x002dff08
+0x000 width : 0n40 <<<<<<<<<<<<<<<
+0x004 height : 0n70
0:000>
Related
class Test
{
public:
Test()
{
std::cout << "ctor" << std::endl;
ptr = new int(5);
*(ptr + 1) = 42;
};
~Test() { std::cout << "dtor" << std::endl; };
int* ptr;
};
void foo()
{
Test test;
}
int main()
{
foo();
return 0;
}
In the above example I want to set a data breakpoint at the address pointed to by ptr plus an offset (4 bytes in this case), to detect the heap corruption.
After I break inside the Test constructor, I have tried using the following to set the breakpoint but have failed:
0:000> ba w 4 (ptr + 4)
Bp expression '(ptr + 4)' could not be resolved, adding deferred bp
*** Bp expression '(ptr + 4)' contains symbols not qualified with module name
If I put the address manually then it obviously works. But I don't want to hard code the address in the command because I am doing this as part of a script and the address inside ptr can change every time.
What is the correct syntax for adding the data breakpoint without hardcoding the address?
You need ##c++() or .expr /s c++:
0:000> bp MemoryBreak!Test::Test
0:000> g
Breakpoint 2 hit
MemoryBreak!Test::Test:
[...]
0:000> l+t
Source options are 1:
1/t - Step/trace by source line
0:000> p
MemoryBreak!Test::Test+0x42:
0:000> ? ##c++(ptr)
Evaluate expression: 2790386541360 = 00000289`afffa330
0:000> ba w 4 ##c++(ptr)+4
0:000> bl
1 e Disable Clear 00007ff7`5fac2720 [C:\....cpp # 23] 0001 (0001) 0:**** MemoryBreak!main
2 e Disable Clear 00007ff7`5fac1fa0 [C:\....cpp # 6] 0001 (0001) 0:**** MemoryBreak!Test::Test
3 e Disable Clear 00000289`afffa334 w 4 0001 (0001) 0:****
0:000> g
Breakpoint 3 hit
MemoryBreak!Test::Test+0xa7:
I want to do something like break at Process!Function+0x66 but only if [RDX + 0x01c] == 1.
What would the syntax of breakpoint like this be?
evaluate with ? Process!Function+0x66
copy the result for using in breakpoint 0x12345678`90abcdef
bp 0x12345678`90abcdef ".if ( poi(#rdx+0x1c) != 1) {gc}"
a sample flow
0:000> ? msvcrt!memcpy+0x40
Evaluate expression: 140735863146304 = 00007fff`9f214740
0:000> u msvcrt!memcpy+0x40 l1
msvcrt!memcpy+0x40:
00007fff`9f214740 8a0411 mov al,byte ptr [rcx+rdx]
0:000> bp 00007fff`9f214740 " .if( poi(#rcx+#rdx) != 0x20) {gc}"
0:000> bl 0 e 00007fff`9f214740 0001 (0001) msvcrt!memcpy+0x40 ".if( poi(#rcx+#rdx) != 0x20) {gc}"
0:000> g
Microsoft (R) Windows Debugger Version 10.0.17763.132 AMD64
msvcrt!memcpy+0x40:
00007fff`9f214740 8a0411 mov al,byte ptr [rcx+rdx] ds:000001d6`1ebc6ac1=20
0:000> .lastevent
Last event: 724.1718: Hit breakpoint 0
debugger time: Wed Jun 16 00:25:26.965 2021
0:000> g
msvcrt!memcpy+0x40:
00007fff`9f214740 8a0411 mov al,byte ptr [rcx+rdx] ds:000001d6`20407c4a=20
0:000> .lastevent
Last event: 724.1718: Hit breakpoint 0
debugger time: Wed Jun 16 00:25:45.283 2021
0:000>
I have following c++ code
int myvar=1;
void test1( int j)
{
int b=j+1;
}
void main()
{
myvar=2;
test1(50);
myvar=3;
test1(100);
myvar=6;
test1(200);
}
I'm trying to set a breakpoint that stops if myvar is greater than 4 when running function test1.
Here is my breakpoint:
bp test!test1 ".if ( poi(myvar)>0n4) {} .else {gc} "
however, it stops every time test1 is executed...
The executable file is called test.exe, a 64 bit application.
Any suggestion would be appreciated.
A little bit of debugging in the breakpoint reveals what is happening:
0 e Disable Clear 00007ff6`77e11410 [f:\projects\windbg_help\main.cpp # 4] 0001 (0001) 0:**** windbg_help!test1 "?? myvar; r $t1=myvar; ?? #$t1; r $t2=poi(myvar); ?? #$t2; .if (dwo(myvar) > 0n4) {.echo yes; gc} .else {.echo no; gc} "
I set the t1 temp register to myvar, and t2 temp register to the contents of myvar, then display them:
0:000> g
int 0n2
unsigned int64 0x00007ff6`77e1c000
unsigned int64 0xffffffff`00000002
no
int 0n3
unsigned int64 0x00007ff6`77e1c000
unsigned int64 0xffffffff`00000003
no
int 0n6
unsigned int64 0x00007ff6`77e1c000
unsigned int64 0xffffffff`00000006
yes
ModLoad: 00007ff9`c8520000 00007ff9`c8531000 C:\WINDOWS\System32\kernel.appcore.dll
ModLoad: 00007ff9`c9da0000 00007ff9`c9e3e000 C:\WINDOWS\System32\msvcrt.dll
ModLoad: 00007ff9`c9aa0000 00007ff9`c9bc2000 C:\WINDOWS\System32\RPCRT4.dll
ntdll!NtTerminateProcess+0x14:
00007ff9`cc5cfcd4 c3 ret
Notice how poi(myvar) is returning a 64bit value, and the upper 32bits are set. You poi(myvar) > 0n4 comparison is saying:
if (0xffffffff0000000? > 4) then { always true }
use dwo(myvar) instead to read only the 32bit contents
So I have a crash dump, and using WinDbg, I am able to get the stack trace. However, it appears to be skipping every other function. For instance if the actual code is:
void a()
{
b();
}
void b()
{
c();
}
void c(){}
The stack trace would have a, and c in the stack frame and not b. Is this expected behavior, and is there something I can do to see the entire stack trace? I have using the command "kn" to view the stack trace.
The compiler may optimize the code. One optimization is to inline methods so that call and ret statements and everything related (push, pop etc.) are removed.
For demonstration purposes, I have added a std::cout statement to your code which we can identify so that the full code now reads
#include "stdafx.h"
#include <iostream>
void c()
{
std::cout << "Hello world";
}
void b()
{
c();
}
void a()
{
b();
}
int _tmain(int argc, _TCHAR* argv[])
{
a();
return 0;
}
Walkthrough in debug build
In a typical debug build, there are no optimizations and you can see the methods. To follow the example, start WinDbg first and run the executable via File/Open executable....
0:000> .symfix e:\debug\symbols
0:000> lm
start end module name
010d0000 010f3000 OptimizedInlined (deferred)
...
0:000> ld OptimizedInlined
Symbols loaded for OptimizedInlined
0:000> x OptimizedInlined!c
010e50c0 OptimizedInlined!c (void)
0:000> bp OptimizedInlined!c
0:000> bl
0 e 010e50c0 0001 (0001) 0:**** OptimizedInlined!c
0:000> g
Breakpoint 0 hit
eax=cccccccc ebx=7efde000 ecx=00000000 edx=00000001 esi=00000000 edi=003ffa00
eip=010e50c0 esp=003ff930 ebp=003ffa00 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
OptimizedInlined!c:
010e50c0 55 push ebp
0:000> u eip L18
OptimizedInlined!c [e:\...\optimizedinlined.cpp # 8]:
010e50c0 55 push ebp
010e50c1 8bec mov ebp,esp
010e50c3 81ecc0000000 sub esp,0C0h
010e50c9 53 push ebx
010e50ca 56 push esi
010e50cb 57 push edi
010e50cc 8dbd40ffffff lea edi,[ebp-0C0h]
010e50d2 b930000000 mov ecx,30h
010e50d7 b8cccccccc mov eax,0CCCCCCCCh
010e50dc f3ab rep stos dword ptr es:[edi]
010e50de 6854ca0e01 push offset OptimizedInlined!`string' (010eca54)
010e50e3 a1e4000f01 mov eax,dword ptr [OptimizedInlined!_imp_?coutstd (010f00e4)]
010e50e8 50 push eax
010e50e9 e8c9c1ffff call OptimizedInlined!ILT+690(??$?6U?$char_traitsDstdstdYAAAV?$basic_ostreamDU?$char_traitsDstd (010e12b7)
010e50ee 83c408 add esp,8
010e50f1 5f pop edi
010e50f2 5e pop esi
010e50f3 5b pop ebx
010e50f4 81c4c0000000 add esp,0C0h
010e50fa 3bec cmp ebp,esp
010e50fc e838c2ffff call OptimizedInlined!ILT+820(__RTC_CheckEsp) (010e1339)
010e5101 8be5 mov esp,ebp
010e5103 5d pop ebp
010e5104 c3 ret
Walkthrough release build
In the release build, see how things change. The WinDbg commands are the same, but the methods c(), b() and a() cannot be found. The std::cout method call has been inlined into wmain():
0:000> .symfix e:\debug\symbols
0:000> lm
start end module name
01360000 01367000 OptimizedInlined (deferred)
...
0:000> ld OptimizedInlined
Symbols loaded for OptimizedInlined
0:000> x OptimizedInlined!c
0:000> x OptimizedInlined!b
0:000> x OptimizedInlined!a
0:000> x OptimizedInlined!wmain
013612a0 OptimizedInlined!wmain (int, wchar_t **)
0:000> bp OptimizedInlined!wmain
0:000> bl
0 e 013612a0 0001 (0001) 0:**** OptimizedInlined!wmain
0:000> g
Breakpoint 0 hit
eax=6142f628 ebx=00000000 ecx=0087a6e8 edx=0019def8 esi=00000001 edi=00000000
eip=013612a0 esp=0042f8f8 ebp=0042f934 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
OptimizedInlined!wmain:
013612a0 8b0d44303601 mov ecx,dword ptr [OptimizedInlined!_imp_?coutstd (01363044)] ds:002b:01363044={MSVCP120!std::cout (646c6198)}
0:000> u eip L4
OptimizedInlined!wmain [e:\...\optimizedinlined.cpp # 23]:
013612a0 8b0d44303601 mov ecx,dword ptr [OptimizedInlined!_imp_?coutstd (01363044)]
013612a6 e895040000 call OptimizedInlined!std::operator<<<std::char_traits<char> > (01361740)
013612ab 33c0 xor eax,eax
013612ad c3 ret
Like already mentioned by Lieven. this is a case where compiler is making function calls inline.
There are 2 things that i would suggest:
a. If you have control over the code and build environment. Build a non-optimized/debug version of the code. This will ensure that compiler does not inline functions on its own.
b. In WinDBG you can see the disassembly under View --> Disassembly. Here you can see that code for function b() is actually in lined under a().
In case you are comfortable with assembly debugging you can get the value of locals as well :)
How do I use and benefit from the GFlags setting Enable heap tagging by DLL?
I know how to activate the setting for a process, but I did not find useful information in the output of !heap -t in WinDbg. I was expecting some output like this:
0:000> !heap -t
Index Address Allocated by
1: 005c0000 MyDll.dll
2: 006b0000 AnotherDll.dll
so that I can identify which heap was created by which DLL and then e.g. identify the source of a memory leak.
Is this a misunderstanding of the term "heap tagging by DLL" or do I need some more commands to get to the desired result?
My research so far:
I googled for a tutorial on this topic, but I couldn't find a detailed description
I read WinDbg's .hh !heap but it's not explained there in detail as well. Tag is only used in !heap -b
again a very late answer
to benefit from HeapTagging you need to create a tag first in your code.
as far as i know (that is upto xp-sp3) there were no Documented APIS to Create a tag
(I havent mucked with heap since then so i am not aware of latest apis in os > vista Rewrites were done to heap manager so probably many of the ^^^features^^^ that i post below might have been corrected or bettered or bugs removed )
in xp-sp3 you can use undocumented RtlCreateTagHeap to create a new tag to either Process Heap or Private Heap
and after you create tha tag you need to set the global flag 8000 | 800
htg - Enable heap tagging
htd - Enable heap tagging by DLL
and theoratically all allocs and frees must get tagged .
but practically only allocations > 512 kB gets tagged in xp-sp3 with these basic steps
it either is a bug or a feature that limits tagging to allocations and frees > 512 kB
HeapAlloc goes through ZwAllocateVirtualMemory in case of Allocations > 512 kB in 32 bit process refer HeapCreate / HeapAlloc Documentation in msdn
and as a debuging aid you can patch ntdll.dll on the fly to enable tagging for all Allocations and frees .
below is a sample code that demonstrates the tagging and how to view it all in windbg
compile using cl /Zi /analyze /W4 <src> /link /RELEASE
use windbg to execute the app and watch tagging with !heap * -t command
#include <windows.h>
#include <stdio.h>
//heaptags are kinda broken or they are intentionally
//given only to allocations > 512 kb // allocation > 512 kb
//go through VirtualAlloc Route for Heap created with maxsize
//set to 0 uncomment ALLOCSIZE 0xfdfd2 and recompile to watch
// tagging increase by 100% with ALLOCSIZE 0xfdfd1 only 50 allocs
// and frees that are > 512 kB will be tagged these magic numbers
// are related to comment in HeapCreate Documentation that state
// slightly less than 512 kB will be allocated for 32 bit process
// tagging can be dramatically increased by patching ntdll when
// stopped on system breakpoint patch 7c94b8a4 (xpsp3 ntdll.dll)
// use the below command in windbg for finding the offset of pattern
// command must be in single line no line breaks
// .foreach /pS 4 /ps 4 ( place { !grep -i -e call -c
// "# call*RtlpUpdateTagEntry 7c900000 l?20000" } ) { ub place }
// the instruction we are searching to patch is
//7c94b8a1 81e3ff0fffff and ebx,0FFFF0FFFh
// patch 0f to 00 at system breakpoint with eb 7c94b8a1+3 00
#define BUFFERSIZE 100
#define ALLOCSIZE 0xfdfd1
//#define ALLOCSIZE 0xfdfd2
typedef int ( __stdcall *g_RtlCreateTagHeap) (
HANDLE hHeap ,
void * unknown,
wchar_t * BaseString,
wchar_t * TagString
);
void HeapTagwithHeapAllocPrivate()
{
PCHAR pch[BUFFERSIZE] = {};
HANDLE hHeap = 0;
ULONG tag1 = 0;
ULONG tag2 = 0;
ULONG tag3 = 0;
ULONG tag4 = 0;
ULONG tag5 = 0;
g_RtlCreateTagHeap RtlCreateTagHeap = 0;
HMODULE hMod = LoadLibrary("ntdll.dll");
if(hMod)
{
RtlCreateTagHeap = (g_RtlCreateTagHeap)
GetProcAddress( hMod,"RtlCreateTagHeap");
}
if (hHeap == 0)
{
hHeap = HeapCreate(0,0,0);
if (RtlCreateTagHeap != NULL)
{
tag1 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag1");
tag2 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag2");
tag3 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag3");
tag4 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag4");
}
}
HANDLE DefHeap = GetProcessHeap();
if ( (RtlCreateTagHeap != NULL) && (DefHeap != NULL ))
{
tag5 = RtlCreateTagHeap (DefHeap,0,L"HeapTag!",L"MyTag5");
for ( int i = 0; i < BUFFERSIZE ; i++ )
{
pch[i]= (PCHAR) HeapAlloc( DefHeap,HEAP_ZERO_MEMORY| tag5, 1 );
HeapFree(DefHeap,NULL,pch[i]);
}
}
if(hHeap)
{
for ( int i = 0; i < BUFFERSIZE ; i++ )
{
pch[i]= (PCHAR) HeapAlloc( hHeap,HEAP_ZERO_MEMORY| tag1, 1 );
//lets leak all allocs patch ntdll to see the tagging details
//HeapFree(hHeap,NULL,pch[i]);
}
for ( int i = 0; i < BUFFERSIZE ; i++ )
{
pch[i]= (PCHAR) HeapAlloc( hHeap,HEAP_ZERO_MEMORY| tag2, 100 );
// lets leak 40% allocs patch ntdll to see the tagging details
if(i >= 40)
HeapFree(hHeap,NULL,pch[i]);
}
// slightly less than 512 kb no tagging
for ( int i = 0; i < BUFFERSIZE / 2 ; i++ )
{
pch[i]= (PCHAR) HeapAlloc(
hHeap,HEAP_ZERO_MEMORY| tag3, ALLOCSIZE / 2 );
}
// > 512 kb default tagging
for ( int i = BUFFERSIZE / 2; i < BUFFERSIZE ; i++ )
{
pch[i]= (PCHAR) HeapAlloc(
hHeap,HEAP_ZERO_MEMORY | tag4 ,ALLOCSIZE );
}
for (int i =0 ; i < BUFFERSIZE ; i++)
{
HeapFree(hHeap,NULL,pch[i]);
}
}
}
void _cdecl main()
{
HeapTagwithHeapAllocPrivate();
}
the compiled exe to be run with windbg as below
DEFAULT execution and inspection
**only 50 tags will be visible all of them are > 512 kB Allocations
cdb -c "g;!heap * -t;q" newheaptag.exe | grep Tag**
heaptag:\>cdb -c "g;!heap * -t;q" newheaptag.exe | grep Tag
Tag Name Allocs Frees Diff Allocated
Tag Name Allocs Frees Diff Allocated
Tag Name Allocs Frees Diff Allocated
0004: HeapTag!MyTag4 50 50 0 0
patching ntdll on system breakpoint should make all tags visible
eb = write byte
patch and run the exe on exit inspect heaps with tags
cdb -c "eb 7c94b8a1+3 00;g;!heap * -t;q" newheaptag.exe | grep Tag
heaptag:\>cdb -c "eb 7c94b8a1+3 00;g;!heap * -t;q" newheaptag.exe | grep Tag
Tag Name Allocs Frees Diff Allocated
0012: HeapTag!MyTag5 100 100 0 0 <-our tag in process heap
Tag Name Allocs Frees Diff Allocated
Tag Name Allocs Frees Diff Allocated
0001: HeapTag!MyTag1 100 0 100 3200 <--- leak all
0002: HeapTag!MyTag2 100 60 40 5120 <--- leak 40 %
0003: HeapTag!MyTag3 50 50 0 0 <--- clean < 512 kB
0004: HeapTag!MyTag4 50 50 0 0 <----clean > 512 kB