Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

DLL Injection and function interception tutorial

, 23 Oct 2003
How to inject a DLL into a running process and then intercept function calls in statically linked DLLs.
injecto_src.zip
Injecto_src
src
DLL
DLL.dsw
DLL.dsp
EXE
EXE.dsp
EXE.dsw
OPCODES2.HLP
IntelCodeTable.pdf
adams_asm_tut
STARS.PAS
download.info
PALETTE.DAT
PLASMA.DAT
SINTABLE.DAT
          ��������������������������������������������������������ͻ
          �             Adam's Assembler Tutorial 1.0              �Ŀ
          �                                                        � �
          �                        PART IV                         � �
          ��������������������������������������������������������ͼ �
            ����������������������������������������������������������


Revision :  1.5
Date     :  01-03-1996
Contact  :  blackcat@faroc.com.au
            http://www.faroc.com.au/~blackcat

Note     :  Adam's Assembler Tutorial is COPYRIGHT, and all rights are
            reserved by the author.  You may freely redistribute only the
            ORIGINAL archive, and the tutorials should not be edited in any
            form.


 ����������������������������������������������������������������������������

Welcome back, budding Assembler coders.  The tutorials seem to be getting
popular now, and I've had some mail requesting me to cover the VGA so I'll
give it a go.  This is basically what I've been leading up to in my own
disjointed way anyhow, as graphics programming is not only rewarding, it's
also fun too!  Well, I think it's fun.  :)

Firstly though, we must finish off the CMP/JMP stuff, and cover shifts.  When
you're coding in Assembler, you'll find comparisons, shifts and testing bits
are very common operations.


 ����������������������������������������������������������������������������

 A Comparison Example
-----------------------

I won't bother going over the following example - it's fairly easy to
understand and you should get the basic idea anyway.

   DOSSEG
   .MODEL SMALL
   .STACK 200h
   .DATA

FirstString    DB  13, 10, "Is this a great tutorial or what? :) - $"
SecondString   DB  13, 10, "NO? NO? What do you mean, NO?$"
ThirdString    DB  13, 10, "Excellent, let's hear you say that again.$"
FourthString   DB  13, 10, "Just a Y or N will do.$"
ExitString     DB  13, 10, "Fine, be like that!$"

   .CODE

START:
   MOV   AX, @DATA                   ; New way of saying:
   MOV   DS, AX                      ; DS -> SEG data segment

KeepOnGoing:
   MOV   AH, 9
   MOV   DX, OFFSET FirstString      ; DX -> OFFSET FirstString
   INT   21h                         ; Output the first message

   MOV   AH, 0                       ; Get a key - store it in AX
   INT   16h                         ; AL - ASCII code, AH - scan code
                                     ; It doesn't echo onto the screen
                                     ; though, we have to do that ourselves

   PUSH  AX                          ; Here we display the char - note that
   MOV   DL, AL                      ; we save AX.  Obviously, using AH to
   MOV   AH, 2                       ; signal to print a string destroys AX
   INT   21h
   POP   AX

   CMP   AL, "Y"                     ; Check to see if 'Y' was pressed
   JNE   HatesTute                   ; If it was, keep going

   MOV   AH, 9                       ; Display the "Excellent..." message
   MOV   DX, OFFSET ThirdString
   INT   21h
   JMP   KeepOnGoing                 ; Go back to the start and begin again

HatesTute:
   CMP   AL, "N"                     ; Make sure it was 'N' they pressed
   JE    DontLikeYou                 ; Sadly, it was equal

   MOV   DX, OFFSET FourthString     ; Ask the user to try again
   MOV   AH, 9
   INT   21h
   JMP   KeepOnGoing                 ; Let 'em try

DontLikeYou:
   MOV   DX, OFFSET SecondString     ; Show the "NO? NO? What..." string
   MOV   AH, 9
   INT   21h

   MOV   DX, OFFSET ExitString       ; Show the "Fine, be like that!" string
   MOV   AH, 9
   INT   21h

   MOV   AX, 4C00h                   ; Return to DOS
   INT   21h
END START

You should understand this example, play around with it and write something
better.  Those with a "Peter Norton's Guide to..." book or similar,
experiment with the keyboard subfunctions, and see what other similar GetKey
combinations exist, or better still, play around with interrupt 10h and
go into some weird video mode - one which your PC supports! - and use some
color.


 ����������������������������������������������������������������������������

 Shifts
--------

A simple concept, and one which I should have discussed before, but like I
said - I have my own disjointed way of going about things.

First you'll need to understand some hexadecimal and binary arithmetic - a
subject I _should_ have covered before.  I usually use a scientific
calculator - hey, I always use a calculator, I'm not stupid! - but it is good
to be able to know how to multiply, add and convert between the various bases.

You also cannot use a calculator in Computing exams, not in Australia anyway.


 CONVERTING BINARY TO DECIMAL:

Way back in Tutorial One we looked at what binary numbers look like, so
imagine I have an eight-bit binary number such as:

 11001101

What is this in decimal???  There are a number of ways to convert such a
number, and I use the following, which I believe is probably the easiest:

 �����������������������������������������������������������������ͻ
 �  Binary Number        �  1  �  1 �  0 �  0 �  1 �  1 �  0 �  1  �
 �����������������������������������������������������������������Ķ
 �                       �   7 �  6 �  5 �  4 �  3 �  2 �  1 �  0  �
 �  Decimal equivalent   �  2  � 2  � 2  � 2  � 2  � 2  � 2  � 2   �
 �����������������������������������������������������������������Ķ
 �  Decimal equivalent   � 128 � 64 � 32 � 16 �  8 �  4 �  2 �  1  �
 �����������������������������������������������������������������������ķ
 �  Decimal value        � 128 + 64 +  0 +  0 +  8 +  4 +  0 +  1  = 205 �
 �����������������������������������������������������������������������ͼ

Get the idea?  Note for the last line, it would be more accurate to write:

   1 x 128 + 1 x 64 + 0 x 32 + 0 x 16 + 1 x 8 + 1 x 4 + 0 x 2 + 1 x 1
 =     128 +     64 +      0 +      0 +     8 +     4 +     0 +     1
 = 205

Sorry if this is a little confusing, but it is difficult to explain without
demonstrating.  Here's another example:

 �����������������������������������������������������������������ͻ
 �  Binary Number        �  0  �  1 �  1 �  1 �  1 �  1 �  0 �  0  �
 �����������������������������������������������������������������Ķ
 �                       �   7 �  6 �  5 �  4 �  3 �  2 �  1 �  0  �
 �  Decimal equivalent   �  2  � 2  � 2  � 2  � 2  � 2  � 2  � 2   �
 �����������������������������������������������������������������Ķ
 �  Decimal equivalent   � 128 � 64 � 32 � 16 �  8 �  4 �  2 �  1  �
 �����������������������������������������������������������������������ķ
 �  Decimal value        �  0  + 64 + 32 + 16 +  8 +  4 +  0 +  0  = 124 �
 �����������������������������������������������������������������������ͼ

Note:

   � You can use this technique on 16 or 32-bit words too, just work your way
     up.  Eg:  After 128, you'd write 256, then 512, 1024 and so on.

   � You can tell if the decimal equivalent will be odd or even by the first
     bit.  Eg:  In the above example, the first bit = 0, so the number is
     EVEN.  In the first example, the first bit is 1, so the number is ODD.

FUN FACT:  In case you didn't already know, bit stands for Binary digIT.  :)


 CONVERTING DECIMAL TO BINARY:

This is probably easier than base-2 to base-10.  To find out what 321 would be
in binary, you'd do the following:

    321                    =    256  X  1
    321 - 256 = 65         =    128  X  0
    65                     =     64  X  1
    65  -  64 = 1          =     32  X  0
    1                      =     16  X  0
    1                      =      8  X  0
    1                      =      4  X  0
    1                      =      2  X  0
    1                      =      1  X  1

And you get the binary number - 101000001.  Easy huh?   Let's just try another
one to make sure we know how:

    198                    =    128  X 1
    198 - 128 = 70         =     64  X 1
    70  -  64 =  6         =     32  X 0
    6                      =     16  X 0
    6                      =      8  X 0
    6                      =      4  X 1
    6   -   4 =  2         =      2  X 1
    2   -   2 =  0         =      1  X 0

And this gives us - 11000110.  Note how you can check the first digit to see
if you got your conversion right.  When I wrote the first example, I noticed
I had made a mistake when I checked the first bit.  On the first example, I
got 0 - not good for an odd number.  I realised my mistake and corrected the
example.


 CONVERTING HEXADECIMAL TO DECIMAL:

Before we begin, you should know that the hexadecimal number system uses the
'digits':

   0         =  0 (decimal)  =     0 (binary)
   1         =  1 (decimal)  =     1 (binary)
   2         =  2 (decimal)  =    10 (binary)
   3         =  3 (decimal)  =    11 (binary)
   4         =  4 (decimal)  =   100 (binary)
   5         =  5 (decimal)  =   101 (binary)
   6         =  6 (decimal)  =   110 (binary)
   7         =  7 (decimal)  =   111 (binary)
   8         =  8 (decimal)  =  1000 (binary)
   9         =  9 (decimal)  =  1001 (binary)
   A         = 10 (decimal)  =  1010 (binary)
   B         = 11 (decimal)  =  1011 (binary)
   C         = 12 (decimal)  =  1100 (binary)
   D         = 13 (decimal)  =  1101 (binary)
   E         = 14 (decimal)  =  1110 (binary)
   F         = 15 (decimal)  =  1111 (binary)

You'll commonly hear hexadecimal referred to as hex, or base-16 and it is
commonly denoted by an 'h' - eg 4C00h, or a '$', eg - $B800.

Working with hexadecimal is not as hard as it may look, and converting back
and forth is pretty easy.  As an example, we'll convert B800h to decimal:

FUN FACT: B800h is the starting address of the video in text mode for CGA and
          above display adaptors.  :)

          B    = 4096 x B = 4096 x 11 = 45056
          8    =  256 x 8 =  256 x  8 =  2048
          0    =   16 x 0 =   16 x  0 =     0
          0    =    1 x 0 =    1 x  0 =     0

          So B800h = 45056 + 2048 + 0 + 0
                   = 47104

          Note:  For hexadecimal numbers greater than FFFFh (65535 decimal),
                 you merely follow the same procedure as for binary, so for
                 the fifth hexadecimal digit, you'd multiply it by 65535.

                 Hit 16 X X on your calculator, and keep hitting =.  You'll
                 see the numbers you'd need to use.  The same applies for
                 binary.  Eg:  2 X X and = would give you 1, 2, 4, 8, 16...
                 etc.

Okay, that seemed pretty easy.  I don't even think we need a second example.
Let's have a crack at:


 CONVERTING DECIMAL TO HEXADECIMAL:

Again, the same sort of procedure as the one we followed for binary.  So
convert 32753 to hexadecimal, you'd do:


          32753 / 4096                  =  7 (decimal) = 7h

          32753 - (4096 x 7) = 4081

          4081 /  256                   = 15 (decimal) = Fh

          4081 - (256 x 15)  =  241

          241 / 16                      = 15 (decimal) = Fh

          241 - (16 x 15)    = 1

          1 / 1                         =  1 (decimal) = 1h


So eventually we get 7FF1h as our answer.  This is not a particularly nice
process and requires some explanation.

   1) When you divide 32753 by 4096 you get 7.9963379... We are not interested
      in the .9963379 rubbish, we just take the 7, as 7 is the highest whole
      number that we can use.

   2) The remainder left over from the above operation is 4081.  We must now
      perform the same operation on this, except with 256.  Dividing 4081
      by 256 gives us 15.941406...  Again, we just take the 15.

   3) Now we have a remainder of 241.  Dividing this by 16 gives us 15.0625.
      We take the 15, and calculate the remainder.

   4) Our last remainder just happens to be one.  Dividing this by one gives
      us, you guessed it - one.  YOU SHOULD NOT GET AN ANSWER TO SEVERAL
      DECIMAL PLACES HERE.  IF YOU HAVE - YOU HAVE DONE THE CALCULATION WRONG.

It's a particularly nasty process, but it works.  I do not use this except
when I have to - I'm not crazy - I use a scientific calculator, or Windows
Calculator <shudder> if I must.


 ����������������������������������������������������������������������������

Okay, now we've dealt with the gruesome calculations, you're ready for
shifts.  There are generally two forms of the shift instruction - SHL (shift
left) and SHR (shift right).  Basically, all these instructions do is shift
and expression to the left or right by a number of bits.  Their main
advantage is their ability to let you replace slow multiplications with much
faster shifts.  You will find this will speed up pixel/line/circle algorithms
by an amazing amount.

PC's are becoming faster and faster by the day - a little too fast for my
liking.  Back in the days of the XT - multiplication was _really_ slow -
perhaps taking up to four seconds for certain operations.  Not so much of this
applies today, but it is still a good idea to optimize your code.

When we plot a pixel onto the screen, we have to find the offset for the pixel
to plot.  Basically, what we do is to multiply the Y location by 320, add the
X location onto it, and add this to address A000h.

So basically, we get:   A000:Yx320+X

Now, as fast as your wonderful 486 or Pentium machine is, this could be made
a lot faster.  Lets rewrite that equation above so we use some different
numbers:

                            8          6
              Offset = Y x 2   +  Y x 2  + X
Or:
              Offset = Y x 256 +  y x 64 + X

Recognise those numbers?  They look an awful lot like the ones we saw in that
binary-to-decimal conversion table.  However, we are still using
multiplication.  How can we incorporate shifts into the picture?

What about:

              Offset = Y SHL 8 + Y SHL 6 + X

Now this is a _lot_ faster, as all the computer has to do is shift the number
left - much better.   Note that shifting to the left INCREASES the number,
and shifting to the right will DECREASE the number.

Here's an example that may help you if you are still unsure as to what is
going on.  Let's say that we're working in base-10 - decimal.  Now let's take
the number 36 as an example.  Shifting this number LEFT by 1, gives us:

  36  +  36                                             = 72

Now SHL 2:

  36  +  36  +  36  +  36                               = 144

And SHL 3:
  36 +  36   +  36  +  36  +  36  +  36  +  36  +  36   = 288

Notice the numbers forming?  There were 2 36's with SHL 1, 4 36's with SHL 2
and 8 36's with SHL 3.  Following this pattern, it would be fair to assume
that 36 SHL 4 will equal 36 x 16.

Note however, what is really happening.   If you were to work out the binary
value of 36, which looks like this: 100100, and then shifted 36 LEFT by two,
you'd get 144, or 10010000.  All the CPU actually does it stick a few extra
1's and 0's in a location in memory.


As another example, take the binary number 1000101.  If we were to shift it
LEFT 3, we'd end up with:

        1 0 0 0 1 0 1
          <---------- SHL 3
  1 0 0 0 1 0 1 0 0 0

Now lets shift the number 45 RIGHT 2.  In binary this is 101101.  Hence:

        1 0 1 1 0 1
        SHR 2 ---->
            1 0 1 1

Notice what has occurred?  It is much easier for the CPU to just move some
bits around, (approximately 2 clock ticks), rather than to multiply a number
out.  (Can get to around 133 clock ticks).

We will be using shifts a lot when programming the VGA, so make sure you
understand the concepts behind them.


 ����������������������������������������������������������������������������

         ����������������������������������������������������������Ŀ
         �                                                          �
         �            PROGRAMMING THE VGA IN ASSEMBLER              �
         �                                                          �
         ������������������������������������������������������������


I have received quite a bit of mail asking me to cover the VGA.  So for all
those who asked, we'll be spending most of our time, but not all, on
programming the VGA.  After all, doesn't everyone want to code graphics?

When we talk about programming the VGA, we are generally talking about mode
13h, or one of its tweaked relatives.  In standard VGA this is the _only_ way
to use 256 colors, and it's probably one of the easiest modes to use too.
If you've ever tried experimenting with SVGA, you'll understand the nightmare
it is for the programmer in supporting all the different SVGA cards that
exist - except if you use VESA that is, which we'll discuss another time.  The
great thing about standard mode 13h is you know that just about every VGA card
in existence will support it.  People today often ignore mode 13h, thinking
the resolution to be too grainy by today's standards, but don't forget that
Duke Nukem, DOOM, DOOM II, Halloween Harry and most of the Apogee games use
this mode to achieve some great effects.

The great thing about mode 13h - that's 320x200x256 in case you were unaware,
is that accessing VGA RAM is incredibly easy.  As 320 x 200 only equals
64,000, it is quite possible to fit the entire screen into one 64K segment -
leaving out the hell of planes, (or should that be plains of Hell?), and
masking registers.

The bad news is that standard mode 13h really only gives you one page to use,
seriously hampering scrolling and page-flipping.  We'll later cover how to
get into your own modes - and mode X which will avoid these problems.


So, how do you get into the standard mode 13h?

The answer is simple.  We use interrupt 10h - video interrupt, and call
subfunction 00h - set mode.  In Pascal, you could declare a procedure like
this:

Procedure Init300x200;   Assembler;

Asm     { Init300x200 }
   mov   ah, 00h         { Set video mode }
   mov   al, 13h         { Use mode 13h   }
   int   10h             { Do it          }
End;    { Init300x200 }


You may also see:

   mov   ax, 13h
   int   10h

This is perfectly correct, and probably saves one clock tick by not putting
00h in AH and then 13h in AL, but it is more correct to use the first
example.


Okay, so we're in mode 13h, but what can we actually do in it, other than look
at a blank screen?  We could go back to text mode by using:

   mov   ah, 00h
   mov   al, 03h
   int   10h

but that's a little dull.  Why not plot a pixel?


 ����������������������������������������������������������������������������

There are a number of ways you could get a pixel on the screen.  The easiest
way in Assembler is to use interrupts.  You do it like this in Pascal:

Procedure PutPixel(X, Y : Integer; Color : Byte);   Assembler;

Asm     { PutPixel }
   mov   ah, 0Ch        { Draw pixel subfunction        }
   mov   al, [Color]    { Move the color to plot in AL  }
   mov   cx, [X]        { Move the X value into CX      }
   mov   dx, [Y]        { Move the Y value into DX      }
   mov   bx, 1h         { BX = 1, page 1                }
   int   10h            { Plot it                       }
End;    { PutPixel }


However, even though this is in Assembler, it isn't particularly speedy.  Why
you ask?  Because it uses interrupts.  Interrupts are fine for getting in and
out of video modes, turning the cursor on and off, etc... but not for
graphics.

You can think of interrupts like an answering machine.  "The CPU is busy right
now, but if you leave your subfunction after the tone - we'll get back to
you."

Not good.  Let's use that technique we discussed earlier during shifts.  What
we want to do is put the value of the color we want to plot into the VGA
directly.  To do this, we'll need to move the address of the VGA into ES,
and calculate the offset of the pixel we want to plot.  An example of this
is shown below:

Procedure PutPixel(X, Y : Integer; Color : Byte);   Assembler;

Asm     { PutPixel }
   mov   ax, 0A000h     { Move the segment of the VGA into AX,   }
   mov   es, ax         { and now into ES                        }
   mov   bx, [X]        { Move the X value into BX               }
   mov   dx, [Y]        { Move the Y value into DX               }
   mov   di, bx         { Move X into DI                         }
   mov   bx, dx         { Move Y into BX                         }
   shl   dx, 8          { In this part we use shifts to multiply }
   shl   bx, 6          { Y by 320                               }
   add   dx, bx         { Now here we add X onto the above,      }
   add   di, dx         { giving us DI = Y x 320 + X             }
   mov   al, [Color]    { Put the color to plot into AL          }
   stosb                { Put the byte, AL, at ES:DI             }
End;    { PutPixel }

This procedure is fast enough to begin with, though I gave out a much faster
one a few tutorials ago which uses a pretty ingenious technique to get DI.


 ����������������������������������������������������������������������������

Okay, I think that's enough for this week.  Have a play with the PutPixel
routines and see what you can do with them.  For those with a "Peter Norton's
Guide to..." book, see what other procedures you can make using interrupts.


 THINGS TO DO:

    1) We covered a lot in this tutorial, and some important concepts were
       in it.  Make sure you are comfortable with the comparisons, because
       we'll get into testing bits soon.

    2) Make sure you understand the binary -> decimal, decimal -> binary,
       decimal -> hex and hex -> decimal stuff.  Make yourself some example
       sums and test your answers with Windows Calculator.

    3) You _must_ understand shifts.  If you are still having problems,
       make some expressions up on paper, and test your answers with a program
       such as:

       Begin   { Main }
          WriteLn(45 SHL 6);
          ReadLn;
       End.    { Main }

       and/or Windows Calculator.

    4) Have a look at the VGA stuff, and make sure you have grasped the theory
       behind it, because next week we're really going to go into it in
       depth.

Next week I'll also try to give some C/C++ examples as well as the Pascal ones
for all you C programmers out there.


 ����������������������������������������������������������������������������

Next tutorial will cover:

   � How the VGA is arranged
   � How we can draw lines and circles
   � Getting and setting the palette in Assembler
   � Fades
   � Some C/C++ examples

If you wish to see a topic discussed in a future tutorial, then mail me, and
I'll see what I can do.


 ����������������������������������������������������������������������������

Don't miss out!!!  Download next week's tutorial from my homepage at:

  � http://www.faroc.com.au/~blackcat


See you next week!

- Adam.

" I _never_ write code with bugs, I just add some unintentional features! "

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

CrankHank

Qatar Qatar
Nasser R. Rowhani
Programming simply pumps my adrenaline..
 
Okay... I like people critisizing me...
Let me fix this article...

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 24 Oct 2003
Article Copyright 2003 by CrankHank
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid