53魔力发布网,提供最好最新的开机信息

查看完整版本: 汇编基础教程

妖哥 2008-5-5 05:12

汇编基础教程

汇编语言基础
   要看懂汇编基础,得学会C语言,这样子,就很容易的理解汇编程序代码!
   电脑是由输出,输入,存储器,扩展槽,处理器(控制器,计算器,寄存器等)等组合而成!
不过,硬件发展得很快速,所以工程师们又想到了更能发挥硬件信号的处理,所以又产生了新的语言,通过虚似信号来控制整个计算机,比如,控制器通过电容,电阻,存储器还有输出输入产生的信号,所模似出他们的变频信号,再通过控制器处理所有信号!也让所有硬件设备产生的信号,能更好的控制!不过,我们现在得先了解汇编语言,汇编语言和C/C++一样,编辑出汇编语言代玛,再编译成机器代码,比如,汇编语言,让他能在机器语言代码中识别,就把机器语言的2进制翻译成汇编语言代码,而更好的接近我们的语言!
  CPU(分为控制器和计算器) 内存(为内部存储器--》现在的发展,主板控制系统已经支持内存上G的存储了,存储器最基础单位叫字位,每个字位可存放一个二进制数,0或1表示。每八个字位组成一个字节(BYTE)中文叫比特,为一个存储单元。每个字节只能存放一个字符,这个字符可以是一个数字,一个字母,一个符号,也可以是一个操作指令。通常几个字节组成一个数据,称为一个字,多个字组成一条语句)
   每个存储单元是从0开始顺序编号,我们可以用B来代表一个比特,1KB=1024b 1MB=1024KB 1GB=1024MB 1TB=1024GB
   一个字节由8个比特(2进制)组成,一个字由两个字节组成,这两个字节分别是高位字节和底位字节。
   CPU要从内存中读数据,首先要指定存储单元的地址。
   高级语言分为解释过程:源程序送入计算机后,解释程序读一条语句,分析解释这条语句并翻译成目标程序执行,再读下一条语句,直到处理完整个源程序。我们称这种工作方式为解释方式。
编译过程:用户用高级语言编的源程序输入到计算机后,编译程序便把源程序整个翻译成用机器指令表示的目标程序,然后,在一个称作连接编辑程序组成了编译系统!
  汇编/C/C++是编译过程的程序!
记注--汇编语言只能在8086CPU上运行!
8086CPU有14个寄存器,每一个都有自己的名称,AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW。
8068CPU所有的寄存器都是16位的,每个可以放两个字节。通用的有4个AX,BX,CX,DX,他们常用来存放一般性的数据!
AX可分为AH,AL
BX可分为BH,BL
CX可分为CH,CL
DX可分为DH,DL

MOV指令称为传送指令!

现在看看下面的汇编代码
  mov ax,18     “将18送入寄存器AX”                      “AX=18”
  mov ah,78     “将78送入寄存器AH”                      “AH=78”
  add ax,8      “将寄存器AX中的值加上8”                 “AX=AX+8”
  mov ax,bx     “将寄存器BX中的数据送入寄存器AX”        “AX=BX”
  add ax,bx     “将AX和BX中的数值相加,结果存在AX中”    “AX=AX+BX”
分析下面AX和BX的数据
  mov ax,4E20H   “AX=4E20H”             “BX=0000H”
  add ax,1406H   “AX=6226H”             “BX=0000H”
  mov bx,2000H   “AX=6226H”             “BX=2000H”
  add ax,bx      “AX=8226H”             “BX=2000H”
  mov bx,ax      “AX=8226H”             “BX=8226H”
  add ax,bx      “AX=16452H”            “BX=8226H”
看懂上面的几条汇编指令吗?
   段地址*16+偏移地址=物理地址
   8086CPU有4个段寄存器:CS,DS,SS,ES
   CS和IP是8086CPU中最关键的寄存器,他们指示了CPU当前要读取指令的地址,CS为代码段寄存器,IP为指令指针寄存器。

   但MOV指令不能用于设置CS,IP的值,修改CS,IP的内容是通过JMP指令来修改,JMP被统称为转移指令。
  要想修改CS,IP的内容,可用指令"JMP 段地址:偏移地址"完成。
  看看下面的汇编指令
  JMP 2AE3:3   执行后:CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令。
  JMP 3:0B16   执行后:CS=0003H ,IP=0B16H,CPU将从00B46H处读取指令。
JMP段地址:偏移地址 指令的功能为:用指令中给出的段地址修改CS,偏移地址修改IP,想修改IP的内容,可以这么写JMP AX 指令执行前AX=1000H,CS=2000H,IP=0003H 指令执行后AX=1000H,CS=2000H,IP=1000H

jmp指令是无条件转移指令。
jcxz指令是有条件转移指令。

机器码                              汇编指令
B8 00 00                        mov ax,0000     ;ax=0000
05 23 01                        add ax,0123     ;ax=ax+0123
8B D8                           mov bx,ax       ;bx=ax
FF E3                           jmp bx          ;修改偏移地址=0123
机器码                              汇编指令
b8 20 4e                        mov ax,4E20H
05 16 14                        add ax,1416H
bb 00 20                        mov bx,2000h
01 d8                           add ax,bx
89 c3                           mov bx,ax
01 d8                           add ax,bx
b8 la 00                        mov ax,00LAH
bb26 00                         mov bx,0026H
00 d8                           add al,bl
00 dc                           add ah,bl
00 c7                           add bh,al
b4 00                           mov ah,0
00 d8                           add al,bl
04 9c                           add al 9ch


DS寄存器通常是用来存放访问数据的段地址。
代码如下
mov bx,10000h
mov ds,bx
mov al,[0]
上面的3条指令将10000H(1000:0)中的数据读到al中。
mov al,[0]     含义是将mov指令数据直接送入寄存器或将一个寄存器中的内容送入另一个寄存器中。“[...]”表示一个内存单元,“[...]”中的0表示内存单元的偏移地址。
字的传送
mov bx,1000h
mov ds,bx
mov ax,[0]    ;1000:0处的字型数据送入ax
mov [0],cx    ;cx中的16位数据送到1000:0处

PUSH指令为入栈

POP指令为出栈

代码如下
mov ax,0123h
push ax
mov bx,2266h
push bx
mov cx,1122h
push cx
pop ax
pop bx
pop cx
好了,在看看源程序

assume cs:codesg           ;定义codesg代码段和CPU中的段寄存器CS联系起来。
codesg segment             ;定义一个段,段的代码名称为"codesg"。
start: mov ax,0123h  
       mov bx,0456h
       add ax,bx
       add ax,ax
      
       mov ax,4c00h
       int 21h             ;程序返回AX,4C00H处
codesg ends               ;名称为"codesg"的段代码到这里结束。
end                       ;整个程序的结束标记

伪指令为  段名 segmnt
          :
          :
          :
          段名 ends
   segmnt和ends是一对成对使用的伪指令,这是在写可被编译器编译的汇编程序时,必须要用的一对伪指令。segment和ends的功能是定义一个段,segment说明一个段的开始,ends说明一个段的结束。
  end是一个汇编程序的结束标记,编译器在编译汇编程序的过程中,如果碰到了伪指令END,就结束对源程序的编译,如果程序写完了,要在结束处加上伪指令END。否则,编译器在编译程序时,无法知道程序在哪里结束。
  assume这是一条伪指令的含义为“假设”。
  段名称可以自定义的。
  在目前阶段,我们不必要去理解INT 21H指令含义,和为什么要在这条指令前面加上指令MOV AX,4C00H。我们只要知道,在程序的末尾使用这两条指令就可以实现程序返回。
  约定符号idata表示常量--在“[...]”里用一个常量0表示内存单元的偏移地址,以后可以用mov ax[idata]就可以代表 mov ax[1],mov ax[2],mov ax[3]等。mov bx,idata就可以代表mov bx,1  mov bx,等。
mov ax,[bx]功能是bx中存放的数据作为一个偏移地址EA,段地址:SA默认在ds中,将SA:EA处的数据送入ax中。可以这样表示:(ax)=((ds)*16+(bx));
思考后看分析
ssume cs:codesg   ;取段名
codesg segment    ;段开始
mov ax,2000e
mov ds,ax         ;DS=2000E
mov bx,1000h      ;以上,设置DS:BX指向2000:1000
mov ax,[bx]      ;(AX)=((DS)*16+(BX))
inc bx           ;BX加上1
inc bx           ;BX=1再加上1结果=1002H
mov [bx],ax      
inc bx
inc bx
mov [bx],ax
inc bx
mov [bx],al
inc bx     
mov [bx],al
codesg ends    ;段结束
end            ;程序结束
inc bx的含义就是BX中的内容加1
(AX)=(AX)*2可以这样写
mov ax,2
add ax,ax
(AX)=(AX)*2*2可以这样写
MOV AX,2
ADD AX,AX
ADD AX,AX
如果2^12怎么写?2^12=2*2*2*2*2*2*2*2*2*2*2*2
(ax)=(ax)*2*2*2*2*2*2*2*2*2*2*2*2
这里我们可以用loop来简化我们的程序
assume cs:code    ;命段名
code segment      ;段开始
     mov ax,2     ;AX=2   
     mov cx,11    ;CX=11
s:   add ax,ax    ;AX=AX
     loop s       ;如果S执行了10次后跳过,每LOOP S一次CX自动减1
     mov ax,4c00h ;返回
     int 21h
code ends         ;段结束
end               ;程序结束

执行loop s时,首先(CX)减1,然后若(CX)不为0时,向前转到S处执行ADD AX,AX。
从中可以体会如何用CX和LOOP S相配实现循环功能。
mov cx,循环次数
S:
循环执行的程序段
loop s

下来,讲解一下数据段信息~记住,汇编语言可分为数据段和代码段。
请看程序
assume cs:codesg    ;命段名
codesg segment      ;段开始
     dw 0123h,0456h,0789h,0abch,0defh,0fedh,0fedh,0987h
     dw 0,0,0,0,0,0,0      ;这个DW部分是数据段,它定义了8个字型数据,在程序加载后,将取得8个字的内存空间,存放这8个数据,我们在后面的程序中将这段空间当作线栈来使用!
start: mov ax,cs     ;AX=CS
       mov ss,ax     ;SS=AX
       mov sp,32    ;将设置栈顶SS:SP指向CS:32
       mov bx,0     ;BX清除为0
       mov cx,8     ;CX=8
S:     push cs:[bx] ;入栈(CS)=((SS)*16+(BX))
       add bx,2     ;BX+2
       loop s       ;以上将代码段0~16单元中的8个字型数据依次入栈
       mov bx,0     ;BX清除为0
       mov cx,8     ;CX=8
s0:    pop cs:[bx]  ;出栈(CS)=((SS)*16+(BX))
       add bx,2     ;BX+2
       loop s0      ;以上依次出栈8个字型数据到代码段0~16单元中
       mov ax,4c00h
       int 21h      ;返回
codesg ends         ;段结束
end start           ;指明程序的入口在start处
好了~看完了程序,大家也应该哪是数据段,哪是代码段了吧!

assume cs:codesg
codesg segment
      :
      :
    数据
      :
      :
start:
      :
      :
      代码

DD它和DB和DW一样!定义字节数据和字型数据。DD是定义双字型数据!
DUP是一个操作符,它是和DB,WD,DD等数据定义伪指令配合使用的,用来进行数据的重复。
比如 DB 3 DUP (0)
定义了3个字节,它们的值都是0,相当于DB 0,0,0
DB 3 DUP (0,1,2)
定义了3个字节,它们的值是0,1,2,0,1,2,0,1,2,相当于DB 0,1,2,0,1,2,0,1,2
DB 3 DUP (‘abc’'ABC')
定义了3个字节,它们的值是abcABCabcABCabcABC相当于DB 'abcABCabcABCabcABC'

下来再给大家一个例子
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0fedh,0987h
data enda
stack segent
dd 7 dup (0)
stack engment
code segment
start:  mov ax,stack
        mov ss,ax
        mov ax,data
        mov bx,0
        mov cx,8
s:      push [bx]
        add bx,2
        loop s
        mov bx,0
        mov cx,8
s0:     pop [bx]
        add bx,2
        loop s0
        mov ax,4c00b
        int 21b
code ends
end start
上面的程序是将数据,代码,栈放入不同的段里头!大家仔细的研究

and指令:逻辑与指令,按位进行与运算。
or指令:逻辑与指令,按位进行或运算。

ASCII码给出的数据是用'...'的方式指明

     在前面我们用[BX]的方式指明一个内存单元,我们可以用更灵活的方式来指明内存单元:[BX+IDATA]表示一个内存单元,它的偏移地址为(BX)+IDATA(BX中的数值加上IDATA)。

mov ax,[200+bx]

我们可以用数学方式来描述以上的指令:(ax)=((ds)*16+(bx)+200);
下来看看这个例子,是融合了上面的几个命令

assume cs:code,ds:data
data segment
db 'BaSiC'
db 'MinIX'
data enda
code segment
start:  mov ax,data
        mov ds,ax
        mov bx,0
        mov cx,5
s:      mov al, [bx]            ;定义一个字符串的字符
        and al,11011111b
        mov [bx],al
        mov al,[5+bx]           ;定义第2个字符串中的字符
        or al,00100000b
        mov [5+bx],al
        inc bx
        loop s
code ends
end start

我们看一下mov ax,[bx+si]的指令含义:
将一个内存单元的内容送入AX,这个内存单元的长度为2字节(字单元),存放一个字,偏依地址为BX中的数值加上SI中的数值,段地址在DS中。数学化描述:(ax)=((ds)*16(bx)+(si))
该指令也可以写成如下格式:
mov ax,[bx][si]
指令要处理的数据有多长?
mov word prt ds[0],1
inc word ptr [bx]
inc word ptr ds[0]
add word prt [bx],2

寻址方式的综合应用
关于DEC公司的一条记录(1982年)如下
公司名称:DEC
总裁姓名:Ken Olsen
排名: 137
收入: 40(40亿美元)
著名产品: PDP(小型机)
到了1988年DEC公司的信息有了如下变化
Ken Olsen 在富翁榜上的排名已升到38位
DEC的收入增加了70亿美元
该公司的著名产品已变为VAX系列计算机
好了,先用C语言写下这段程序

struct company
{
char cn[3];
char hn[9];
int pw;
int sr;
char cp[3];
};
struct company dec={"DEC","Ken Olsen"137,40,"PDP"};
/*定义一个公司记录的变量,内存中将存有一条公司的记录*/
main()
{
int i;
dec.pm=38;
dec.sr=dec.sr+70;
i=0;
dec.cp[1]='v';
i++;
dec.cp[i]='A';
i++;
dec.cp[i]='X'
return 0;
}

我们再用按照C语言的风格,用汇编语言写一下这个程序,注意和C语言相关语句的对比:

assume cs:seg,ds:data
data segment
db 'cn'
db 'hn'
db 'pw'
db 'cp'
data enda
seg segment
start:    mov ax,seg
          mov ds,ax
          mov bx,60h                           ;记录首址送BX
          mov worrd ptr [bx].0ch,38            ;排名字段改为38
                                               ;C语言:dec.pm=38;
          add worrd ptr [bx].0ch,70            ;收入字段增加70
                                               ;C语言:dec.sr=dec.sr+70;
                                               ;产品字段改为字符串‘VAX’
          mov si,0                             ;C语言:i=0;
          mov byte ptr [bx].10h[si],'V'        ;dec.cp[1]='v';
          inc si                               ;i++;
          mov byte ptr [bx].10h[si],'A'        ;dec.cp[i]='A';
          inc si                               ;i++;
          mov byte ptr [bx].10h[si],'X'        ;dec.cp[i]='X'
seg ends
end start

DIV是除法指令。

offset操作符,它的功能是取得标号的偏移地址。
看程序
assume cs:codesg
codesg segment
start: mov ax,cffset start   ;相当于MOV AX,0
  s:   mov ax,cffset s       ;相当于MOV AX,3
codesg ends
end start

ret指令用栈中的数据,修改IP的内容,从而实现远转移。
retf指令用栈中的数据,修好CS和IP的内容,从而实现远转移。
call指令不能实现短转移,除此之外,CALL指令实现转移的方法和JMP指令的原理相同。
mul是乘法指令
adc是带进位加法指令。
cmp是比较指令,CMP的功能相当于减法指令,只是不保存结果。
sdd指令是带借为减法指令。
pushf的功能是将标示寄存器的值压栈,而popf 是从栈中弹出数据,送入标志寄存器中。

看程序

assume cs:codesg
datasg segment
db "Beginner's All-purpose Symbolic Instructiom Code.",0
detasg ends
codesg segment
degin: mov ax,datasg
       mov ds,ax
       mov si,0
       call letterc
       mov ax,4c00h
       int 21h
letterc: :
         :
codesg ends
end begin

do0程序的主要任务是显示字符串。
int指令是调用任何一个中断的中断处理程序。
对端口的读写不能用MOV,PUSH,POP等内存读写命令。
端口的读写指令只有两条:IN和OUT,分别用于从端口读取数据和往端口写入数据。

Shl和SHR是逻辑移位指令。SHL是逻辑左移指令。SHR是逻辑右移位指令。


请看一些代码

; PECSUM (c)1998 By Virogen
; Blah, as you can see i whipped this shit up quick and i have no desire
; to waste more time on it, resulting in some ugly code<g>.
;
.386
locals
jumps
.model flat,STDCALL
extrn MapFileAndCheckSumA:PROC
extrn ExitProcess:PROC
extrn MessageBoxA:PROC
extrn GetCommandLineA:PROC
extrn CreateFileA:PROC
extrn MapViewOfFile:PROC
extrn CreateFileMappingA:PROC
extrn GetFileSize:PROC
extrn GetFileTime:PROC
extrn CloseHandle:PROC
extrn UnmapViewOfFile:PROC
extrn SetFileTime:PROC
extrn lstrcpy:PROC
extrn lstrcat:PROC
extrn IsBadReadPtr:PROC
include mywin.inc
org 0  
.data                                 
ver db '1.0'
caption db 'Virogen''s PE EXE/DLL CheckSum Util',0
nomatch db 0ah,0dh,'THE CHECKSUMS DO NOT MATCH!',0ah,0dh,'Store New Checksum in Header?',0
match db 0ah,0dh,'THE CHECKSUMS MATCH!',0
notpemsg db 'There was an error setting the checksum in the header.',0ah,0dh
          db 'This file is not a PE EXE/DLL or there was an error opening it.',0
cmdline db 'There was an error checksumming the specified file!'
        db 0ah,0dh,'Usage: PECSUM [file]',0
fname db 260 dup(0)
oldcsum dd 0
newcsum dd 0
fnameptr dd 0
fname_pre db     'File: ',0
text  db         'Header Chksum:',9
hcsumt db 9 dup(' ')
      db 0ah,0dh,'Real Chksum:',9
ncsumt db 9 dup(' ')
cr_lf db 0ah,0dh,0
errorf dd 0
handle dd 0
map_ptr dd 0
maphandle dd 0
creation dd 0,0
lastwrite dd 0,0
lastaccess dd 0,0
mboxtext db 512 dup(0)

.code           
start:   
    call GetCommandLineA
    or eax,eax
    jz _exit_bad_cmd_line
    xchg esi,eax
sl:
    cmp byte ptr [esi],0
    jz _exit_bad_cmd_line
    shl eax,8
    lodsb
    cmp eax,'.exe'
    jz esl
    cmp eax,'.EXE'
    jnz sl
esl:
    add esi,2
    mov fnameptr,esi

CsumStart:
    mov byte ptr mboxtext,0

    mov newcsum,0   
    push offset newcsum
    push offset oldcsum
    push fnameptr
    call MapFileAndCheckSumA
    cmp newcsum,0
    jz _exit_bad_cmd_line

    mov eax,oldcsum
    lea edi,hcsumt
    call HexWrite32
    lea edi,ncsumt
    mov eax,newcsum
    call HexWrite32

    push offset fname_pre
    push offset mboxtext
    call lstrcpy

    push fnameptr
    push offset mboxtext
    call lstrcat

    push offset cr_lf
    push offset mboxtext
    call lstrcat

    push offset text
    push offset mboxtext
    call lstrcat

    mov eax,newcsum
    cmp eax,oldcsum
    jz ok_b
    push offset nomatch
    push offset mboxtext
    call lstrcat
    push MB_ICONEXCLAMATION or MB_YESNO
    push offset caption
    push offset mboxtext
    push 0
    call MessageBoxA
    cmp eax,IDNO
    jz _exit
    mov esi,fnameptr
    call SetCsum
    cmp errorf,0
    jz CsumStart
    push MB_ICONEXCLAMATION
    push offset caption
    push offset notpemsg
    push 0
    call MessageBoxA
    mov eax,-1
    jmp _exit

ok_b:
    push offset match
    push offset mboxtext
    call lstrcat
    push MB_ICONINFORMATION
    push offset caption
    push offset mboxtext
    push 0
    call MessageBoxA

    xor eax,eax
    jmp _exit   

_exit_bad_cmd_line:

    push MB_ICONEXCLAMATION
    push offset caption
    push offset cmdline
    push 0
    call MessageBoxA
    mov eax,-1
_exit:
    push eax
    call ExitProcess


; entry esi->filename
SetCsum proc

    call OpenFile
    cmp eax,-1
    jnz open_ok
    inc errorf
    ret         
open_ok:
    mov handle,eax

    push offset creation
    push offset lastaccess
    push offset lastwrite
    push eax
    call GetFileTime                     

    call create_mapping                       ; create file mapping
    jc abort_
                                              ; eax->mapped file
    cmp word ptr [eax],'ZM'                  
    jnz abort_

    mov edi,[eax+3Ch]                       
    add edi,eax                              ; edi->pe hdr
    push 2
    push edi
    call IsBadReadPtr                        ; make sure we can read it if MZ/NE exe!
    or eax,eax
    jnz abort_
    cmp word ptr [edi],'EP'                  ; PE?
    jnz abort_
    add edi,checksum                         ; edi->checksum
    mov eax,newcsum
    stosd
    jmp wasgood

abort_:
    inc errorf
wasgood:
    push map_ptr
    call UnmapViewOfFile
    push maphandle
    call CloseHandle

    push offset creation
    push offset lastaccess
    push offset lastwrite
    push handle
    call SetFileTime              ; restore orginal file time

    push handle
    call CloseHandle

    ret                                 
SetCsum endp

; create_mapping - create file mapping of [handle]

create_mapping proc
    push 0              ; no map name
    push 0              ; default size
    push 0              ; high size
    push PAGE_READWRITE ; read&write
    push 0            
    push handle
    call CreateFileMappingA
    call test_error
    jc create_abort
    mov maphandle,eax

    push 0               ; # of bytes, 0= map entire file
    push 0               ; file offset low
    push 0               ; file offset high
    push FILE_MAP_WRITE  ; access flags - read&write
    push eax             ; handle
    call MapViewOfFile
    call test_error
    jc create_abort
    mov map_ptr,eax
   
create_abort:
    ret
create_mapping endp

; test_error - test API for an error return
;  entry: eax=API return
;  returns: carry if error
;
test_error proc
   cmp eax,-1
   jz api_err
   or eax,eax
   jz api_err
   clc
   ret
api_err:
   stc
   ret
test_error endp

OpenFile proc
      push 0
      push 20h                          ; attribute normal
      push 3                            ; 3=open existing file
      push 0
      push 0
      push 0C0000000h                   ; permissions
      push esi
      call CreateFileA
      ret
OpenFile endp


; below is your usual hex->ascii conversion - ripped from borland, why waste
; time coding stupid shit twice
HexWrite8 proc
        mov     ah, al
        and     al, 0fh
        shr     ah, 4
                                ; ah has MSD
                                ; al has LSD
        or      ax, 3030h
        xchg    al, ah
        cmp     ah, 39h
        ja      @@4
@@1:
        cmp     al, 39h
        ja      @@3
@@2:
        stosw
        ret
@@3:
        sub     al, 30h
        add     al, 'A' - 10
        jmp     @@2
@@4:
        sub     ah, 30h
        add     ah, 'A' - 10
        jmp     @@1
HexWrite8 endp
HexWrite16 proc
        push    ax
        xchg    al,ah
        call    HexWrite8
        pop     ax
        call    HexWrite8
        ret
HexWrite16 endp
HexWrite32 proc
        push    eax
        shr     eax, 16
        call    HexWrite16
        pop     eax
        call    HexWrite16
        ret
HexWrite32 endp

end start
ends


------------------------------------------------------------
大家来分析这两个程序-最好能把它用C语言写出来!呵呵




include mywin.inc      

ID_OFF          equ     0ch    ; offset of our marker in PE
DECRYPTOR_SIZE  equ     (offset decryptor_code_end-offset decryptor_code)  ;
VIRTUAL_SIZE    equ     DECRYPTOR_SIZE
MAX_OBJS        equ     6               ; maximum objects we can handle
                                        ; by increasing this you are increasing the size
                                        ; of the table in decryptor by MAX_OBJS*8.

.586p
locals
jumps
.model flat,STDCALL

extrn   ExitProcess:PROC         
extrn   CreateFileA:PROC         
extrn   CloseHandle:PROC         
extrn   ReadFile:PROC            
extrn   WriteFile:PROC           
extrn   SetFilePointer:PROC      
extrn   MapViewOfFile:PROC      
extrn   CreateFileMappingA:PROC  
extrn   UnmapViewOfFile:PROC     
extrn   SetEndOfFile:PROC        
extrn   SetFilePointer:PROC      
extrn   GetFileAttributesA:PROC  
extrn   SetFileAttributesA:PROC  
extrn   GetFileSize:PROC         
extrn   GetTickCount:PROC        
extrn   GetFileSize:PROC         
extrn   GetFileTime:PROC         
extrn   SetFileTime:PROC         
extrn   CheckSumMappedFile:PROC  
extrn   MessageBoxA:PROC         
extrn   GetCommandLineA:PROC     
extrn   lstrcat:PROC            
extrn   IsBadReadPtr:PROC        
extrn   WriteConsoleA:PROC
extrn   GetStdHandle:PROC
extrn   ReadConsoleA:PROC



org 0  
.data                                   ; data object

; conditional compile
;console_app equ 1


;
cr      equ 0dh
lf      equ 0ah
tab     equ 9
hline   equ 196
marker  equ 90909090h

cr_lf_tab       db cr,lf,tab,tab,0
init_txt        db 50 dup(hline),cr,lf
caption         db 'Virogen''s PE Encryptor v0.75, (c)1998 Virogen[NOP]'
ifndef console_app
                db 0
endif           
ifdef console_app
                db cr,lf,'                       email:vgen@hotmail.com',cr,lf
                db 50 dup(hline),cr,lf,0        
endif           
badcmd_txt      db 'Invalid command line!',cr,lf,'Usage: VGCRYPT filename',cr,lf,0
success_txt:
ifdef console_app
                db cr,lf
endif           
                db 'Successfully encrypted!'
ifdef console_app
                db 0
endif                           
file_txt        db cr,lf,' Installed on file: ',tab,0
                db 400 dup (0)          ; plenty of space
obj_txt         db cr,lf,' Installed in object: ',tab,0
                db 9 dup(0)
eobj_txt        db cr,lf,' Encrypted objects: ',0
                db (MAX_OBJS*8)+1 dup(0)        
hole_txt        db cr,lf,'VGCrypt installed in alignment hole, with no phsyical size increase!',0
already_txt:
ifdef console_app
                db cr,lf,'File appears to already be encrypted. Encrypting again.',0
endif
                db 'File appears to already be encrypted. Do you wish to encrypt again?',0
append_question db 'Could not locate any "caves" to install into!',cr,lf,'Click YES'
                db ' to create new object',cr,lf,'Click NO to append to last object.',0
error_txt:
ifdef console_app
                db cr,lf,' '
endif           
                db 'There was an error encrypting the file!',cr,lf
ifdef console_app
                db 0
endif           
fname_txt       db 'Specified file: ',0
                db 260 dup(0)
nohole_txt      db cr,lf,'No available "caves" to install into, forced to increase physical size.',0
doing_obj_txt   db cr,lf,'Encrypting object: ',tab,0
skip_obj_txt    db cr,lf,'Skipping object: ',tab,0
found_hole_obj  db cr,lf,'Found hole in object: ',tab,0
done_txt        db '..Done',0
creation        dd 0,0                     ; our file time structures
lastaccess      dd 0,0
lastwrite       dd 0,0
oldchksum       dd 0
fsize           dd 0
map_ptr         dd 0
oldattrib       dd 0                        ; stored file attribs
fnameptr        dd 0                        ; ptr to file name we're inf
ptrpeheader     dd 0
objPsize        dd 0
maphandle       dd 0
handle          dd 0
objtblVA        dd 0
objptr          dd 0
lastobjimageoff dd 0
originalpsize   dd 0
originalvsize   dd 0
error           db -1                       ;
importtbl        dd 0
exporttbl       dd 0
byteswrote      dd 0
hstdo           dd 0
hstdi           dd 0
ynbuf           db 0
use_hole        db 0
holeptr                dd 0


bad_otbl:                       ; this is the list of bad objs - did I miss any?
   dd 'rsr.'                            ; rsrc
   dd 'ler.'                            ; relo
   dd 'ade.'                            ; edata
   dd 'ete.'                            ; etext
   dd 'adi.'                            ; idata
   dd 'adr.'                            ; rdata
   dd 'slt.'                            ; tls
   dd 0                                 

;---- decryptor code installed into file ----
;
;
;
;*** CLOSED SOURCE, for security
;
;
;
; --- end of decryptor code ---


; --- start of VGCrypt ---
.code                                   ; code object - change flags to rwx
start:
ifdef console_app      
        call    GetSHandle
        lea     ebx,init_txt
        call    WriteString
endif   
        call    GetCommandLineA                 ; retrieve command line
        or      eax,eax                        
        jz      _exit_bad_cmd_line              ; if none then abort /w msg
        xchg    esi,eax
sl:
        cmp     byte ptr [esi],0                ; if first byte is NULL then something way wrong
        jz      _exit_bad_cmd_line   
        shl     eax,8                           ; rotate 1 byte in eax, for loop.. eax running load
        lodsb                           ; get next byte in al
        cmp     eax,'rypt'                      ; end of our proggie name?
        jnz     not_eoc
        cmp     byte ptr [esi],'.'
        jnz     esl
not_eoc:   
        cmp     eax,'.exe'                      ; .exe end of our proggie name?
        jz      esl
        cmp     eax,'.EXE'                      ; .EXE end of our proggie name?
        jnz     sl
esl:
        lodsb
        cmp     al,' '
        jz      esl            
        cmp     al,'"'
        jz      esl
        dec     esi
esl2:   
        cmp     byte ptr [esi],0                ; if first char in parameter 1 is NULL then we fuq
        jz      _exit_bad_cmd_line   
        
ifndef console_app      
        push    esi                             
        push    offset success_txt              
        call    lstrcat                 ; append filename to success message
endif   
ifdef console_app      
        push    esi
        push    offset fname_txt
        call    lstrcat
        lea     ebx,fname_txt
        call    WriteString
endif   
        mov     fnameptr,esi                    ; set fnameptr->filename
        call    EncryptFile                     ; go encrypt        
        ;cmp     error,-4
        ;jz      _exit
        cmp     error,-1                        ; error?
        jz      _exit_error                     ; if so go display error message
        
ifndef console_app      
        push    offset obj_txt
        push    offset success_txt
        call    lstrcat                 ; append object name we inserted or appending to
        
        push    offset eobj_txt
        push    offset success_txt
        call    lstrcat                 ; append objects we encrypted   
        
        cmp     use_hole,1
        jnz     no_hole_msg   
        push    offset hole_txt
        jmp     app_success
no_hole_msg:   
        push    offset nohole_txt   
app_success:
        push    offset success_txt
        call    lstrcat

        push    0                 
        push    offset caption   
        push    offset success_txt
        push    0                 
        call    MessageBoxA      
endif
ifdef console_app
        lea     ebx,success_txt
        call    WriteString     
endif   
                                 
        xor     eax,eax           
        jmp     _exit            

_exit_error:
ifndef console_app
        push    fnameptr           
        push    offset error_txt   
        call    lstrcat            

        push    MB_ICONEXCLAMATION
        push    offset caption     
        push    offset error_txt   
        push    0                  
        call    MessageBoxA        
endif
ifdef console_app
        lea     ebx,error_txt
        call    WriteString     
endif   
        mov     eax,2              
        jmp     _exit              

_exit_bad_cmd_line:
ifndef console_app      
        push    MB_ICONEXCLAMATION
        push    offset caption   
        push    offset badcmd_txt
        push    0                 
        call    MessageBoxA      
endif   
ifdef console_app
        lea     ebx,badcmd_txt
        call    WriteString
endif   
        xor     eax,eax           
        inc     eax               

_exit:   
        call    ExitProcess,eax

;-----------------------------------------------
; encrypt file - call with fnameptr set
;
EncryptFile proc

        mov     eax,fnameptr
        push    eax                                                               
        call    GetFileAttributesA                  ; get file attributes         
        mov     oldattrib,eax                                                      
                                                                                   
        cmp     eax,-1                               ; if error then maybe shared  
        jnz     not_shared                                                         
        ret                                          ; can't encrypt it            

not_shared:
        push    20h                                 ; +A
        mov     eax,fnameptr
        push    eax
        call    SetFileAttributesA                  ; clear 'da attribs
        
        mov     esi,fnameptr
        call    OpenFile
        call    test_error
        jnc     open_ok
        ret         
open_ok:
        mov     handle,eax                                                         
                                                                                    
        push    offset creation                                                     
        push    offset lastaccess                                                   
        push    offset lastwrite                                                   
        push    eax                                                                 
        call    GetFileTime                           ; grab the file time         
                                                                                    
        xor     ecx,ecx                                ; only map size of file      
        call    create_mapping                        ; create file mapping         
        jc      abort_encrypt                                                      
                                               ; eax->mapped file               
        cmp     word ptr [eax],'ZM'                    ; is EXE?                    
        jnz     abort_encrypt                                                      
                                                                                    
        call    GetPEHeader                           ; load esi->PE Header         
                                                                                    
        push    2                                                                  
        push    esi                                    ; test ptr for read acces
        call    IsBadReadPtr                           ; was ptr any good?      
        or      eax,eax                                                            
        jnz     abort_encrypt                                                      
                                                                                    
        cmp     word ptr [esi],'EP'                    ; PE?                        
        jnz     abort_encrypt                                                      
        cmp     dword ptr [esi+ID_OFF],marker          ; marker?                    
        jnz     not_encrypted                          ; if yes, already processed  
ifndef console_app
        push    MB_ICONHAND or MB_YESNO                                             
        push    offset caption                                                      
        push    offset already_txt                                                  
        push    0                                                                  
        call    MessageBoxA                                                         
        cmp     eax,IDYES                                                           
        jnz     abort_encrypt                                                      
endif
ifdef console_app      
        lea     ebx,already_txt
        call    WriteString
endif   
not_encrypted:
        call    unmap                                 ; unmap file                                   
                                                                                                     
        mov     ecx,1000h                              ; give us room to add to the file, if needed      
        call    create_mapping                        ; map file again                              
        jc      abort_encrypt                                                                        
                                                                                                     
        call    GetPEHeader                           ; load esi -> pe header                        
                                                                                                     
        call    GetTickCount                          ; get tick count                              
        mov     key,eax                                ; save for encryption key                     
                                                                                                     
        mov     dword ptr [esi+ID_OFF],marker          ; save marker                                 
                                                                                                     
        ;mov     eax,[esi+imagebase]                                                                  
        ;mov     svd_imagebase,eax                      ; save the image base                             
                                                                                                     
        mov     eax,[esi+datadir]                       
        mov     importtbl,eax
        mov     eax,[esi+edatadir]
        mov     exporttbl,eax
        
        xor     eax,eax                                                                              
        mov     ax, word ptr [esi+NtHeaderSize]        ; get header size                             
        add     eax,18h                                ; object table is here                        
        add     eax,esi                                                                              
        mov     objptr,eax                                                                           
        
        ; Let's check for existance of cave after object table in PE header
        
        
        ;mov     edi,[eax+objpoff]
        ;add     edi,map_ptr
        ;push    esi
        ;int 3
        ;push    eax
        ;xor     eax,eax
        ;mov     ax,[esi+numObj]        
        ;inc     eax
        ;mov     ecx,40
        ;xor     edx,edx        
        ;mul     ecx
        ;pop     esi
        ;add     esi,eax        
        ;cmp     esi,edi
        ;jge     no_room_in_hdr
        ;xchg    edi,esi
        ;mov     cx,DECRYPTOR_SIZE
        ;xor     eax,eax
        ;push    edi
        ;repz    scasb
        ;pop     edi
        ;or      cx,cx
        ;jnz         no_room_in_hdr               
        ;pop     esi
        ;push    esi
        ;mov     eax,objptr
         ;mov     ecx,[esi+filealign]
        ;mov     ebx,[eax+objpoff]
        ;sub     ebx,edi
        ;sub     ebx,map_ptr
        ;mov     [eax+objpoff],ebx
                                
        ;mov     holeptr,edi               
        ;mov     use_hole,1
no_room_in_hdr:        
                     
        ; Here we will traverse through the object table, encrypting objects
        ; which can be encrypted and also searching for a 'cave' big enough
        ; to hold us.        
        ;pop         esi
        push         esi               
        mov     eax,objptr
        lea     edi,otable                                                         
        xor     ecx,ecx                                                            
        mov     cx,[esi+numObj]                          ; get number of objects   
;        dec     cx                                                                 
otbl_loop:
        cmp     edi,offset otable_end-8                  ; filled up table?
        jz      next_obj   
        call    test_obj                                 ; see if good obj name
        jc      next_obj
        pushad                              
        cmp     use_hole,1                               ; already found hole?
        jz      no_hole_here
        mov     edx,[eax+objpsize]                       ; get obj psize
        mov     ecx,[eax+objvsize]                       ; get obj vsize        
        cmp     ecx,edx                                  ; any hole here?
        jge     no_hole_here
        sub     edx,ecx                                  ; get size of hole
        cmp     edx,DECRYPTOR_SIZE                       ; big enough for us?
        jl      no_hole_here   
        mov     objptr,eax                               ; save ptr obj rec
        mov     use_hole,1                               ; set flag                                             
        mov     ecx,[eax+objvsize]                       ; encrypt vsize
        mov     [edi+4],ecx                              ; if vsize<psize
ifdef console_app
        lea     ebx,found_hole_obj
        call    WriteString
        mov     ebx,eax
        call    WriteString
endif   
no_hole_here:               
        push    offset cr_lf_tab
        push    offset eobj_txt
        call    lstrcat
        popad
        pushad
        push    eax
        push    offset eobj_txt
        call    lstrcat
        popad
               
        push    eax ecx
        mov     ebx,eax[objflags]                        ; get obj flags
        or      ebx,oflag_write                           ; OR in writable flag
        mov     eax[objflags],ebx                        ; save new object flags
        mov     ebx,eax[objrva]                          ; get the object's rva        
        mov     [edi],ebx                                ; save it
        mov     ecx,[edi+4]                              ; see if we set above
        or      ecx,ecx                                  ; if so then we are
        jnz     already_vsize                            ; gonna use vsize
        mov     ecx,eax[objpsize]                        ; get the object's physical size   
        mov     [edi+4],ecx               
already_vsize:                                                                          
ifdef console_app      
        lea     ebx,doing_obj_txt
        call    WriteString     
        mov     ebx,eax
        call    WriteString
endif   
        add     edi,8                                    ; increment to next table member      
        push    edi                                                                           
        mov     esi,eax[objpoff]                         ; esi->object physical offset         
        add     esi,map_ptr                                                                     
        mov     edi,esi                                                                        
        call    encrypt_object                           ; encrypt the object                  
        pop     edi ecx eax                                                                             
ifdef console_app      
        lea     ebx,done_txt
        call    WriteString     
endif   
ifdef console_app      
        jmp     next_did
endif   
next_obj:   
ifdef console_app
        lea     ebx,skip_obj_txt
        call    WriteString     
        mov     ebx,eax
        call    WriteString
endif   
next_did:
        add     eax,40                                   ; increment to next object record   
        loop    otbl_loop
done_otbl:   
        pop     esi                                       ; restore ptr pe hdr   
                                                                              
        cmp     use_hole,1                                                   
        jz      found_hole                                                      
        
        sub     eax,40
        
; make sure nuff room to add another object before we ask if
; the user wants to. dunno if this is best way to do it, but I
; just scan to make sure the next 40 bytes (1 object record) are
; all 0.
        
        pushad        
        mov     edi,eax
        add     edi,40
        mov         ecx,40
        xor        eax,eax
        repz    scasb
        or      ecx,ecx
        jnz         go_append        
        
        push    MB_ICONQUESTION or MB_YESNO
        push    offset caption
        push    offset append_question
        push    0
        call    MessageBoxA            
        cmp     eax,IDNO
        jz      go_append      
        ;cmp     eax,IDCANCEL
        ;jnz     go_create
        ;popad
        ;mov     error,-4
        ;jmp     abort_encrypt
go_create:        
; user selected to create new object
        popad                                   
        mov     ecx,[eax+objrva]                ; get last object rva
        add     ecx,[eax+objvsize]                ; +=last object virtual size
        push    eax                                ; save obj record ptr
        xchg    ecx,eax                                ; eax=last object virtual end
        mov     ecx,[esi+objalign]                ; ecx=object alignment
        call    align_fix                        ; go align da shiznit
        xchg    ecx,eax                                ; ecx=next object's rva
        pop     eax                               ; restore obj record ptr
        mov     edx,[eax+objpoff]                ; edx=last object physical offset
        add     edx,[eax+objpsize]              ; edx+=last object psize=obj pend
        add     eax,40                                ; goto next object in table (new)
        mov     dword ptr [eax],'cgv.'                ; set object name to .vgc
        mov     dword ptr [eax+objpsize],0        ; set psize to 0, updated later
        mov     dword ptr [eax+objvsize],0        ; set vsize to 0, update later
        mov     [eax+objrva],ecx                ; set rva of object
        mov     [eax+objpoff],edx               ; set physical offset of obj
        inc     word ptr [esi+numObj]                  ; increment number of objects
        jmp     after_apop
        
go_append:
        popad
after_apop:     
        mov     objptr,eax                              ; objptr->last obj      
found_hole:
        mov     edi,objptr                              ; objptr->obj /w hole

        cmp         holeptr,0
        jnz     go_pe_hdr_hole
       
        push    edi                                                                  
        push    offset obj_txt                          ; strcat object name
        call    lstrcat                         ; for display               
                                                                                 
        mov     eax,[edi+objpoff]                       ; get object physical off
        mov     lastobjimageoff,eax                     ; save it               
        mov     ecx,[edi+objpsize]                      ; get object physical size   
        mov     originalpsize,ecx                       ; save it 4 later        
                                                                                 
        mov     eax,[edi+objvsize]                      ; get object virtual size
        mov     originalvsize,eax                       ; save it                    
        cmp     use_hole,1                                                      
        jz      psize_less_vsize                                                
        cmp     eax,ecx                                                         
        jge     psize_less_vsize                        ; padded space for alignment?
        mov     eax,ecx                                 ; set vsize to psize     
psize_less_vsize:
        add         eax,VIRTUAL_SIZE                        ; add our virtual size
        mov         dword ptr [edi+objvsize],eax            ; save new virtual size
        
        cmp     use_hole,1                              ; if using hole then add virtual size
        jz      in_hole_no_psize                                                         
        mov     eax,originalpsize                       ; get physical size of object                                               
        add     eax,DECRYPTOR_SIZE                      ; adjust physical size of object
        mov     ecx,[esi+filealign]                                                      
        call    align_fix                               ; on file alignment              
        mov     [edi+objpsize],eax                                                               
        ; now we must CORRECTLY calculate the new image size. This was the
        ; bug under winNT appendation.   
        mov     ecx,dword ptr [esi+objalign]            ; get object alignment
        mov     eax,dword ptr [edi+objvsize]            ; add virtual size
        add     eax,dword ptr [edi+objrva]              ; +last object rva
        call    align_fix                              ; set on obj alignment
        mov     dword ptr [esi+imagesize],eax           ; save new imagesize  
            
        
        mov     eax,originalpsize
        jmp     not_in_hole_psize_entry
in_hole_no_psize:   
   
        mov     eax,originalvsize
not_in_hole_psize_entry:

        mov     [edi+objflags],0E0000060h               ; set object flags r/w/x/init data
        add     eax,[edi+objrva]                        ; add last object's RVA     
                                                        ; eax now RVA of decryptor code
        jmp     new_entry
go_pe_hdr_hole:
        mov     eax,holeptr
        sub     eax,map_ptr
new_entry:   
        mov     ebx,[esi+entrypointRVA]                  ; get original entry         
        mov     [esi+entrypointRVA],eax                  ; put our RVA as entry      
        mov     newep,eax                                ; save it for decryptor too
                                                                                             
        mov     [host_eip],ebx                           ; save it                    
        
        push    esi
        
        ; CLOSED SOURCE
               
        mov     edi,map_ptr
        cmp     holeptr,0
        jz      not_hdr_hole
        mov     edi,holeptr
        jmp     copy_to_hdr
not_hdr_hole:        
        cmp     use_hole,1                               ; if in hole then install at end of vsize
        jz      endvsize
        add     edi,originalpsize                        ; add original physical size
        jmp     go_copy
endvsize:        
        add     edi,originalvsize                        ; add original virtual size
go_copy:   
        add     edi,lastobjimageoff                      ; add object physical offset  
copy_to_hdr:        
        lea     esi,decryptor_code                       ; esi->decryptor code         
        mov     ecx,DECRYPTOR_SIZE                                                     
        rep     movsb                                                                  
                                                                                       
        pop     esi                                     ; restore ptr pe hdr               
                                                                                       
        cmp     use_hole,1                               ; did we find hole to install in?
        jnz     go_pad_fixup_size                        ; if no then we better pad
        mov     ecx,fsize                                ; else new filesize=old filesize  
        jmp     go_fixup_size                                                         
        
go_pad_fixup_size:            
        mov     ecx,[esi+filealign]                     ; calculate amt                        
        sub     ecx,DECRYPTOR_SIZE                       ; of padding needed                    
        xor     eax,eax                                                                     
        rep     stosb                                    ; pad up object to alignment      
                                                                                       
        mov     eax,map_ptr                              ; eax->beginning of mapped file        
        sub     edi,eax                                  ; get difference from current ptr      
        mov     ecx,edi                                  ; save file size               
go_fixup_size:   
        push    ecx                                     ; ecx=real file size            
                                                                                         
        call    unmap                                   ; unmap file                     
                                                                                         
        pop     ecx                                                                     
        push    FILE_BEGIN                              ; from file begin               
        push    0                                 &nb