


汇编基础教程
汇编语言基础
要看懂汇编基础,得学会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
&nb