Hello,
There is DEBUG_EVENT structure (https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-debug_event)which describes a debug event.
In MASM32 SDK this structure was defined like this:
DEBUGSTRUCT UNION
Exception EXCEPTION_DEBUG_INFO <{?,?,?,?,?,EXCEPTION_MAXIMUM_PARAMETERS dup(?)},?>
CreateThread CREATE_THREAD_DEBUG_INFO <?,?,?>
CreateProcessInfo CREATE_PROCESS_DEBUG_INFO <?,?,?,?,?,?,?,?,?,?>
ExitThread EXIT_THREAD_DEBUG_INFO <?>
ExitProcess EXIT_PROCESS_DEBUG_INFO <?>
LoadDll LOAD_DLL_DEBUG_INFO <?,?,?,?,?,?>
UnloadDll UNLOAD_DLL_DEBUG_INFO <?>
DebugString OUTPUT_DEBUG_STRING_INFO <?,?,?>
RipInfo RIP_INFO <?,?>
DEBUGSTRUCT ENDS
DEBUG_EVENT STRUCT
dwDebugEventCode DWORD ?
dwProcessId DWORD ?
dwThreadId DWORD ?
u DEBUGSTRUCT <>
DEBUG_EVENT ENDS
In MASM64 SDK it was defined like this:
DEBUG_EVENT STRUCT
dwDebugEventCode DWORD ?
dwProcessId DWORD ?
dwThreadId DWORD ?
DWORD ?
UNION
Exception EXCEPTION_DEBUG_INFO <>
CreateThread CREATE_THREAD_DEBUG_INFO <>
CreateProcessInfo CREATE_PROCESS_DEBUG_INFO <>
ExitThread EXIT_THREAD_DEBUG_INFO <>
ExitProcess EXIT_PROCESS_DEBUG_INFO <>
LoadDll LOAD_DLL_DEBUG_INFO <>
UnloadDll UNLOAD_DLL_DEBUG_INFO <>
DebugString OUTPUT_DEBUG_STRING_INFO <>
RipInfo RIP_INFO <>
ENDS
DEBUG_EVENT ENDS
Thanks to MASM32 SDK one can use `.` notation to access union elements inside a structure:
DBEvent.u.Exception.pExceptionRecord.ExceptionCode
But in MASM64 SDK I couldn't manage to use dot notation to access union elements inside a structure(in our case DEBUG_EVENT elements), so I have to manually access the union elements:
mov r15, qword ptr [DBEvent + 16] ; DBEvent.u.Exception.pExceptionRecord.ExceptionCode
; or another example
mov r15, qword ptr [DBEvent + 16 + 48] ; DBEvent.u.CreateProcessInfo.lpStartAddress
I wonder If I can still use dot notation in such cases?
Hello bluedevil
I spent some time exploring your code and my suggestions as well as optimisations are attached
one idea is about using ofn struct predefined (thus moved from .data -> .data)
another one - drop using rXX registers and start using directly DBEvent var members...
Are you planning to write a kind of mini-debugger?
@greenozon thanks for your reply. I have updated my code thanks to you. I have attached my latest code.
My code seems working but I have one issue: the image base and entry point values are wrong if i select a 64bit binary. But it is ok if I select a 32 bit binary.
I am filling the buffer with wsprintf
invoke wsprintf, addr buffer, addr ProcessInfo, \
DBEvent.CreateProcessInfo.hFile,\ ; image file handle
DBEvent.CreateProcessInfo.hProcess, \ ; process handle
DBEvent.CreateProcessInfo.hThread,\ ; thread handle
DBEvent.CreateProcessInfo.lpBaseOfImage, \ ; imagebase
DBEvent.CreateProcessInfo.lpStartAddress ; entrypoint
invoke MessageBox, NULL, addr buffer, addr AppName, MB_OK or MB_I
But the real problem resides in WaitForDebugEvent api. When this function runs, it fills the DEBUG_EVENT structure. And the entrypoint and imagebase values are wrong if the executable is 64bit binary
.data?
<snip>
DBEvent DEBUG_EVENT <>
.code
<snip>
invoke WaitForDebugEvent, addr DBEvent, INFINITE
Folks, If you have time and help me on this issue I will be very happy!
Quote from: bluedevil on October 04, 2022, 08:53:32 AM
But the real problem resides in WaitForDebugEvent api. When this function runs, it fills the DEBUG_EVENT structure. And the entrypoint and imagebase values are wrong if the executable is 64bit binary
.data?
<snip>
DBEvent DEBUG_EVENT <>
.code
<snip>
invoke WaitForDebugEvent, addr DBEvent, INFINITE
I really dont't get that WaitForDebugEvent API fills DEBUG_EVENT structure true if I open a 32 bit executable, but worng on If i open a 64bit executable.
This is what I get when running the program with TUTE02 as the debugee:
Is this also what you are getting for imagebase and start address?
edit = Removed attached image as it has served its purpose
The debugger and debuggee appear to have the same load address and entry point as specified in their PE headers. So, when the debugger (TUTE28) loads the debuggee it must use a different address than that in the PE header (for TUTE02), if my suspicions are correct.
There is a link option that allows to specify the load address, rather than the default iirc. Try changing that for TUTE02 (link option) to see if it makes a difference.
example: (I looked it up) where "..." indicates existing part of what you have for link.exe
... /BASE:0x180000000 ...
Process Explorer show also same addresses, so it's possible and x64 feature.
Process Explorer v16.43 (https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer)
@TimoVJL and @zedd151 thank you for your brilliant comments. I have realized that my code actually works normal!
I have double checked by using, Process Explorer and x64dbg, they both gave the same ImageBase and EntryPoint values. Only IDA Pro doesn't; and I think IDA Pro rebases executable to default imagebases.
Also M$ Docs (https://learn.microsoft.com/en-us/cpp/build/reference/base-base-address?view=msvc-170) clearly states that:
Quote
For security reasons, Microsoft recommends you use the /DYNAMICBASE option instead of specifying base addresses for your executables. /DYNAMICBASE generates an executable image that can be randomly rebased at load time by using the address space layout randomization (ASLR) feature of Windows. The /DYNAMICBASE option is on by default.
So I have relinked msgbox.exe with
/DYNAMICBASE:NO, then I got the default imagebase address!
Ah, ida pro. That's weird.
For me, I could actually care less what M$ says about security reasons. If they knew anything about computer security we would all be happily running Windows xp still. Their best OS, imo.
Quote from: zedd151 on October 08, 2022, 02:02:10 AM
Ah, ida pro. That's weird.
For me, I could actually care less what M$ says about security reasons. If they knew anything about computer security we would all be happily running Windows xp still. Or even Windows 2000.
You are wisdom :eusa_clap: :eusa_clap:
Quote from: bluedevil on October 08, 2022, 01:31:13 AMAlso M$ Docs (https://learn.microsoft.com/en-us/cpp/build/reference/base-base-address?view=msvc-170) clearly states that:
Quote
For security reasons, Microsoft recommends you use the /DYNAMICBASE option instead of specifying base addresses for your executables. /DYNAMICBASE generates an executable image that can be randomly rebased at load time by using the address space layout randomization (ASLR) feature of Windows. The /DYNAMICBASE option is on by default.
/DYNAMICBASE for security reasons is nonsense: (https://www.blackhat.com/presentations/bh-dc-07/Whitehouse/Paper/bh-dc-07-Whitehouse-WP.pdf)
QuoteOur analysis of the results uncovers predictability in the implementation that reduces its effectiveness.
Quote from: jj2007 on October 08, 2022, 02:14:22 AM
/DYNAMICBASE for security reasons is nonsense: (https://www.blackhat.com/presentations/bh-dc-07/Whitehouse/Paper/bh-dc-07-Whitehouse-WP.pdf)
QuoteOur analysis of the results uncovers predictability in the implementation that reduces its effectiveness.
:biggrin:
If its the one I am thinking of, its fundamentally a good idea, randomising the start address of and executable to make hacking it a lot more complicated. I have some time ago done test pieces where you randomly alter ESP (in 32 bit) at the entry point of an executable which made some forms of hacking unreliable but it was never any use to me so I never pursued it.
Quote from: hutch-- on October 08, 2022, 10:19:23 AM
I have some time ago done test pieces where you randomly alter ESP (in 32 bit) at the entry point of an executable which made some forms of hacking unreliable but it was never any use to me so I never pursued it.
You got my attention :icon_idea:
You basically have to alter ESP right at the entry point. The tests I did some time ago called a random algo within a plus or minus range, modified ESP and then the normal code was run and it seemed to work OK.
But I still know the entry point right? And what about 'esp' before main? It seem to me like I still can learn how much you changed esp?
So you're basically relocating the entire stack to a new place, right? How do you figure out where to relocate it? Can't just plop it anywhere ...
Well, almost everywhere... but it must be downwards, and you need to probe the stack
It must be done directly AFTER the program entry point, set a range of plus and minus 1k for a random algo and then add that to ESP.
After that, just run the normal app code.
Are you sure you can increase esp?
With a default 1 meg stack, twiddling it in either direction by up to 1k is no big deal.
Quote from: hutch-- on October 09, 2022, 09:43:32 AM
With a default 1 meg stack, twiddling it in either direction by up to 1k is no big deal.
Now I am curious, Hutch. Attached is a minimal window application, 38 lines of plain simple Masm64 SDK code. Where would you insert an
add rsp, 1000h?
Subtracting works fine:
WinMain proc
LOCAL msg:MSG
sub rsp, 1000h
:biggrin:
It Verx !
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm64\include64\masm64rt.inc
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
entry_point proc
sub rsp, 512
conout "How D",lf
waitkey
.exit
entry_point endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end
Quote from: hutch-- on October 09, 2022, 02:34:24 PM
:biggrin:
It Verx !
entry_point proc
sub rsp, 512
I know. Now what about add rsp, 512?
Quote from: hutch-- on October 09, 2022, 09:43:32 AM
With a default 1 meg stack, twiddling it in either direction by up to 1k is no big deal.
:biggrin:
Well, thats simple, change the example to ADD rather than SUB.
Quote from: jj2007 on October 09, 2022, 07:48:10 AM
it must be downwards, and you need to probe the stack
Remember we are in the Campus.
You raise a much more than trivial point here.
Correct me if I'm wrong, but it's always been my understanding that the stack starts at the end ("top" as in highest address) of what used to be called a "segment" in DOS days, and grows downwards. So that would mean that if you add to the stack you'll be in trouble, as you'll be addressing out-of-bounds memory. Correct? In other words, the stack pointer doesn't start out in the middle of the field, where it can be either added to or subtracted from, right? (There might be a little bit of padding on the bottom, but I'm guessing not much, much less than a K.)
Quote from: NoCforMe on October 09, 2022, 09:39:31 PMif you add to the stack you'll be in trouble, as you'll be addressing out-of-bounds memory. Correct?
Correct.
Quote from: jj2007 on October 09, 2022, 07:48:10 AM
it must be downwards, and you need to probe the stack
Please find attached Hutch' marginally modified code. Here are the changes:
entry_point proc
mov ecx, 800 ; 800kBytes
@@: sub rsp, 1024
mov al, [rsp]
dec ecx
jge @B
conout "How D",lf
This is basically the tests I did years ago but now in 64 bit.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm64\include64\masm64rt.inc
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
entry_point proc
rcall GetTickCount ; get a seed
bswap rax ; invert bytes for fastest changing
rcall seed_rrand,rax ; use it as a random seed
mov rax, rvcall(rrand,1,16) ; call the range random algo
shl rax, 3 ; mul by 8, keep stack aligned
sub rsp, rax ; sub rax from rsp
conout "RSP = ",str$(rsp),lf,lf ; display rsp
conout "How D",lf ; a text message
waitkey ; wait for result
.exit ; bye
entry_point endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end
Yep, looks good :thumbsup:
Here is a simple variant, tested only on Win7:
entry_point proc
mov ecx, 1000 ; 1000kBytes
@@: sub rsp, 1024
mov al, [rsp]
dec ecx
jge @B
conout "Stack is stuck at ", hex$(rsp), "h", lf
waitkey
Output: Stack is stuck at 35A70h :tongue:
Here is a tweaked version.
; «»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
; The design is to produce and option range of random OFFSETs that are aligned by 8 to
; modify the 64 bit stack pointer. 16 potential random numbers multiplied by 8 to
; maintain stack alignment.
; «»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
include \masm64\include64\masm64rt.inc
.data?
sptr dq ?
cntr dq ?
.code
; «»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
entry_point proc
mov sptr, rsp
rcall GetTickCount ; get a seed
bswap rax ; invert bytes for more deviation
rcall seed_rrand,rax ; use it as a random seed
mov cntr, 1024 ; run many iterations
lp:
mov rax, rvcall(rrand,1,16) ; call the range random algo
sub cntr, 1
jnz lp
shl rax, 3 ; mul by 8, produces aligned multiply
sub rsp, rax ; sub rax from rsp
conout "RSP = ",str$(rsp),lf,lf ; display rsp
conout "How D",lf ; a text message
; ---------------------------------------------------------------
; This would be put somewhere in the app that is not easy to find.
; ---------------------------------------------------------------
.if rsp == sptr
conout "Phark, someone has hacked the app !!!!",lf
.endif
; ---------------------------------------------------------------
waitkey ; wait for result
.exit ; bye
entry_point endp
; «»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
end
I hope I didn't miss something. hutch-- your technique works perfectly on my win10x64 machine.
But what I wanted to ask is; we do not need this technique because of /DYNAMICBASE parameter right?
Linker puts /DYNAMICBASE:YES by default and in every run rsp value changes already?
ASLR is just a bit in PE header, easy to change.
Quote from: TimoVJL on October 10, 2022, 06:23:28 PM
ASLR is just a bit in PE header, easy to change.
But if I have executable I already can reverse/disassemble/debug the binary. Even so I liked hutch--'s approach.
Of course an application can check that bit from header or PEB and act their own way and alert user.
Here is a test UI app that uses the above technique. It must be run directly after the entry point and for a UI app, the range of OFFSETS must be aligned by 16. The test is buried in the "StatusBar" proc to make it harder to find.
While anything can be broken if the attacker is both patient enough and knows enough, small tricks like this increase the complexity of hacking an app and combined with a range of other tricks, you can help the attacker grow old trying to break an app.
Continuing the discussion of just how the stack works (under Windows), check this page of Raymond Chen's (https://devblogs.microsoft.com/oldnewthing/20190111-00/?p=100685). Excellent explanation of what can and can't be done with the stack (talking X86 here; he covers other architectures also).
I especially like this little illustration from his article:
I generally like Chen's articles a lot, but this one is not very clear. Note also that the "red zone" (why "still valid"?) does not exist for the two architectures we are dealing with in this forum, x86 and x64.
To take away: mov [esp-100], eax is not a good idea, because your debugger might shamelessly use that area.