Using cdb, I have following conditional breakpoint
bp 004bf9f8 ".if (@eax = 0) {.echotime;.echo Breakpoint 004bf9f8;r};gc"
As this is to investigate an intermittent issue, it has to stay attached for quite some time but the conditional breakpoint slows down the overall performance of the application far more than I can afford.
eax=0
. I have read some posts explaining why conditional breakpoints slow down so much and those make perfect sense so I assume this is a dead end.if eax = 0 { <whatever>}
so I could add an unconditional breakpoint on <whatever>
?Edit
from the comments, the plan of action is to script as follows
.dvalloc <bytes>
and store the result in a variable called patch
a 004bfa08 JMP patch
a patch MOV eax,esi
a patch+x CMP eax,0
a patch+y JNE 004bfa0a
a patch+z JMP 004bfa0a
and now I should be able to add an unconditional breakpoint on patch+z
to dump the information I need without halting the application.
bp patch+z ".echotime;.echo Breakpoint patch+z;~.;r;!dpx;gc"
Edit 2
Following POC works in a live debugging session but this still has to be converted to a script that doesn't halt the application, waiting for keypresses or anything.
Commands executed
.dvalloc 1000
a 004bfa00
JMP 0x0c570000
NOP
a 0xc570000
mov edi,edx
mov esi,eax
mov ebp,edi
cmp eax,0
jne 0x004bfa06
jmp 0x004bfa06
bp 0c570011 ".echo Hello World"
Commands executed including output/context
0:010> .dvalloc 1000
Allocated 1000 bytes starting at 0c570000
0:010> u 004bf9f8 LD
application+0xbf9f8:
004bf9f8 53 push ebx
004bf9f9 56 push esi
004bf9fa 57 push edi
004bf9fb 55 push ebp
004bf9fc 51 push ecx
004bf9fd 890c24 mov dword ptr [esp],ecx
004bfa00 8bfa mov edi,edx |
004bfa02 8bf0 mov esi,eax |-> these get overwritten so repeat in patch
004bfa04 8bef mov ebp,edi |
004bfa06 8bd5 mov edx,ebp
004bfa08 8bc6 mov eax,esi
004bfa0a e8e5feffff call application+0xbf8f4 (004bf8f4)
0:010> a 004bfa00
JMP 0x0c570000
NOP
0:010> u 004bf9f8 LD
application+0xbf9f8:
004bf9f8 53 push ebx
004bf9f9 56 push esi
004bf9fa 57 push edi
004bf9fb 55 push ebp
004bf9fc 51 push ecx
004bf9fd 890c24 mov dword ptr [esp],ecx
004bfa00 e9fb050b0c jmp 0c570000
004bfa05 90 nop
004bfa06 8bd5 mov edx,ebp
004bfa08 8bc6 mov eax,esi
004bfa0a e8e5feffff call application+0xbf8f4 (004bf8f4)
0:010> a 0xc570000
0c570000 mov edi,edx
mov edi,edx
0c570002 mov esi,eax
mov esi,eax
0c570004 mov ebp,edi
mov ebp,edi
0c570006 cmp eax,0
cmp eax,0
0c57000b jne 0x004bfa06
jne 0x004bfa06
0c570011 jmp 0x004bfa06
jmp 0x004bfa06
0c570016
0:010> u 0x0c570000 L6
0c570000 8bfa mov edi,edx
0c570002 8bf0 mov esi,eax
0c570004 8bef mov ebp,edi
0c570006 3d00000000 cmp eax,0
0c57000b 0f85f5f9f4f3 jne application+0xbfa06 (004bfa06)
0c570011 e9f0f9f4f3 jmp application+0xbfa06 (004bfa06)
0:010> bp 0c570011 ".echo Hello World"
Edit 3
manually patching 7 running executables was successful but depending on the address returned by .dvalloc
, the assembled JMP
instruction contains a different instruction. I assumed it would be as simple as subtracting the address we jump to from the address obtained from .dvalloc
but that does not seem to be the case.
-------------------------------------------------------------
.dvalloc+0x11 |a jmp 004bfa06 |opcode |cd |LE
--------------|---------------|-------|---------|------------
1. 00df0011 |e9f0f96cff |e9 |f0f96cff |ff 6c f9 f0
2. 00e30011 |e9f0f968ff |e9 |f0f968ff |ff 68 f9 f0
3. 00f00011 |e9f0f95bff |e9 |f0f95bff |ff 5b f9 f0
4. 00ff0011 |e9f0f94cff |e9 |f0f94cff |ff 4c f9 f0
5. 093a0011 |e9f0f911f7 |e9 |f0f911f7 |f7 11 f9 f0
6. 0c570011 |e9f0f9f4f3 |e9 |f0f9f4f3 |f3 f4 f9 f0
7. 0ce70011 |e9f0f964f3 |e9 |f0f964f3 |f3 64 f9 f0
-------------------------------------------------------------
The first f
is a sign bit perhaps?
Edit 4
The calculation is straightforward after all, took me long enough though. The first f
indeed is the sign.
004bfa06
jmp 004bfa06
instruction. In my case, that is always .dvalloc+0x16
(.dvalloc+0x11
is the start of the instruction)Applied to my last attempt (7), that gives
004fba01 - 0ce70016 = f3 64 f9 f0.
The instruction to edit the memory at 0ce70011 then becomes e9f0f964f3.
Following is the function prologue where I am setting the breakpoint. The instruction at 004bfa08 (MOV param_1,ESI)
is redundant because of the previous instruction at 004bfa02 (MOV ESI,param_1)
so that might be usefull but I lack the knowledge on how to proceed from here.
**************************************************************
* FUNCTION *
**************************************************************
int * __register FUN_004bf9f8(int param_1, int param_2,
int * EAX:4 <RETURN>
int EAX:4 param_1
int EDX:4 param_2
int ECX:4 param_3
undefined4 Stack[-0x14]:4 local_14
004bf9f8 53 PUSH EBX
004bf9f9 56 PUSH ESI
004bf9fa 57 PUSH EDI
004bf9fb 55 PUSH EBP
004bf9fc 51 PUSH param_3
004bf9fd 89 0c 24 MOV dword ptr [ESP]=>local_14,param_3
004bfa00 8b fa MOV EDI,param_2
004bfa02 8b f0 MOV ESI,param_1
004bfa04 8b ef MOV EBP,EDI
004bfa06 8b d5 MOV param_2,EBP
004bfa08 8b c6 MOV param_1,ESI
004bfa0a e8 e5 fe CALL FUN_004bf8f4
ff ff
004bfa0f 8b d8 MOV EBX,param_1
Adding Another Answer
source we are going to use compiled with in vs2017 community as x64
our task is to break only when eax = 0x1337 without any conditional bps
cl /Zi /W4 /analyze /EHsc /Od /nologo patch.cpp /link /release /entry:main /subsystem:windows
Source
#include <windows.h>
#pragma comment(lib,"kernel32.lib")
int count = 0;
int somefunc()
{
return count++;
}
int main(void)
{
for (;;)
{
somefunc();
Sleep(2);
}
}
the disassembly of function somefunc() is as follows
F:\src\deto>cdb -c "uf deto!somefunc;q" deto.exe | f:\git\usr\bin\awk.exe "/Reading/,/quit/"
0:000> cdb: Reading initial command 'uf deto!somefunc;q'
deto!somefunc:
00000001`40001000 4883ec18 sub rsp,18h
00000001`40001004 8b05f61f0000 mov eax,dword ptr [deto!count (00000001`40003000)]
00000001`4000100a 890424 mov dword ptr [rsp],eax
00000001`4000100d 8b05ed1f0000 mov eax,dword ptr [deto!count (00000001`40003000)]
00000001`40001013 ffc0 inc eax
00000001`40001015 8905e51f0000 mov dword ptr [deto!count (00000001`40003000)],eax
00000001`4000101b 8b0424 mov eax,dword ptr [rsp]
00000001`4000101e 4883c418 add rsp,18h
00000001`40001022 c3 ret
quit:
I am going to edit bytes in three places using this JavaScript
function log(a)
{
host.diagnostics.debugLog( a + '\n');
}
function exec (cmdstr)
{
return host.namespace.Debugger.Utility.Control.ExecuteCommand(cmdstr);
}
function patch()
{
log("hi")
exec(".dvalloc /b 0x200000000 1000")
exec("eb 0x14000100a 0x49 0xbf 0x0 0x0 0x0 0x0 0x2 0x0 0x0 0x0 0x41 0xff 0xe7")
exec("eb 0x200000000 0x3d 0x37 0x13 0x0 0x0 0x49 0xbf 0x0 0x8 0x0 0x0 0x2 0x0 0x0 0x0 0x74 0x25 0x36 0x89 0x4 0x24 0xa1 0x0 0x30 0x0 0x40 0x1 0x0 0x0 0x0 0xff 0xc0 0xa3 0x0 0x30 0x0 0x40 0x1 0x0 0x0 0x0 0x49 0xbf 0x1b 0x10 0x0 0x40 0x1 0x0 0x0 0x0 0x41 0xff 0xe7 0x41 0xff 0xe7")
exec("eb 0x200000800 0xcc")
}
the bytes to patch were taken from this python script
from keystone import *
print ("assemble at 0x14000100a\n")
CODE = b"mov r15,0x200000000;jmp r15"
ks = Ks(KS_ARCH_X86,KS_MODE_64)
encoding,count = ks.asm(CODE)
for i in encoding:
print(hex(i),end = " ")
print("\n")
print ("Assemble at 0x200000000\n")
CODE = b"cmp eax,0x1337;\
mov r15,0x200000800;\
je here;\
mov dword ptr ss:[rsp],eax;\
mov eax,dword ptr ds:[0x140003000];\
inc eax;\
mov dword ptr ds:[0x140003000],eax;\
mov r15, 0x14000101b;\
jmp r15;\
here:;\
jmp r15;"
encoding,count = ks.asm(CODE)
for i in encoding:
print(hex(i),end = " ")
print("\n")
print ("Assemble at 0x200000800 an int 3 aka 0xcc")
loaded the exe in windbg patched and executed to break in 0x200000800
Microsoft (R) Windows Debugger Version 10.0.17763.132 AMD64
ntdll!LdrpDoDebuggerBreak+0x30:
00007ff9`b33f121c cc int 3
0:000> .scriptload f:\wdscr\patch.js
JavaScript script successfully loaded from 'f:\wdscr\patch.js'
0:000> dx @$scriptContents.patch()
hi
@$scriptContents.patch()
0:000> uf deto!somefunc
Flow analysis was incomplete, some code may be missing
deto!somefunc [f:\src\deto\deto.cpp @ 5]:
5 00000001`40001000 4883ec18 sub rsp,18h
6 00000001`40001004 8b05f61f0000 mov eax,dword ptr [deto!count (00000001`40003000)]
6 00000001`4000100a 49bf0000000002000000 mov r15,200000000h <<<<<<<<<<<
6 00000001`40001014 41ffe7 jmp r15 <<<<<<<<<<<<<<<<<<<
0:000> g
(3aec.405c): Break instruction exception - code 80000003 (first chance)
00000002`00000800 cc int 3 <<<<<<<<<<<<<<
0:000> r eax
eax=1337