0

I'm using windbg to find the memory of a specific structure in windows. The way is to look at certain values stored in stack and registers in entry function. I notice that register ax holds that value when the program executes one instruction(So I have the address of that instruction). I assume that the value of ax is passed from the address of that structure and my job is to find the address of that instruction. The problem is that the assembly in IDA is very different from the assembly(use u command) in windbg. Thus, I'm unable to target the location from pseudocode in IDA and comparison between assembly in windbg and IDA.

assembly
0000 function start(set break point)
...
...
some instructions without bp
010f instruction A(where ax already holds the value. set break point)

One very straight approach is to stop the machine when ax has the value I want. But there is no straight approach in windbg. Maybe like Thomas said there are other ways. So I try to use j and t command in windbg.

j(@ax=x) 'p';'r;r ax;r rip'

With above command with bp, I can set conditional break point in certain address. But this infeasible since there are too many instructions and windbg has bp limits.

In documentation ,t command can execute many instructions and display values of registers. Since ax is not displayed in r command, I can track rax instead. How should I write my command in this form?

t [r] [= StartAddress] [Count] ["Command"] 

The StartAddress should be the entry function. But what about the count? I don't want to view thousands of output but it may not reach to instruction A if count is not big enough. How to solve this problem? Can I just use a big enough value and set bp at instruction A?

Update: I managed to print all the instructions that have been executed since the entry function with ta command. But still too many instructions for me. I can only save the output to some files and find the location by string operation with C.

2
  • What you're asking for is not a capability of the x86 debug interface. Did you try looking at the code nearby to see where rax was assigned to? Commented Aug 24, 2023 at 18:34
  • I did print instructions just excuted by t command but that's too many instructions. Although I know the rax's origin in nearby memory, the number of registers that I need to track is growing exponentially(if the instruction is mov then it's fine, the number won't change. But if it's add or and then I need to track one more registers). So I would like to have some commands to track them at least some locations that cause the value to change Commented Aug 25, 2023 at 0:55

3 Answers 3

1

or if you know the address and need to stop only when some artifact is equal to your target value you can use conditional breakpoints

assuming you have the following code and want to break only when register ax contains 0x1337

int main (void)
{
    unsigned long a = 0;
    unsigned char b =1;
    while (a < 0xffffffff) {
        a=a+b;
    }
    return a;
}

start debugger

cdb -c "g loopy!main;uf ." loopy.exe

debugger stopped at main and disassembled the main function

Microsoft (R) Windows Debugger Version 10.0.19041.685 AMD64

0:000> cdb: Reading initial command 'g loopy!main;uf .'
loopy!main:
00000001`40001000 4883ec18        sub     rsp,18h
00000001`40001004 c744240400000000 mov     dword ptr [rsp+4],0
00000001`4000100c c6042401        mov     byte ptr [rsp],1

loopy!main+0x10:
00000001`40001010 837c2404ff      cmp     dword ptr [rsp+4],0FFFFFFFFh
00000001`40001015 7312            jae     loopy!main+0x29 (00000001`40001029)

loopy!main+0x17:
00000001`40001017 0fb60424        movzx   eax,byte ptr [rsp]
00000001`4000101b 8b4c2404        mov     ecx,dword ptr [rsp+4]
00000001`4000101f 03c8            add     ecx,eax
00000001`40001021 8bc1            mov     eax,ecx
00000001`40001023 89442404        mov     dword ptr [rsp+4],eax  <<<< setting break here
00000001`40001027 ebe7            jmp     loopy!main+0x10 (00000001`40001010)

loopy!main+0x29:
00000001`40001029 8b442404        mov     eax,dword ptr [rsp+4]
00000001`4000102d 4883c418        add     rsp,18h
00000001`40001031 c3              ret

setting a conditional break to print only when register ax contains 0x1337

0:000> bp 00000001`40001023 ".if(@ax!=0x1337) {gc}.else {r @ax;dd @rsp+4 l1;gc}"

continuing and result of conditional break point

0:000> g

result

ax=1337
00000000`0014ff14  00001336
ax=1337
00000000`0014ff14  00011336
ax=1337
00000000`0014ff14  00021336
ax=1337
00000000`0014ff14  00031336
(408.2dcc): Control-C exception - code 40010005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
KERNELBASE!CtrlRoutine+0x4c0f1:
00007ffe`95fee971 0f1f440000      nop     dword ptr [rax+rax]
0:001>
2
  • yeah, I found this quite handy if I know the address. But my situation is a little bit awkward. I know the range within the function but there are still several hundreds of instructions(with the method below). still too much work for me. Some debug tools do have break if certain registers change. Although this mechanism is relatively slow, it's quite useful in my case. Not sure why windbg doesn't implement this. The only method I think of is copy paste the whole output into a txt file and do some string operation with languages like c and python. Commented Aug 25, 2023 at 6:47
  • .logopen .logclose does the job of sending all the output to a text file
    – blabb
    Commented Aug 25, 2023 at 6:52
1

disassembly at an arbitrary location

0:000> u .
ntdll!RtlpFlsAlloc+0x45:
trace from here 
00007ffe`988a4909 e8e2000000      call    ntdll!RTL_BINARY_ARRAY<RTLP_FLS_CALLBACK_ENTRY,8,4>::SlotAllocate (00007ffe`988a49f0)
00007ffe`988a490e 8bd0            mov     edx,eax
00007ffe`988a4910 83f8ff          cmp     eax,0FFFFFFFFh
00007ffe`988a4913 0f8493da0400    je      ntdll!RtlpFlsAlloc+0x4dae8 (00007ffe`988f23ac)
00007ffe`988a4919 8d7af0          lea     edi,[rdx-10h]
00007ffe`988a491c 85ff            test    edi,edi
00007ffe`988a491e 0f8492000000    je      ntdll!RtlpFlsAlloc+0xf2 (00007ffe`988a49b6)
to here
00007ffe`988a4924 81fff00f0000    cmp     edi,0FF0h

allow all 64 bit registers to be displayed

0:000> rm2
0:000>

allow register display after each instruction

0:000> .prompt_allow +reg
Allow the following information to be displayed at the prompt:
(Other settings can affect whether the information is actually displayed)
   sym - Symbol for current instruction
   dis - Disassembly of current instruction
    ea - Effective address for current instruction
   reg - Register state
   src - Source info for current instruction
Do not allow the following information to be displayed at the prompt:
  None

Trace to address =. means from current address (rip) upto 00007ffe`988a4924

0:000> ta =. 00007ffe`988a4924


rax=00000229c6dc8d50 rbx=0000000000000008 rcx=00007ffe9899d2f8
rdx=0000000000000010 rsi=0000000000000001 rdi=0000000000000000
rip=00007ffe988a49f0 rsp=00000061850eeaa8 rbp=00007ffe969ba840
 r8=0000000000000000  r9=0000000000000010 r10=0000000000000003
r11=00000061850ee6b8 r12=00007ffe96987850 r13=ffffffffffffffff
r14=00000061850eeb28 r15=00007ffe9899d2f0
iopl=0         nv up ei pl nz na po nc
ntdll!RTL_BINARY_ARRAY<RTLP_FLS_CALLBACK_ENTRY,8,4>::SlotAllocate:

XXXXXXXXXXXXXXXXXXXXXXXXXXcut off irrlevent traces XXXXXXXXXXXX 

ntdll!RTL_BINARY_ARRAY<RTLP_FLS_CALLBACK_ENTRY,8,4>::SlotAllocate+0x64:
00007ffe`988a4a54 c3              ret
rax=0000000000000011 rbx=0000000000000008 rcx=0000000000000002
rdx=0000000000000011 rsi=0000000000000001 rdi=0000000000000000
rip=00007ffe988a490e rsp=00000061850eeab0 rbp=00007ffe969ba840
 r8=00000229c6dc8d40  r9=0000000000000010 r10=0000000000000003
r11=00000061850ee6b8 r12=00007ffe96987850 r13=ffffffffffffffff
r14=00000061850eeb28 r15=00007ffe9899d2f0
iopl=0         nv up ei pl nz na po nc
ntdll!RtlpFlsAlloc+0x4a:
00007ffe`988a490e 8bd0            mov     edx,eax
rax=0000000000000011 rbx=0000000000000008 rcx=0000000000000002
rdx=0000000000000011 rsi=0000000000000001 rdi=0000000000000000
rip=00007ffe988a4910 rsp=00000061850eeab0 rbp=00007ffe969ba840
 r8=00000229c6dc8d40  r9=0000000000000010 r10=0000000000000003
r11=00000061850ee6b8 r12=00007ffe96987850 r13=ffffffffffffffff
r14=00000061850eeb28 r15=00007ffe9899d2f0
iopl=0         nv up ei pl nz na po nc
XXXXXXXXXXXXXXXXXXXXXXXXXXcut off irrlevent traces XXXXXXXXXXXX 
ntdll!RtlpFlsAlloc+0x5a:
00007ffe`988a491e 0f8492000000    je      ntdll!RtlpFlsAlloc+0xf2 (00007ffe`988a49b6) [br=0]
rax=0000000000000011 rbx=0000000000000008 rcx=0000000000000002
rdx=0000000000000011 rsi=0000000000000001 rdi=0000000000000001
rip=00007ffe988a4924 rsp=00000061850eeab0 rbp=00007ffe969ba840
 r8=00000229c6dc8d40  r9=0000000000000010 r10=0000000000000003
r11=00000061850ee6b8 r12=00007ffe96987850 r13=ffffffffffffffff
r14=00000061850eeb28 r15=00007ffe9899d2f0
iopl=0         nv up ei pl nz na pe nc
traced till stop address
ntdll!RtlpFlsAlloc+0x60:
00007ffe`988a4924 81fff00f0000    cmp     edi,0FF0h
0:000>
1

well if you really need to trace each execution just looking for a value in some register and willing to spend the time you can run a script recursively on each instruction

it is very time consuming and is equivalent to setting the trap flag on each instruction

here is how you do it using the same example code in an earlier answer

int main (void)
{
    unsigned long a = 0;
    unsigned char b =1;
    while (a < 0xffffffff) {
        a=a+b;
    }
    return a;
}

write a script file loopy.wds with contents like this

.if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}

when reached the main function i save the current time

0:000> r $t1 = @$dbgtime
0:000> ? @$t1
Evaluate expression: 133374252215057636 = 01d9d72c`fd763ce4

and start single stepping using

0:000> t "$<d:\\loopy.wds"

it will keep on tracing every instruction until the register eax holds 0x1001

0:000> .if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}
0:000> .if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}
0:000> .if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}
0:000> .if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}
0:000> .if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}
0:000> .if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}
0:000> .if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}

it will stop only when eax = 0x1001

0:000> .if (@eax == 0x1001) {} .else {t "$<d:\\loopy.wds"}
loopy!main+0x27:
00000001`40001027 ebe7            jmp     loopy!main+0x10 (00000001`40001010)
0:000> ? @$t1
Evaluate expression: 133374252215057636 = 01d9d72c`fd763ce4
0:000> ? @$dbgtime
Evaluate expression: 133374255572685217 = 01d9d72d`c59791a1
0:000> ? @$dbgtime - @$t1
Evaluate expression: 3563614608 = 00000000`d4687190
0:000> r rax
rax=0000000000001001
0:000> dv
              b = 0x01 ''
              a = 0x1001
0:000> .load kdexts

0:000> 0: kd> !filetime 01d9d72c`fd763ce4
0:000>  8/25/2023 13:50:21.505 (unknown)
0:000> 0: kd> !filetime 01d9d72d`c59791a1
0:000>  8/25/2023 13:55:57.268 (unknown)
0:000> 0: kd> !filetime 00000000`d4687190
0:000>  1/ 1/1601 05:35:56.361 (unknown)  <<<< 
it took about 5 minutes to trace until eax became 0x10001 from 0x0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.