Statistics

Members: 1927
News: 293
Web Links: 1
Visitors: 4047965

Who's Online

Damn Vulnerable LinuxDamn Vulnerable Linux (DVL) is a Linux-based (modified Damn Small Linux) tool for IT-Security & IT-Anti- Security and Attack & Defense. [CLICK HERE FOR MORE INFOS! ]

Featured Conference Video

T16-Recon2006-Joe_Stewart-OllyBonE.gif OllyBone - Semi-Automatic Unpacking on IA-32. View the conference video here!
Home
ASM Building Blocks
User Rating: / 0
PoorBest 
Written by Laura Fairhead   


Here are some simple but very powerful library routines, primarily concerned with screen output. They all follow the same conventions:

  • Routines preserve all registers that they are not specified to return.
  • The direction flag (DF) should always be clear before calling.

All code is presented in MASM format. I do not use very many of the functions of this assembler so it should be trivial to assemble these under a different one. I do, however, use OPTION SCOPED, this means that labels within a PROC block are local to that PROC block (a double colon suffixed label is given global scope though).

First come the primitive routines. These are responsible for the actual output and simply call DOS to do it. The name for this sort of thing is called a 'wrapper' function. It does nothing in itself except afford a particular interface to an application. If all your access to the OS is in a small number of logical wrapper functions then porting your code to other systems becomes a lot easier.

;pstrcx- write CX characters to stdout ; uses DOS function 040h
;

;entry:     DS:SI=string address
;           CX=length of string
;
;exit:      (no parameters are returned)

pstrcx PROC NEAR

;assume that DOS can't handle a zero-byte write ;(I don't trust those M$ programmers)

        JCXZ don
PUSH AX
PUSH BX
MOV AH,040h
MOV BX,1            ;stdout is handle #1
XCHG DX,SI
INT 021h
XCHG DX,SI
POP BX
POP AX

don: RET

pstrcx ENDP

Note the use of XCHG. XCHG is an extremely useful instruction indeed, even though there are those who wish to see it's death along with all those other "horrible, odd-ball, x86 specific". XCHG in essence performs two operations simultaneously, which is hideously useful considering they are both MOV's, also if one of the registers is AX (or EAX in 32-bit code) you get a lovely 1 byte instruction bonus.

XCHG is in fact the real instruction hiding behind the psuedo-op NOP. If you look at the opcode for a NOP, it is 090h, this is actually the encoding for XCHG AX,AX, which since it has no effect on the machine state whatsoever (except of course IP+=1) is ideally suited for this.

I haven't looked back since adding putch to my library. I used to use the sequence:-

MOV DL,<char>; MOV AH,2; INT 021h

Not only is the putch method much cleaner and more flexible it is also saving bytes! Of course the pay-back is that this method adds clocks. However if you think about it the wasted clocks are meaningless really. Sending characters one at a time to stdout is rather like spelling out a dictate to your secretary letter-by-letter. In a case where you want more MIPS you should be looking at your higher level algorithm and not the output routine, an INT takes a vast amount of time anyway...

;putch-     write single character to stdout
;           uses DOS function 02h
;
;entry:     AL=character to write
;
;exit:      (no parameters are returned)

putch PROC NEAR

        PUSH DX
XCHG DX,AX
MOV AH,2
INT 021h
XCHG DX,AX
POP DX
RET

putch ENDP

Not hot on speed this strlen, it was written to be compact. You can if you wish write MUCH faster code than this. I believe X-Bios2 presented something along these lines in a previous APJ. However, the most important thing here is certainly not speed, and again if you wanted speed on string handling so badly, you should really not use asciiz at all; it was never designed for that.

;strlen- return length of asciiz string ;
;entry: DS:SI=address of asciiz string ;
;exit: CX=length of string

strlen PROC NEAR

        PUSH AX
XOR CX,CX
DEC CX

lop: INC CX

        LODSB
CMP AL,1
JNC lop
SBB SI,CX
POP AX
RET

strlen ENDP

Now, already, we start getting serious payback for being so good. The code virtually writes itself.....

;pstr- write asciiz string to stdout ;
;entry: DS:SI=address of asciiz string ;
;exit: (no parameters are returned)

pstr PROC NEAR

        PUSH CX
CALL NEAR PTR strlen
CALL NEAR PTR pstrcx
POP CX
RET

pstr ENDP

;pstrcr- write asciiz string to stdout with appended newline ;
;entry: DS:SI=address of asciiz string ;
;exit: (no parameters are returned)

pstrcr PROC NEAR

        CALL NEAR PTR pstr
JMP NEAR PTR outcr

pstrcr ENDP

;outcr- write newline to stdout
;
;entry: (no entry parameters)
;
;exit: (no parameters are returned)

outcr PROC NEAR

        PUSH AX
MOV AL,0Dh;CALL NEAR PTR putch
MOV AL,0Ah;CALL NEAR PTR putch
POP AX
RET

outcr ENDP

;pchn- write repeated character to stdout ;

;entry:     AL=character
;           CX=repetitions  (0 is valid and does nothing)
;
;exit:      (no parameters are returned)

pchn PROC NEAR

        JCXZ don
PUSH CX
lop:    CALL NEAR PTR putch
LOOP lop
POP CX

don: RET

pchn ENDP

;pstrlcl- output string DS:SI left justified in a field ; of CL spaces
;

;           if the field width is smaller than the string length
;           then the string is simply output
;
;entry:     DS:SI=asciiz string
;           CL=field width
;
;exit:      (all registers preserved)

pstrlcl PROC NEAR

        PUSH AX
PUSH CX
CALL NEAR PTR pstr
MOV CH,0
XCHG CX,AX
CALL NEAR PTR strlen
SUB AX,CX
JNA SHORT don
XCHG CX,AX
MOV AL,020h
CALL NEAR PTR pchn
don:    POP CX
POP AX
RET

pstrlcl ENDP

Note the use of JNA. If you look at the logic for the JNA branch (not many people seem to do this) you find that it branches iff CF=1 OR ZF=1, hence after the SUB if the result goes <=0

You may notice that all the routine names are <= 8 chars. The reason for this being that you can save each one as a seperate file, giving it the name of the routine. This allows easy reference but has a drawback or two:

(i) you have to remember the dependencies when you INCLUDE them (ii) you end up with a LOT of files

So far I haven't found either of these 'drawbacks' to be a serious problem.

I will be referring back to routines a lot in future articles; whenever routines are required I will state it and the code shall have a list of INCLUDE's for the routines to be included. In this manner it will be possible to present quite untrivial programs within a reasonable amount of space.