News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Debugging: how to recognise a no stack frame proc

Started by jj2007, March 04, 2020, 11:29:21 PM

Previous topic - Next topic

daydreamer

Quote from: jj2007 on March 15, 2020, 02:52:25 AM
You attached code, daydreamer :thumbsup:

Can you explain where exactly you are testing your ideas?
MACRO versions only take place when used in code,PROC versions only need to be assembled if you want to use them instead of MACRO version,calling from several places,if use MACRO's instead IFDEF is used to avoid twice size of code =MACRO version+PROC version
also IFDEF can be used to reduce size not assemble PROC's that are never called
my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

jj2007

No, I meant examples from the source you posted. Where do you test your ideas, and how?

LiaoMi

Quote from: jj2007 on March 05, 2020, 01:40:24 PM
In the meantime, I found at least a method to recognise that a proc has no stack frame.

The idea is very simple: in a large source, replace every ret that is followed by somecode endp with @MbRet. It takes an optional argument that should be the name of the proc; if there is no argument, the source line will be shown instead (as in the CodeNoArgs example below).

If the source has stack problems, they will be ## flagged to the console ##, even if the exe was built as a Windows application.

There are basically three cases:
1. too many push instructions: @MbRet will tell you about it, and correct the stack so that at least the uses esi edi ebx works properly.

2. too many pop instructions: @MbRet will tell you about it, and correct the stack but one or more of the regs in the uses esi edi ebx list will be trashed anyway.

3. stack corruption: the return address is somewhere in nirwana land. @MbRet will inform you about it, but the program will crash anyway.

I've tested it with a 20k lines source, and so far I've found one bug with it, see first post above. @MbRet tests runtime behaviour, so its messages will pop up only when you are really using a proc. So for rarely used exotic functions it may take a while until you discover the bug.

Once you have verified that there are no problems, put @MbRet off below the include line, so that only an ordinary ret will be generated.

Here is a little demo with some procs that treat the stack badly (tested ok with WinXP, Win7-64 and Win10):

include \masm32\include\masm32rt.inc ; purest Masm32 SDK code
include MbRet.inc ; must be in the exe's folder
; @MbRet off ; switch it off once tests are OK

.code
TwoArgsNoLocals proc uses esi edi ebx mode, crashArg
  mov ebx, mode ; no Locals, but has args, and therefore a stack frame
  print "2 args: ebx="
  print str$(ebx), 13, 10
  .while sdword ptr ebx>=0
push eax ; stack imbalances will be flagged
dec ebx
  .Endw
  pop eax ; stack is balanced
  pop ecx ; for mode=3
  pop edx
  .if crashArg==99
mov dword ptr [ebp+4], 12345678h ; bad news for the return address ;-)
  .endif
  @MbRet 2ArgsNoLocals ; instead of the ret
TwoArgsNoLocals endp

ThreeArgsLocal123 proc uses ecx mode, arg2, arg3
Local buffer[123]:BYTE
  push eax ; stack not balanced, will be flagged
  @MbRet ThreeArgsLocal123
ThreeArgsLocal123 endp

CodeNoArgs proc uses esi edi ebx ecx ebp
  print "CodeNoArgs - cannot work with @MbRet: "
  ; push eax ; activate to produce a crash
  @MbRet ; no label -> line number 32; @MbRet will say "no frame found"
CodeNoArgs endp

start:
  cls
  m2m esi, 3
  mov ebx, 12345678h
  .Repeat
invoke TwoArgsNoLocals, esi, 123
print hex$(ebx), ": ebx after the invoke", 13, 10, 10
dec esi
  .Until Zero?
  invoke CodeNoArgs
  invoke ThreeArgsLocal123, 123, 456, 789
  inkey chr$(10, "hit any key for the stack corruption test:", 13, 10)
  invoke TwoArgsNoLocals, 2, 99
  MsgBox 0, "You won't see this box", "Hi", MB_OK
  invoke ExitProcess, 0
end start


The procs have little problems with a sloppy use of the push and pop instructions. Here is the output:
2 args: ebx=3
### MbRet 2ArgsNoLocals: stack is -4 bytes off, 1 push(es) too many ###
12345678: ebx after the invoke

2 args: ebx=2
12345678: ebx after the invoke

2 args: ebx=1
### MbRet 2ArgsNoLocals: stack is 4 bytes off, 1 pop(s) too many - check uses ###
0040107A: ebx after the invoke

CodeNoArgs - cannot work with @MbRet: ### MbRet 31: no stack frame found ###
### MbRet ThreeArgsLocal123: stack is -4 bytes off, 1 push(es) too many ###

hit any key for the stack corruption test:

2 args: ebx=2
### stack corruption? 2ArgsNoLocals: retaddr 12345678h is above the code segment ###


After the stack corruption test, it will produce an exception.

Hi jj2007,

this code does not work for me ..


0040104F                                    | C2 0800                    | RET     8                                                       |


0019FF64                                      0019FF80   


0019FF80                                    | DCFF                       | FDIV    ST(7), ST(0)                                            |
0019FF82                                    | 1900                       | SBB     DWORD PTR DS:[EAX], EAX                                 |
0019FF84                                    | 74 7B                      | JE      1A0001                                                  |


EAX : 00000002
EBX : 00000002
ECX : 00000002
EDX : 00000002
EBP : 00000003
ESP : 0019FF70
ESI : 00401076     <mbret4masm32.EntryPoint>
EDI : 12345678
EIP : 0019FF80
EFLAGS : 00000206     L'Ȇ'
ZF : 0
OF : 0     L'Ȁ'
CF : 0     L'Ā'
PF : 1
SF : 0
TF : 0     L'Ā'
AF : 0
DF : 0
IF : 1
LastError : 000036B7 (ERROR_SXS_KEY_NOT_FOUND)
LastStatus : C0150008 (STATUS_SXS_KEY_NOT_FOUND)
GS : 002B
ES : 002B
CS : 0023
FS : 0053
DS : 002B
SS : 002B     '+'
x87r0 : 00000000000000000000
x87r1 : 00000000000000000000
x87r2 : 00000000000000000000
x87r3 : 00000000000000000000
x87r4 : 00000000000000000000
x87r5 : 00000000000000000000
x87r6 : 00000000000000000000
x87r7 : 00000000000000000000
x87TagWord : FFFF
x87ControlWord : 027F     L'ɿ'
x87StatusWord : 0000
x87TW_0 : 3 (Empty)
x87TW_1 : 3 (Empty)
x87TW_2 : 3 (Empty)
x87TW_3 : 3 (Empty)
x87TW_4 : 3 (Empty)
x87TW_5 : 3 (Empty)
x87TW_6 : 3 (Empty)
x87TW_7 : 3 (Empty)
x87SW_B : 0
x87SW_C3 : 0
x87SW_TOP : 0 (ST0=x87r0)
x87SW_C2 : 0
x87SW_C1 : 0
x87SW_O : 0
x87SW_ES : 0
x87SW_SF : 0
x87SW_P : 0
x87SW_U : 0
x87SW_Z : 0
x87SW_D : 0
x87SW_I : 0
x87SW_C0 : 0
x87CW_IC : 0
x87CW_RC : 0 (Round Near)
x87CW_PC : 2 (Real8)
x87CW_PM : 1
x87CW_UM : 1
x87CW_OM : 1
x87CW_ZM : 1
x87CW_DM : 1     L'ā'
x87CW_IM : 1
MxCsr : 00001F80
MxCsr_FZ : 0
MxCsr_PM : 1
MxCsr_UM : 1
MxCsr_OM : 1
MxCsr_ZM : 1
MxCsr_IM : 1     L'ā'
MxCsr_DM : 1
MxCsr_DAZ : 0
MxCsr_PE : 0
MxCsr_UE : 0
MxCsr_OE : 0
MxCsr_ZE : 0
MxCsr_DE : 0
MxCsr_IE : 0
MxCsr_RC : 0 (Round Near)
MM0 : 0000000000000000
MM1 : 0000000000000000
MM2 : 0000000000000000
MM3 : 0000000000000000
MM4 : 0000000000000000
MM5 : 0000000000000000
MM6 : 0000000000000000
MM7 : 0000000000000000
XMM0  : 00000000000000000000000000000000
XMM1  : 00000000000000000000000000000000
XMM2  : 00000000000000000000000000000000
XMM3  : 00000000000000000000000000000000
XMM4  : 00000000000000000000000000000000
XMM5  : 00000000000000000000000000000000
XMM6  : 00000000000000000000000000000000
XMM7  : 00000000000000000000000000000000
YMM0  : 0000000000000000000000000000000000000000000000000000000000000000
YMM1  : 0000000000000000000000000000000000000000000000000000000000000000
YMM2  : 0000000000000000000000000000000000000000000000000000000000000000
YMM3  : 0000000000000000000000000000000000000000000000000000000000000000
YMM4  : 0000000000000000000000000000000000000000000000000000000000000000
YMM5  : 0000000000000000000000000000000000000000000000000000000000000000
YMM6  : 0000000000000000000000000000000000000000000000000000000000000000
YMM7  : 0000000000000000000000000000000000000000000000000000000000000000
DR0 : 00000000
DR1 : 00000000
DR2 : 00000000
DR3 : 00000000
DR6 : 00000000
DR7 : 00000000


INT3 breakpoint at mbret4masm32.0040104F (0040104F)!
EXCEPTION_DEBUG_INFO:
           dwFirstChance: 1
           ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
          ExceptionFlags: 00000000
        ExceptionAddress: 0019FF82
        NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 00000002 Inaccessible Address
First chance exception on 0019FF82 (C0000005, EXCEPTION_ACCESS_VIOLATION)!

jj2007

Sorry, LiaoMi, I don't understand: what is not working? It doesn't assemble, or it crashes? Could you post a complete source please, so that I can check if there is a problem in my routine? Thanks :thup:

LiaoMi

Quote from: jj2007 on March 17, 2020, 01:17:34 AM
Sorry, LiaoMi, I don't understand: what is not working? It doesn't assemble, or it crashes? Could you post a complete source please, so that I can check if there is a problem in my routine? Thanks :thup:

Your finished example does not display messages like yours, instead, the program prints the first line and exits  :rolleyes:, after debugging, I showed the place where the crash occurs.

2 args: ebx=3 <- Exit
### MbRet 2ArgsNoLocals: stack is -4 bytes off, 1 push(es) too many ###
12345678: ebx after the invoke

2 args: ebx=2
12345678: ebx after the invoke

2 args: ebx=1
### MbRet 2ArgsNoLocals: stack is 4 bytes off, 1 pop(s) too many - check uses ###
0040107A: ebx after the invoke

CodeNoArgs - cannot work with @MbRet: ### MbRet 31: no stack frame found ###
### MbRet ThreeArgsLocal123: stack is -4 bytes off, 1 push(es) too many ###

hit any key for the stack corruption test:

2 args: ebx=2
### stack corruption? 2ArgsNoLocals: retaddr 12345678h is above the code segment ###