Announcement

Collapse
No announcement yet.

A small trick with the stack

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • A small trick with the stack

    I have been playing with an idea that probably can be used in a protection
    system that branches to a sub after pushing the return values onto the stack.

    The RET that the compiler places at the end of the sub removes
    the value pushed onto the stack and branches to the next return address
    that is pushed onto the stack.

    The subs do not show in a assembler dump as a referenced function so they
    are a lot harder to find and it is also an unusual technique that chains
    subs/functions in a non standard way.

    Regards,

    [email protected]
    Code:
      ' #########################################################################
      
          #COMPILE EXE
          #INCLUDE "D:\PB6\WINAPI\WIN32API.INC"
      
      ' #########################################################################
      
      FUNCTION PBmain as LONG
      
          LOCAL ret1 as LONG
          LOCAL ret2 as LONG
          LOCAL ret3 as LONG
      
          ret3 = CodePtr(nextLabel)
          ret2 = CodePtr(do_that)
          ret1 = CodePtr(do_this)
      
          ! push ret3
          ! push ret2
          ! jmp  ret1
      
          NextLabel:
          MsgBox "Hi, I am back"
      
          FUNCTION = 0
      
      End FUNCTION
      
      ' #########################################################################
      
      SUB do_this
      
          MsgBox "I am at 'do_this' sub"
      
      END SUB
      
      ' #########################################################################
      
      SUB do_that
      
          MsgBox "I am at 'do_that' sub"
      
      END SUB
      
      ' #########################################################################
    ------------------
    hutch at movsd dot com
    The MASM Forum

    www.masm32.com

  • #2
    Steve that's beautiful -- SUB do_that is being executed without actually being directly called? (so it wouldn't show up in a deadlisting?) So, it'd be ideal to store protection-related code in SUB do_that?


    ------------------
    -

    Comment


    • #3
      Wayne,

      The code will be in the dead listing but because it is not referenced
      directly by the CALL syntax, it will not be recognised as a function
      so it will be a lot harder to find.

      With the dead listing you normally have something like,
      Code:
          FN_00001:
            ; assembler code
            ret
      With this technique you usually end up with just the assembler code
      without the label at the beginning to mark the start of the function
      so it is no joy to find.

      The initial JMP ADDRESS can be traced but the second and subsequent
      sub/function that is called by this technique are much harder to trace
      because they have to find where the address was pushed onto the stack
      which can be earlier in the code.

      It can be traced in a debugger like SoftIce but that does not make the
      code and easier to modify. To protect the subs called this way from being
      nopped out, use them in other code and if they do not perform correctly
      reformat their hard disk or something else as friendly.

      Regards,

      [email protected]

      ------------------
      hutch at movsd dot com
      The MASM Forum

      www.masm32.com

      Comment


      • #4
        Steve,
        what is the link to that Batch file stuff you were discribing on the
        Win32 forum....I didn't understand it ....but would like to review it...

        It seemed it was a good security concept...?

        Thanks, Brad

        ------------------
        mailto:[email protected][email protected]</A>

        Comment


        • #5
          Steve yeah I love it, I never knew it was possible to go to an area of code without actually calling/jmping to it so I'll definately make good use of this trick sometime
          Keep these gems coming!

          ------------------
          -

          Comment


          • #6
            Brad,

            I don't remember anything specific about using batch files but I
            did make a comment some time ago about using a DOS EXE file to
            delete a 32 bit EXE file then delete itself. All you need to do
            is write a simple DOS program that reads the command line, store
            it in DB format in you main 32 bit EXE file, write it to disk
            before you end the program and call the EXE file as the last command
            in your own EXE file and the DOS program will delete your EXE and
            then itself.

            Its a technique for doing installation removal so that you can fully
            remove the directory with no dead junk leftover.

            Regards,

            [email protected]

            ------------------
            hutch at movsd dot com
            The MASM Forum

            www.masm32.com

            Comment


            • #7
              I see it, I believe it... But I don't quite understand it. Sorry,
              I'm practically a assembly illiterate.
              So, I take it that when you write in assembly, you have to push
              the return address to the stack? And when a END SUB is reached,
              the compiler adds a pop? What about END FUNCTION?
              You guys don't have to waste your time answering... Just wishing
              I understood assembly a little better...

              ------------------

              Comment


              • #8
                Lloyd,

                All that is happening here is that the CALL/RET syntax that is
                built into the processor is being used in a non-standard way.

                When you use the assembler instruction CALL, the address of the
                following instruction is pushed onto the stack and then the
                instruction pointer EIP is moved to the address that is called.

                This is something like a JUMP while storing the return address
                on the stack. What this example does is precalculate the return
                addresses of both subs and the following label from where it is
                started and then JUMPs to the first address.

                The assembler instruction RET will return to the address that is
                stored on the stack which in this case is not the address placed
                there by a CALL but the address that has been pushed onto the stack
                manually.

                By pushing a number of return addresses onto the stack and then
                using JMP to the first, you chain the locations that RET returns
                to.

                One place where it can be useful is to access a function that is
                not normally accessed in code for things like product registration
                or other protection schemes. This is useful here because assembler
                dumps tend to assume CALL/RET syntax and when a piece of code is not
                accessed in this manner, recognition of the location of a function
                in an assembler dump is one hell of a lot harder.

                Regards,

                [email protected]

                ------------------
                hutch at movsd dot com
                The MASM Forum

                www.masm32.com

                Comment


                • #9
                  Removing addresses from the stack, placing another one "underneath", and then replacing them was a technique I used a lot in the old days of Z80 assembler (still my favourite!).

                  ------------------

                  Comment


                  • #10
                    I found a variation for the JMP in the original example, instead
                    of using,
                    Code:
                      ! jmp  ret1
                    Use
                    Code:
                      ! push ret1
                      ! retn
                    If you bother to spread the pushes between function calls and place
                    the RETN near the end of the function/sub the disassembly is a
                    true joy to behold in terms of comprehension. Should be enough to
                    drive the would be cracker nutz.

                    Regards,

                    [email protected]

                    ------------------
                    hutch at movsd dot com
                    The MASM Forum

                    www.masm32.com

                    Comment


                    • #11
                      Eesh, not even a single jump now! That's a Jedi trick if ever i've seen one
                      Now to implement it ...

                      ------------------
                      The PowerBASIC Crypto Archives
                      -

                      Comment


                      • #12
                        Thought I would resurrect this old post with a slightly new wrinkle: applying MACROS to the mix.

                        The code:

                        Code:
                        MACRO keyin
                           LOCAL ret1&, ret2&
                           ret2& = CODEPTR(ImDone)
                           ret1& = CODEPTR(MMsg1)
                           ! push ret2&
                           ! push ret1&
                        END MACRO
                        
                        MACRO keyout
                           ! retn
                           ImDone:
                        END MACRO
                        
                        SUB MMsg1
                           MSGBOX "Message 1"
                        END SUB
                        Insert these lines in your code. Then, in any sub or function, place keyin at the beginning and keyout at the end. "Message 1" will appear each time the function or sub executes.

                        By using MACROS, you can easily sprinkle a validity check throughout your code (more than one variation if you want to create additional MACROS). The possibilities are limited only by your imagination.

                        One issue: if you execute an EXIT SUB or EXIT FUNCTION before reaching keyout, there will be problems, so this must be taken into consideration as keyout can only appear in each routine once (else the compiler will complain).
                        --
                        <strong>Billing clients for your freelance work?</strong> Try <a href="http://www.minute-2-minute.com">Minute-2-Minute</a>, the project management, timing, and billing system. Perfect for programmers who charge by the hour. FREE 45-day trial.

                        Comment


                        • #13
                          >Thought I would resurrect this old post with a slightly new wrinkle

                          And I thought I'd move it to a more appropriate place - a forum which did not exist during its first life.

                          MCM
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            back in the day

                            I remember doing this back in the 90's on the amiga just to save cycles, it was quicker to drop the return address and jump to the sub routine. Even system calls were quicker, grab all the call addresses and build a table then jmp to the required routine which now sounds so dangerous but that was back in the day.

                            Comment


                            • #15
                              I think we all did stuff "back in the day."

                              As mainframe COBOL programmers we had to deal with a 64K table size limit (that would be an "Array" to PB programmers).

                              BUT.. you could take advantage of the the way the compiler worked by coding your tables contiguously in the SOURCE code..
                              Code:
                                01  FILLER.
                                     05  DATAITEM      PIC X (1000) OCCURS 64 TIMEs. 
                                01  FILLER.    
                                     05  FILLER           PIC X (1000) OCCURS 64 TIMES.
                              .. which resulted in contiguous addresses in the executable.. so you could then refer to DATAITEM (subscript) where subscript was any number between 1 and 2000... basically giving you a 128Kb table.

                              Cool huh? Well, as Paul Harvey might have said, "Here's the rest of the story."

                              When IBM introduced the VS COBOL II compiler, it not longer compiled contiguous source code into contiguous runtime addresses, so any code written like this was, to use the technical term, "broke."

                              Fortunately VS COBOL II did not limit tables to 64 K. (They went to 2 G) so it was not too hard to fix.

                              MCM
                              Michael Mattias
                              Tal Systems (retired)
                              Port Washington WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment


                              • #16
                                Hi Steve,

                                Nice piece of code, as usual

                                I made an observation and tried to replace the CODEPTR() function with asm eqv. I didn't expected that dead code removal feature of new compiler will remove SUB's names used within asm code...

                                Probably, this observation/question must go to R&D team, but before sending I'd like to know whether compiler is checking references within assembly code or this applies to BASIC code only?

                                Comment


                                • #17
                                  Steve,
                                  I give you credit for your technique, but I'm sorry to say it gave me a wry chuckle as it drew from the depths of years of dusty memories... that your technique was actually the "normal" way to perform a subroutine call in the assembly language of the RCA 1802!!! It didn't have a complete CALL/RETURN mechanism as did later microprocessors and programming languages.

                                  Thanks for opening the door to that memory!

                                  -John

                                  Comment


                                  • #18
                                    I only came to grab your Character stripper algo code and have just seen this.
                                    It looks very useful indeed.
                                    Thank you

                                    Comment

                                    Working...
                                    X