| 
				 一般程序员开发的软件并不希望别人随便使用,为了防止别人的非法使用,一般是通过加密的方法对软件进行保护,口令加密只是加密方式中的一种。虽然加密的方法对解密高手而言,并不能百分之百的保护,但对一般的使用者来说,加密保护软件的方法,仍是有效的。 Win32环境下PE格式的文件形式有EXE、DLL、OCX、SCR等,其中常用的可执行文件是EXE文件,对PE格式的EXE文件进行口令加密,就是在目标EXE文件上附加一段代码,通过附加代码中的对话框获取口令,并与附加代码里留存的口令进行比较,当口令一致时,表示用户是合法的,否则是非法用户。 本口令加密软件分为三个模块。模块1的功能是生成对话框,选择目标文件,输入口令及校核口令的正确性;模块2的功能是判断目标文件是否是PE格式的EXE文件,是否已口令加密,留存口令,修改目标文件头里的有关结构中的相关字段,并把加密代码附在目标文件上;模块3的功能是重定位,搜索加密代码中所要用到的API函数的地址,以便使用这些API函数生成对话框,并由对话框接收口令,校核口令的正确性,口令正确运行目标文件,口令错误则退出。由于模块1较简单,本文主要说明模块2和模块3的功能与作用。 一、在目标文件上附加口令加密代码 在对PE格式的EXE文件上附加代码的的方法常用的有三种方法。第一种方法是将附加代码插入在目标文件的代码节或其他节(如数据节)的空隙中,一般文件中节对齐在文件中的最大长度是512B(200H),如ML汇编器生成的文件默认文件对齐参数为512B,而代码至少要占用1B,所以最大空隙长度不超过511B。但也有一部分文件,如VC++编译器生成的文件其默认文件中节对齐的最大长度为4KB(1000H),因此,即使是加密代码较长,只要小于4KB,也完全可以插入其节中空隙。第二种方法是将附加代码分段插入文件中的不同节的空隙中,这种方法的优点是不会增加文件的长度,CIH病毒就是采用此法,但要求文件头部有足够的空隙来构建节表等。第三种方法是把附加代码附在目标文件的尾部,构成一个新代码节,这种方法的优点是附加代码的长度不受限制,但文件的长度会增加,另外,要求文件头有一定的空隙来构建节表。本文采用第一种和第三种方法,目的是提高加密的成功率。 要对PE文件进行口令加密,首先要对PE文件的结构有所了解,下图就是PE文件的基本结构,从这个PE文件的结构中,可以看到PE文件是由DOS头、PE文件头、节表和节等组成,每一个节表对应一个节,节表的排列顺序,也就是节的排列顺序。PE装载器就是根据PE文件头结构和节表结构中的相应字段将有关的节装入内存中,当然有些节如重定位节(.reloc节)是不必装入内存的。将加密代码附在目标PE文件后面,就是将加密代码做成一个代码节附在PE文件的后面,为了使PE装载器正确地加载附加的加密代码节,必须构建一个新节表使其指向加密代码节,而新添加的节表须位于原PE文件节表的末尾,这就要求文件头中的空隙要≥40B(因为节表结构的长度为28H),满足这个条件才能建立一个新的节表。而将加密代码插入节的空隙中,不用构建新节表。 
 
  
                                   图 PE文件的基本结构 
 在口令加密中我们要用到文件头中的以下结构及相应字段: 1.DOS头结构 IMAGE_DOS_HEADER  STRUCT …… e_magic   WORD  ?                      ;DOS头标志,“MZ” e_lfanew  DWORD  ?                     ;指向PE文件头(或文件头偏移003ch)       …… IMAGE_DOS_HEADER  ENDS 2.PE文件头(NT映像头)结构 IMAGE_NT_HEADERS   STRUCT Signature   DWORD   ?                    ;PE文件头标志,“PE00” FileHeader  IMAGE_FILE_HEADER < > OptionalHeader  IMAGE_OPTIONAL_HEADER32 < > IMAGE_NT_HEADERS   ENDS 3.映像文件头(FileHeader)结构 IMAGE_FILE_HEADER  STRUCT …… NumberOfSections  WORD  ?                ;节表(节)的数量 …… IMAGE_FILE_HEADER  ENDS 4.可选映像头(OptionalHeader)结构 IMAGE_OPTIONAL_HEADER32  STRUCT …… SizeOfCode           DWORD  ?    ;所有含代码的节的总长度 AddressOfEntryPoint   DWORD   ?            ;程序开始执行的入口地址RVA SectionAlignment      DWORD   ?            ;内存中节对齐的粒度 FileAlignment    DWORD   ?            ;文件中节对齐的粒度 SizeOfImage          DWORD   ?            ;内存中整个PE文件映像的大小 SizeOfHeaders         DWORD   ?            ;DOS头+PE文件头+节表的大小 DataDirectory  IMAGE_DATA_DIRECTORY 16 dup(<>)     ;数据目录结构数组 …… IMAGE_OPTIONAL_HEADER32  ENDS 5.节表结构 IMAGE_SECTION_HEADER  STRUCT Name1  db  IMAGE_SIZEOF_SHORT_NAME  dup(?)   ;节的名称,长度8字节 union Misc …… VirtualSize         dd  ?      ;节的实际大小 ends VirtualAddress      dd  ?      ;节装入内存后的偏移地址RVA SizeOfRawData     dd  ?      ;节在文件中按FileAlignment对齐后的大小 PointerToRawData   dd  ?      ;节在文件中的偏移地址 Characteristics      dd  ?      ;节的属性 IMAGE_SECTION_HEADER  ENDS     本口令加密算法采用将代码插入节中优先,这样可保证文件的长度不变;当节空隙不够时,再采用构建一新代码节。在目标PE文件中附加口令加密代码的主要过程如下: (1)判断是否是PE格式的EXE文件,不是则退出; (1)判断该PE格式EXE文件是否已加密,加密标志设为“zz”,已加密则退出; (3)循环检查节的空隙是否足够,如空隙足够则将加密代码插入对应节空隙中,修改对应节表中的相应字段VirtualSize、Characteristics,修改文件的入口地址AddressOfEntryPoint,指向加密代码的开始执行点,将修改后的文件头写入目标PE文件的头部,将返回地址和口令写入加密代码中,加密完成后退出。 (4)当节的空隙不够时,判断文件头是否有足够的空隙容纳新的节表(节表长度28H),不足退出,放弃口令加密; (5)有新的节表空隙,修改NT映像头中的相关字段; 由于新建了一个节(节表),所以要节数+1,即NumberOfSections+1,并修改SizeOfCode 和SizeOfImage;另外,要修改文件的入口地址AddressOfEntryPoint,使其指向加密代码的开始执行点; (6)在文件头中的空隙处建立一新节表,并为新节表的相关字段赋值,使其指向新建的加密代码节; 有Name1、VirtualSize、VirtualAddress、SizeOfRawData、PointerToRawData、Characteristics。 其中新代码节的VirtualAddress=末节的VirtualAddress+末节的VirtualSize并按SectionAlignment对齐,新代码节的SizeOfRawData=加密代码的长度按FileAlignment对齐,新代码节的PointerToRawData=末节的PointerToRawData+末节的SizeOfRawData; (7)将修改后的文件头写入目标PE文件的头部; (8)将口令加密代码作为一新建代码节附加在目标PE文件的尾部; (9)将返回地址写入加密代码中; (10)将口令写入加密代码中。 下面是附加口令加密代码的主要过程源程序,为了方便阅读加了必要的注释;另一点要注意的是PE文件的头部在磁盘文件里和在内存映射中是完全一致的,所以我们另外申请了一内存块把它装在内存中来修改又快又方便,然后将其重新写入目标文件的头部来完成对文件头的修改,实现代码如下: handlePeFile    proc       ;定义过程所要用的局部变量         local @hFile,@hMapFile,@lpMemory,@lpAlloc,@dwRet,@SizeOfHeader         local @dwEntry,@dwPE_Header_off,@FileAlign,@SectionAlign,@SectionNum         local @NewSection_off,@AppCodeSize,@AddCodeVirt,@AddCodeFile             pushad  ;创建处理异常的SHE结构         assume  fs:nothing   push ebp   push offset _ErrFormat   push offset _Handler   push fs:[0]   mov fs:[0],esp ;取目标PE文件的扩展名,判断是否是.exe invoke  lstrlen,addr szFileName lea ecx,szFileName mov edx,dword ptr [ecx+eax-4] or  edx,20202020h    ;将大写字符转换为小写 .if edx != 'exe.'     ;不是.exe则退出     invoke MessageBox,NULL,addr szTexttip,addr szCaptionTip,MB_OK     jmp _Ret .endif ;以可读、可写方式打开已存在的目标PE文件,如打开只读文件或不存在的文件会报错 invoke CreateFile,addr szFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or \    FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL .if eax != INVALID_HANDLE_VALUE  mov @hFile,eax    ;创建内存映射对象       invoke CreateFileMapping,@hFile,NULL,PAGE_READWRITE,0,0,NULL     .if eax    mov @hMapFile,eax    ;创建内存映射文件    invoke MapViewOfFile,eax,FILE_MAP_READ or FILE_MAP_WRITE,0,0,0             .if eax    mov @lpMemory,eax             mov ebx,eax             assume ebx :ptr IMAGE_DOS_HEADER             .if word ptr [ebx] !="ZM"          ;判断DOS头标记,‘MZ’             invoke MessageBox,NULL,addr szError,addr szCaptionTip,MB_OK             jmp _ErrFormat       ;不是PE格式文件,退出             .endif             mov eax,[ebx].e_lfanew             mov @dwPE_Header_off,eax             add ebx,[ebx].e_lfanew             ;ebx-->指向PE文件头             assume ebx :ptr IMAGE_NT_HEADERS             .if word ptr [ebx]!='EP'   ;判断PE文件头标记,‘PE’             invoke MessageBox,NULL,addr szError,addr szCaptionTip,MB_OK             jmp _ErrFormat      ; 不是PE格式文件,退出             .endif    ;‘zz’已加密标志,判断是否已加密,用于防止重复加密             .if word ptr [ebx+1ah] == 'zz'               invoke MessageBox,NULL,addr szCodeText,addr szCaptionTip,MB_OK             jmp _ErrFormat      ;已加密退出             .endif             push [ebx].OptionalHeader.FileAlignment ;将后面要用的参数赋给局部变量             pop @FileAlign             push [ebx].OptionalHeader.SectionAlignment             pop @SectionAlign             push [ebx].OptionalHeader.SizeOfHeaders             pop @SizeOfHeader             push [ebx].OptionalHeader.AddressOfEntryPoint       pop  @dwEntry             movzx eax,[ebx].FileHeader.NumberOfSections             dec eax             mov @SectionNum,eax             mov ecx,sizeof IMAGE_SECTION_HEADER             mul ecx             add eax,@dwPE_Header_off             add eax,sizeof IMAGE_NT_HEADERS             add eax,sizeof IMAGE_SECTION_HEADER             mov @NewSection_off,eax    ;申请一块内存,长度为SizeOfHeaders,用于文件头的修改             invoke GlobalAlloc,GPTR,[ebx].OptionalHeader.SizeOfHeaders       mov @lpAlloc,eax       mov edi,eax ;将打开的内存映射文件的头部,长度为SizeOfHeaders,传送到申请的内存块里       invoke RtlMoveMemory,edi,@lpMemory,[ebx].OptionalHeader.SizeOfHeaders             invoke UnmapViewOfFile,@lpMemory   ;关闭内存映射文件,后面不用了。             invoke CloseHandle,@hMapFile    ;关闭内存映射对象,后面不用了。             add edi,dword ptr [edi+3ch]           ;edi-->指向PE文件头             assume edi :ptr IMAGE_NT_HEADERS             mov esi,edi                                        add esi,sizeof IMAGE_NT_HEADERS             assume esi :ptr IMAGE_SECTION_HEADER       mov ecx,offset APPEND_CODE_END-offset APPEND_CODE             push ecx             pop @AppCodeSize ;加密代码的长度             xor ebx,ebx    ;循环检查每个节中是否有足够的空隙来插入代码             .repeat                 mov edx,[esi].SizeOfRawData             .if !edx ;判断SizeOfRawData是否为0,为0表示该节是未初始化数据的节,跳过             inc ebx             add esi,sizeof IMAGE_SECTION_HEADER             .continue             .endif             sub edx,[esi].Misc.VirtualSize      ;如果[esi].SizeOfRawData <[esi].Misc.VirtualSize,则CF=1,跳过。             .if CARRY?             inc ebx             add esi,sizeof IMAGE_SECTION_HEADER             .continue             .endif             .if edx >= ecx ;节中空隙足够,则将加密代码插入节的空隙中。             jmp insert_code             .endif             add esi,sizeof IMAGE_SECTION_HEADER             inc ebx             .until ebx > @SectionNum             ;当各节空隙都不够插入加密代码时,添加新节(表)。             mov edi,@lpAlloc             mov ebx,edi             add ebx,@NewSection_off  ;ebx--->指向最后一个节表的尾部(新节表的头部)。             pushad    ;检查文件头是否有足够空隙构建新节表             mov edi,ebx             mov al,0             mov ecx,sizeof IMAGE_SECTION_HEADER             cld             repe scasb             popad             .if !ZERO?             invoke MessageBox,NULL,addr szNoRoom,addr szCaptionTip,MB_OK             invoke GlobalFree,@lpAlloc             jmp _NoRoomRet  ;文件头没有足够空隙构建节表,退出口令加密。             .endif             mov edx,ebx                                        sub edx,sizeof IMAGE_SECTION_HEADER   ;edx--->指向最后一个节表的头部。             assume ebx :ptr IMAGE_SECTION_HEADER,edx :ptr IMAGE_SECTION_HEADER             add edi,dword ptr [edi+3ch]           ;edi--->指向PE文件头             assume edi :ptr IMAGE_NT_HEADERS             inc [edi].FileHeader.NumberOfSections ;节数+1           mov eax,[edx].PointerToRawData       add eax,[edx].SizeOfRawData    mov [ebx].PointerToRawData,eax   ;新节在文件中的偏移    mov ecx,offset APPEND_CODE_END-offset APPEND_CODE             mov [ebx].Misc.VirtualSize,ecx   ;新节的实际大小           invoke _Align,ecx,@FileAlign       mov [ebx].SizeOfRawData,eax    ;新节按FileAlignment对齐后的大小           invoke _Align,ecx,@SectionAlign   ;新节按SectionAlignment对齐后的大小    add [edi].OptionalHeader.SizeOfCode,eax   ;修正SizeOfCode    add [edi].OptionalHeader.SizeOfImage,eax  ;修正SizeOfImage    invoke _Align,[edx].Misc.VirtualSize,@SectionAlign    add eax,[edx].VirtualAddress    mov [ebx].VirtualAddress,eax   ;新节在内存中的偏移地址RVA    ;新节的属性设置为可读、可写、可执行含有代码    mov [ebx].Characteristics,0e0000020h              mov dword ptr [ebx+0h],'czz.'   ;新节名Name1命名为’.zzcode’             mov dword ptr [ebx+4h],' edo'             mov word ptr [edi+1ah],"zz"    ;设置加密标志为"zz"           invoke SetFilePointer,@hFile,[ebx].PointerToRawData,NULL,FILE_BEGIN    ;将口令加密代码作为一个新节,附加在文件的尾部    invoke WriteFile,@hFile,offset APPEND_CODE,[ebx].Misc.VirtualSize,\     addr @dwRet,NULL    mov eax,[ebx].PointerToRawData    add eax,[ebx].SizeOfRawData    invoke SetFilePointer,@hFile,eax,NULL,FILE_BEGIN    invoke SetEndOfFile,@hFile   ;将目标PE文件的长度扩展到加密后的长度             push [ebx].VirtualAddress             pop  @AddCodeVirt             push [ebx].PointerToRawData             pop  @AddCodeFile             jmp append_code    ;以下程序段将加密代码插入节的空隙中    ;将节的属性设置为可读、可写、可执行含有代码 insert_code: or  [esi].Characteristics,IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE\                       or IMAGE_SCN_MEM_EXECUTE or IMAGE_SCN_CNT_CODE             mov ebx,[esi].PointerToRawData             add ebx,[esi].Misc.VirtualSize             invoke SetFilePointer,@hFile,ebx,NULL,FILE_BEGIN    ;将加密代码插入节的空隙中    invoke WriteFile,@hFile,offset APPEND_CODE,@AppCodeSize,\     addr @dwRet,NULL       mov eax,[esi].VirtualAddress             add eax,[esi].Misc.VirtualSize             mov @AddCodeVirt,eax             mov ecx,[esi].PointerToRawData             add ecx,[esi].Misc.VirtualSize             mov @AddCodeFile,ecx             mov eax,@AppCodeSize             add [esi].Misc.VirtualSize,eax  ;修改节的实际大小             mov word ptr [edi+1ah],"zz"   ;设置加密标志为"zz"    ;以下是二种附加加密代码的公用程序部分,这样设计是为了减小程序的长度    ;修改目标PE文件的入口地址,使其指向口令加密代码的开始执行处 append_code:  mov eax,@AddCodeVirt       add eax,(offset _NewEntry-offset APPEND_CODE)       mov [edi].OptionalHeader.AddressOfEntryPoint,eax        invoke SetFilePointer,@hFile,0,NULL,FILE_BEGIN    ;将修改后的文件头写入目标PE文件的头部       invoke WriteFile,@hFile,@lpAlloc,@SizeOfHeader,\    addr @dwRet,NULL             mov eax,@AddCodeVirt       add eax,(offset RetOldEntry-offset APPEND_CODE+4)       sub @dwEntry,eax  ;@dwEntry—加密代码执行完后,返回原程序执行的地址             mov ecx,@AddCodeFile       add ecx,(offset RetOldEntry-offset APPEND_CODE)       invoke SetFilePointer,@hFile,ecx,NULL,FILE_BEGIN    ;将返回地址写在加密代码中0e9h(jmp机器码)的后面       invoke WriteFile,@hFile,addr @dwEntry,4,addr @dwRet,NULL             mov ecx,@AddCodeFile       add ecx,(offset APPEND_PASSWD_CODE-offset APPEND_CODE)       invoke SetFilePointer,@hFile,ecx,NULL,FILE_BEGIN    ;将校核后的口令(最大长度为30个字符),写入附加的加密代码留存口令处       invoke WriteFile,@hFile,offset szPwdBuffer2,30,addr @dwRet,NULL       invoke GlobalFree,@lpAlloc  ;关闭申请的内存块       invoke CloseHandle,@hFile  ;关闭打开的目标文件             assume edi : nothing,esi : nothing             invoke MessageBox,NULL,addr szOkCode,addr szCaptionTip,MB_OK             jmp _Ret _ErrFormat:               assume ebx : nothing               invoke UnmapViewOfFile,@lpMemory  ;关闭内存映射文件               .endif               invoke CloseHandle,@hMapFile   ;关闭内存映射对象           .endif _NoRoomRet:   invoke CloseHandle,@hFile    ;关闭打开的目标PE文件 .else     invoke MessageBox,NULL,addr szErrCreate,addr szCaptionTip,MB_OK .endif _Ret:         pop fs:[0]         add esp,0ch                  popad         ret handlePeFile    endp 二、口令加密的主要功能 1.重定位    由于加密代码是在编译、连接之后附加在目标PE文件的尾部或节的空隙中,而各个目标文件的长度又是可变的,导致附加代码中的变量或过程名偏移地址在装入内存后发生变化,使之在目标文件中运行时寻址错误,为了使附加代码中的变量地址正确,必须进行重定位。其重定位的标准格式如下: call @F @@: pop  ebx        ;这个寄存器不一定非用ebx,也可使用其他不常用的寄存器 sub  ebx,offset @B 2.动态获取API函数地址 由于加密代码是附在各种目标文件的尾部或节的空隙中,所需要的API函数地址,各个目标PE文件不可能为你准备好,因此,加密代码必须自己想法解决。这就是要用LoadLibrary函数来动态装入某个DLL模块,然后再用GetProcAddress函数从装入的DLL模块中获取所需要的API函数的入口地址。但是这二个函数都位于Kernel32.dll中,注意到当Kernel32.dll把目标程序装入内存后,堆栈的顶部存放有程序的返回地址,而这个返回地址正好位于Kernel32.dll中;因此,加密代码一开始执行,就从堆栈中取出该地址[esp](如果程序一开始就压栈后再取出返回地址,那应是此形式[esp+xxxx]),搜索Kernel32.dll的基地址,然后再搜索GetProcAddress的入口地址,获得GetProcAddress的入口地址后,再调用GetProcAddress函数获取LoadLibrary函数的入口地址,有了这二个函数后加密代码所需要的API就可以轻松获得。 3.在加密代码中生成对话框    对于普通的对话框程序来说生成对话框较简单,只要在Link的时候把程序的目标文件.obj和资源文件.res连接起来就可实现。而对于加密代码而言,生成对话框就不那么简单了,不单是需要API函数,而且还需要资源文件,对话框能否实现是口令加密的关键问题也是最难的问题,因为加密代码需要用对话框来接收输入的口令。当然用命令行输入口令也可以。好在Win32汇编提供了对话框模板结构,使我们的问题得以解决。无论是模态对话框还是非模态对话框,其建立的过程都会用到模板,而模板就是具有相对固定格式的内存块,其中的数据用以建立对话框的资源和控件,如菜单、子窗口控件、标题栏等,专供代码调用。 对话框模板主要由二部分组成:模板头和模板控件。 (1)模板头 模板头定义对话框的大小、样式、菜单、CLASS、标题名称等。模板头开始的结构如下: DLGTEMPLATE  STRUCT style   DWORD ?   ;对话框样式,如WS_CAPTION、WS_SYSMENU等 dwExtendedStyle DWORD ?   ;扩展样式,对话框不使用 cdit   WORD ?   ;对话框中控件的数目 x    WORD ? y    WORD ?   ;x、y对话框的屏幕坐标 lx    WORD ? ly    WORD ? ;lx、ly对话框的宽和高 DLGTEMPLATE  ENDS 紧跟DLGTEMPLATE结构后面的是菜单、CLASS和对话框标题等组成的三种可变数组。 菜单数组:数组元素都是字,数组边界须字对齐。若数组第一个元素为0,则该对话框不带菜单,并且数组只有一个元素;若数组第一个元素为0ffffh,则第二个元素为菜单资源的序数值,接着就是Unicode字符集的菜单资源的字符串名。 CLASS数组:数组元素都是字,数组边界须字对齐。若数组的第一个元素为0,则为标准控件或通用控件类,并且数组只有一个元素;若数组第一个元素为0ffffh,则第二个元素为系统预定义窗口类的序数值,接着是Unicode字符集的窗口类的字符串名。 标题数组:紧跟在CLASS数组后的是Unicode字符集的标题栏名(以0结束),如果都是0则对话框无标题栏。注意,如果对话框指定了样式为DS_SETFONT,则紧跟在标题栏后的字指定字体的点阵大小,后面跟着Unicode字符集的字体名字符串。 (2)模板控件 模板控件定义控件的大小、样式、CLASS、控件标题、成形数据等。模板控件的开始结构如下: DLGITEMTEMPLATE  STRUCT style   DWORD ?       ;控件样式 dwExtendedStyle DWORD ? x    WORD ? y    WORD ?  ;x、y控件在对话框上的坐标 lx    WORD ? cy    WORD ?       ;lx、cy控件的宽和高 id    WORD ?  ;控件标识符ID DLGITEMTEMPLATE  ENDS 紧跟DLGITEMTEMPLATE结构之后的是CLASS、控件标题、成形数据等组成的三种可变数组,数组元素都是字。对话框有多少控件就应有多少个DLGITEMTEMPLATE结构,每个结构的起始地址必须是双字对齐,而每种数组边界必须字对齐。 CLASS数组:若数组第一个元素是0ffffh,则第二个元素是系统预定义的序数值(0080h/Button类、0081h/Edit类、0082h/Static类等);否则,其后是Unicode字符集的窗口类的字符串名。 标题数组:若数组第一个元素为0ffffh,则第二个元素为控件的资源标识。否则,则是Unicode字符集的标题字符串名。 成形数据数组:若数组的第一个元素不为0,则是指定该成形数据的大小。 实现口令加密的核心代码如下: ;加密代码所用的函数及函数指针定义 ;定义函数和函数指针是为了用伪指令invoke来调用函数 _ProtoGetProcAddress typedef proto :dword,:dword _ProtoLoadLibrary typedef proto :dword _ProtoMessageBox typedef proto :dword,:dword,:dword,:dword _ApiGetProcAddress typedef ptr _ProtoGetProcAddress _ApiLoadLibrary  typedef ptr _ProtoLoadLibrary _ApiMessageBox  typedef ptr _ProtoMessageBox ;对话框所用函数的定义及函数指针的定义 _ProtoGetModuleHandle   typedef proto :dword _protoGlobalAlloc       typedef proto :dword,:dword _ProtoMultiByteToWideChar   typedef proto :dword,:dword,:dword,:dword,:dword,:dword _ProtoDialogBoxIndirectParam    typedef proto :dword,:dword,:dword,:dword,:dword _ProtoGlobalFree        typedef proto :dword _ProtoEndDialog         typedef proto :dword,:dword _ProtoGetDlgItemText    typedef proto :dword,:dword,:dword,:dword _ProtoSetWindowText     typedef proto :dword,:dword _ProtoSendDlgItemMessage    typedef proto :dword,:dword,:dword,:dword,:dword _ApiGetModuleHandle typedef ptr _ProtoGetModuleHandle    _ApiGlobalAlloc typedef ptr _protoGlobalAlloc        _ApiMultiByteToWideChar typedef ptr _ProtoMultiByteToWideChar  _ApiDialogBoxIndirectParam typedef ptr _ProtoDialogBoxIndirectParam  _ApiGlobalFree typedef ptr _ProtoGlobalFree         _ApiEndDialog typedef ptr _ProtoEndDialog          _ApiGetDlgItemText typedef ptr _ProtoGetDlgItemText     _ApiSetWindowText typedef ptr _ProtoSetWindowText      _ApiSendDlgItemMessage typedef ptr _ProtoSendDlgItemMessage ;附加到目标PE文件上的口令加密代码从这里开始 APPEND_CODE equ this byte include  zz_GetKernel.asm hDllKernel32 dd ? hDllUser32 dd ? _GetProcAddress _ApiGetProcAddress ? _LoadLibrary _ApiLoadLibrary  ? _MessageBox _ApiMessageBox  ? szLoadLibrary db 'LoadLibraryA',0 szGetProcAddress db 'Get
  			
				 |