2005年09月27日

        资源编译器用来把资源脚本文件(*.rc)编译成资源文件(*.res),MASM32 软件包中使用的是Visual C++附带的Rc.exe 程序,TASM软件包中使用的是BRC32.exe 或BRCC32.exe,两者的用法都比较简单,它们有相同的命令行语法:

Rc 或BRC32 [选项] 资源脚本文件名
        Rc 和BRC32 在使用中没有必需的选项,不像汇编编译器一样必须使用一些关键的选项。如果编译成功,就会产生以res 为扩展名的资源文件,两者生成的资源文件的格式是一样的。资源文件编写是PE 开发的标准步骤,不同的语言使用的资源编译器以及生成的 .res 文件格式都是一样的,没有汇编格式的资源文件和C 格式的资源文件之分,所以汇编开发包中的资源编译器实际上就是C 开发包中的资源编译器。由于C 语言的使用远比汇编广泛,所以资源脚本文件的语法是C 格式的,如等值定义语句使用#define 而不是汇编常用的equ,注释使用:“//”而不是“”,头文件习惯使用 .h 扩展名而不是 .inc,参数定义有“或”操作时使用“|”操作符而不是汇编的“or”操作符等,这些在使用中必须注意,否则会引起语法错误。两种资源编译器在使用中稍微有所区别,BRC32.exe 内部可以解释Windows 的一些预定义值,所以不用附带头文件,只有遇到最新的预定义时才需要头文件,而Rc 并没有这个功能,所以在脚本文件中必须把头文件Resource.h 包括进去。

         说明,我们要写一个test.exe 文件,生成最后的可执行文件有4 个步骤:
1. 汇编源文件x.asm,其中用到头文件common.inc,它们经Ml.exe 编译成x.obj;
2. 汇编源文件y.asm,用到头文件common.inc 和y.inc,它们经Ml.exe 编译成y.obj;
3. 资源脚本文件x.rc,经Rc.exe 编译成x.res;
4. 最后用Link 将x.obj,y.obj 和x.res 链接成test.exe。
         看出,当程序调试的时候,如果修改了x.asm,也就是说x.obj 的文件时间比x.asm要早,就需要重新进行步骤1 和4;如果修改了y.asm 或y.inc,那么需要重新执行步骤2 和4;如果修改的是x.rc,则步骤3 和4 必须重新执行;如果修改的是common.inc,因为x.asm 和y.asm 都和它有关,所以步骤1、2 和4 都要重新执行;如果同时修改了common.inc 和x.rc,那么必须重复全部步骤。在这个例子中,文件的依赖关系就是:
1. test.exe 依赖于 x.obj,y.obj 和 x.res;
2. x.res 依赖于 x.rc;
3. x.obj 依赖于 x.asm 和 common.inc;
4. y.obj 依赖于 y.asm,common.inc 和 y.inc。

         ke 可以根据文件的时间正确判断文件的新旧并执行相应的步骤。但make 又是如何知道文件之间的依赖关系呢?这需要用户用一个描述文件来指定。前面提到的makefile 就是这个描述文件,执行make 工具的时候,它会默认用makefile 做描述文件名来进行相应的工作,书写描述文件有规定的语法,虽然语法不是很简单,但写好以后就省事多了。

       Ms的make 工具文件名为nmake.exe,它并不是MASM 软件包的一部分,但可以在Visual C++的Bin 目录下找到。Borland 公司的make 工具文件名是make.exe,它已经包括在TASM 5.0 工具包中。两者默认的描述文件名都是makefile,描述文件的语法也大同小异,只是使用时命令行参数有些不同。
在命令行键入nmake /? 可以显示帮助信息,nmake 的语法为:
nmake [选项] [/f 描述文件名] [/x 输出信息文件名] [宏定义] [目标]
说明如下:
/f 参数棗如果描述文件名不使用默认的 makefile,可以用/f 参数指定。
/x 参数棗如果想把屏幕输出的信息存到一个文件中,可以用/x 参数指定(用 DOS下的管道操作符nmake > 文件名的方法无效)。

描述文件一般需要包含以下内容:
● 注释
● 宏定义
● 显式规则
● 隐含规则
在这里,首先为2.4.1 节中有关test.exe 的例子写出一个描述文件,再逐步介绍各部分的书写语法。为了方便使用,一般都把描述文件的文件名取为默认文件名:makefile。这个例子的makefile 文件如下(注意前面括号里的是行号,不是文件的真正内容):
(001) # nmake 工具的描述文件例子
(002) EXE = Test.exe #指定输出文件
(003) OBJS = x.obj \
(004) y.obj #需要的目标文件
(005) RES = x.res #需要的资源文件
(006)
(007) LINK_FLAG = /subsystem:windows #链接选项
(008) ML_FLAG = /c /coff #编译选项
(009)
(010) #定义依赖关系和执行命令
(011) $(EXE): $(OBJS) $(RES)
(012) Link $(LINK_FLAG) /out:$(EXE) $(OBJS) $(RES)
(013) $(OBJS): Common.inc
(014) y.obj: y.inc
(015)
(016) #定义汇编编译和资源编译的默认规则
(017) .asm.obj:
(018) ml $(ML_FLAG) $<
(019) .rc.res:
(020) rc $<
(021)
(022) #清除临时文件
(023) clean:
(024) del *.obj
(025) del *.res

1. 注释和换行
makefile 中的注释是以#号开头一直到行尾的字符,当nmake 工具处理到这些字符的时候,它会完全忽略#号及全部注释字符。
当一行的内容过长的时候,可以用换行符来继续,makefile 的换行符是\,如例子中的第三行和第四行可以合并为:
OBJS = x.obj y.obj #需要的目标文件
在使用换行符的时候要注意在“\”后面不能再加上其他字符,包括注释和空格,否则nmake 检测到“\”不在一行的最后,就不会把它当成换行符解释,就会出现错误。
2. 宏定义
makefile 中允许使用简单的宏定义指代源文件及其相关编译信息,可以把宏称为变量,在整个描述文件中,只要符合下面语法的行就是宏定义:
变量名=变量内容
如上面例子文件中的2 到8 就是宏定义,在引用宏时只需在变量前加$符号,但是要注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号(),下面都是有效的宏引用:
$(LINK_FLAG)
$(EXE)
$A
$(A)

其中最后两个引用是完全一致的。

3. 显式规则
makefile 中包含有一些规则,这些规则定义了文件之间的依赖关系和产生命令,一个规
则的格式是这样的:
目标文件:依赖文件;命令 (方法1)

目标文件:依赖文件 (方法2)
命令
在规则定义和命令行中,不能包含注释,例子中的第11 和12 行把宏定义展开后就是:
test.exe:x.obj y.obj x.res
Link /subsystem:windows /out:test.exe x.obj y.obj x.res

        这里的目标文件就是test.exe,它依赖于3 个文件x.obj,y.obj 和x.res,如果有必要,产生目标文件的命令就是下面的Link 命令,整个规则可以用两种方法,用第二种方法的时候,命令可以从第二行开始,第一行的“;”省略,但是这时命令前面必须有一个Tab 字符,否则nmake 无法区分这究竟是命令还是别的定义。目标文件可以有多个,依赖文件也可以有多个,同时命令也可以由多个命令行组成,当然这时候就必须用第二种方法定义了。

        我们也可以用test.exe 生成的规则定义其他文件,如x.obj 或x.res 的生成方法,但nmake如何知道哪个是最终要make 的文件呢?实际上nmake 默认将整个描述文件的第一条规则中的目标文件认为是最终文件,如果我们把11,12 行放到第13 行后面,那么x.obj 和y.obj 的建立规则就成了第一条规则,nmake 建立了x.obj 和x.obj 之后就不理会test.exe 的建立了,所以我们必须把最终需要生成的文件放在第一条规则定义。当然,在nmake 的命令行参数中可以指定要make 的目标,如我们要生成x.res 文件,那么不必修改makefile 将x.res 的描述规则移动到最前面,而是直接在命令行键入以下命令即可:
nmake x.res

4. 隐含规则
        隐含规则可以为某一类的文件指出建立的命令,它具体定义了如何将带一个特定扩展名的文件转换成具有另一种扩展名的文件,定义的格式是:
.源扩展名.目标扩展名:;命令 (方法1)

.源扩展名.目标扩展名: (方法2)
命令

        隐含规则的语法和显式规则相似,也是用“:”隔开,在“;”下面书写命令,也可以不用“;”而将命令写在第二行,同理,这时命令之前要加一个Tab 字符。
       隐含规则不能有依赖文件,所以“:”下面没有内容,例子中的第17、18 行定义了从asm文件建立obj 文件的隐含规则,第19 和20 行定义了从rc 文件建立res 文件的隐含规则,隐含规则中无法指定确定的输入文件名,因为输入文件名是泛指的有相同扩展名的一整类文件,这时候就要用到几个特殊的内定宏来指定文件名,这些宏是$@,$*,$?和$<,它们的含义如下:
$@    全路径的目标文件。
$*      除去扩展名的全路径的目标文件。
$?     所有源文件名。
$<     源文件名(只能用在隐含规则中)。
所以第19、20 行中的rc $< 用于x.rc 的时候就是rc x.rc。

        读者可以注意到一些显式规则没有命令行,如第13 行的“$(OBJS): Common.inc”指出了所有的obj 文件全部依赖于Common.inc 文件,第14 行的“y.obj: y.inc”则指出了y.obj 同时也依赖于y.inc 和第13 行的规则合并,y.obj 依赖于Common.inc 也依赖于y.inc,但是这两条规则都没有指出产生这些obj 文件的命令,所以nmake 处理的时候会到隐含规则中去找命令行,最后会用第18 行的“ml $(ML_FLAG) $<命令去产生这些obj 文件。

        资源文件确实太麻烦了,不说那么多重复的代码,就说那些个x、y坐标,就令人十分的头痛了,仔细翻看一下书的第二章,推荐的是Resource WorkshopVisual C++编辑资源。比较了一下,还是用VC容易多了~,虽然生成的代码太多VC的东西,需要手工删除,但是他毕竟是所见即所得!

资源文件:

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include  <resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define ICO_MAIN  0×1000 //图标
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define IDM_MAIN  0×2000 //菜单
#define IDA_MAIN  0×2000 //加速键
#define IDM_OPEN  0×4101
#define IDM_OPTION  0×4102
#define IDM_EXIT  0×4103  
#define IDM_SETFONT  0×4201
#define IDM_SETCOLOR 0×4202
#define IDM_INACT  0×4203
#define IDM_GRAY  0×4204
#define IDM_BIG   0×4205
#define IDM_SMALL  0×4206
#define IDM_LIST  0×4207
#define IDM_DETAIL  0×4208
#define IDM_TOOLBAR  0×4209
#define IDM_TOOLBARTEXT 0×4210
#define IDM_INPUTBAR 0×4211
#define IDM_STATUSBAR 0×4212
#define IDM_HELP  0×4301
#define IDM_ABOUT  0×4302
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN ICON  "Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDM_MAIN menu discardable
BEGIN
 popup "文件(&F)"
 BEGIN
  menuitem "打开文件(&O)…", IDM_OPEN
  menuitem "关闭文件(&C)…", IDM_OPTION
  menuitem separator
  menuitem "退出(&X)",  IDM_EXIT
 END
 popup "查看(&V)"
 BEGIN
  menuitem "字体(&F)…\tAlt+F",IDM_SETFONT
  menuitem "背景色(&B)…\tCtrl+Alt+B",IDM_SETCOLOR
  menuitem separator
  menuitem "被禁用的菜单项", IDM_INACT, INACTIVE
  menuitem "被灰化的菜单项", IDM_GRAY, GRAYED
  menuitem separator
  menuitem "大图标(&G)",  IDM_BIG
  menuitem "小图标(&M)",  IDM_SMALL
  menuitem "列表(&L)",   IDM_LIST
  menuitem "详细资料(&D)",  IDM_DETAIL
  menuitem separator
  popup  "工具栏(&T)"
  BEGIN
     menuitem "标准按钮(&S)",  IDM_TOOLBAR
     menuitem "文字标签(&C)",  IDM_TOOLBARTEXT
     menuitem "命令栏(&I)",  IDM_INPUTBAR
  END
  menuitem "状态栏(&U)",  IDM_STATUSBAR
 END
 popup "帮助(&H)" ,HELP
 BEGIN
  menuitem "帮助主题(&H)\tF1", IDM_HELP
  menuitem separator
  menuitem "关于本程序(&A)…",IDM_ABOUT
 END
END
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDA_MAIN accelerators
BEGIN
  VK_F1, IDM_HELP, VIRTKEY
  "B", IDM_SETCOLOR,VIRTKEY,CONTROL,ALT
  "F", IDM_SETFONT,VIRTKEY,ALT
END
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; Menu.asm
; 菜单资源的使用例子
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff Menu.asm
; rc Menu.rc
; Link /subsystem:windows Menu.obj Menu.res
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .386
  .model flat, stdcall
  option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include  windows.inc
include  user32.inc
includelib user32.lib
include  kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ  1000h ;图标
IDM_MAIN equ  2000h ;菜单
IDA_MAIN equ  2000h ;加速键
IDM_OPEN equ  4101h
IDM_OPTION equ  4102h
IDM_EXIT equ  4103h
IDM_SETFONT equ  4201h
IDM_SETCOLOR equ  4202h
IDM_INACT equ  4203h
IDM_GRAY equ  4204h
IDM_BIG  equ  4205h
IDM_SMALL equ  4206h
IDM_LIST equ  4207h
IDM_DETAIL equ  4208h
IDM_TOOLBAR equ  4209h
IDM_TOOLBARTEXT equ  4210h
IDM_INPUTBAR equ  4211h
IDM_STATUSBAR equ  4212h
IDM_HELP equ  4301h
IDM_ABOUT equ  4302h
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .data?
hInstance dd  ?
hWinMain dd  ?
hMenu  dd  ?
hSubMenu dd  ?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .const
szClassName db ’Menu Example’,0
szCaptionMain db ’Menu’,0
szMenuHelp db ’帮助主题(&H)’,0
szMenuAbout db ’关于本程序(&A)…’,0
szCaption db ’菜单选择’,0
szFormat db ’您选择了菜单命令:%08x’,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_DisplayMenuItem proc _dwCommandID
   local @szBuffer[256]:byte

  pushad
  invoke wsprintf,addr @szBuffer,addr szFormat,_dwCommandID
  invoke MessageBox,hWinMain,addr @szBuffer,offset szCaption,MB_OK
  popad
  ret

_DisplayMenuItem endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Quit  proc

  invoke DestroyWindow,hWinMain
  invoke PostQuitMessage,NULL
  ret

_Quit  endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi hWnd,uMsg,wParam,lParam
  local @stPos:POINT
  local @hSysMenu

  mov eax,uMsg
  .if eax == WM_CREATE
   invoke GetSubMenu,hMenu,1
   mov hSubMenu,eax
;********************************************************************
; 在系统菜单中添加菜单项
;********************************************************************
   invoke GetSystemMenu,hWnd,FALSE
   mov @hSysMenu,eax
   invoke AppendMenu,@hSysMenu,MF_SEPARATOR,0,NULL
   invoke AppendMenu,@hSysMenu,0,IDM_HELP,offset szMenuHelp
   invoke AppendMenu,@hSysMenu,0,IDM_ABOUT,offset szMenuAbout
;********************************************************************
; 处理菜单及加速键消息
;********************************************************************
  .elseif eax == WM_COMMAND
   invoke _DisplayMenuItem,wParam
   mov eax,wParam
   movzx eax,ax
   .if eax == IDM_EXIT
    call _Quit
   .elseif eax >= IDM_TOOLBAR && eax <= IDM_STATUSBAR
    mov ebx,eax
    invoke GetMenuState,hMenu,ebx,MF_BYCOMMAND
    .if eax == MF_CHECKED
     mov eax,MF_UNCHECKED
    .else
     mov eax,MF_CHECKED
    .endif
    invoke CheckMenuItem,hMenu,ebx,eax
   .elseif eax >= IDM_BIG && eax <= IDM_DETAIL
    invoke CheckMenuRadioItem,hMenu,IDM_BIG,IDM_DETAIL,eax,MF_BYCOMMAND
   .endif
;********************************************************************
; 处理系统菜单消息
;********************************************************************
  .elseif eax == WM_SYSCOMMAND
   mov eax,wParam
   movzx eax,ax
   .if eax == IDM_HELP || eax == IDM_ABOUT
    invoke _DisplayMenuItem,wParam
   .else
    invoke DefWindowProc,hWnd,uMsg,wParam,lParam
    ret
   .endif
;********************************************************************
; 按下右键时弹出一个POPUP菜单
;********************************************************************
  .elseif eax == WM_RBUTTONDOWN
   invoke GetCursorPos,addr @stPos
   invoke TrackPopupMenu,hSubMenu,TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL
;********************************************************************
  .elseif eax == WM_CLOSE
   call _Quit
;********************************************************************
  .else
   invoke DefWindowProc,hWnd,uMsg,wParam,lParam
   ret
  .endif
;********************************************************************
  xor eax,eax
  ret

_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
  local @stWndClass:WNDCLASSEX
  local @stMsg:MSG
  local @hAccelerator

  invoke GetModuleHandle,NULL
  mov hInstance,eax
  invoke LoadMenu,hInstance,IDM_MAIN
  mov hMenu,eax
  invoke LoadAccelerators,hInstance,IDA_MAIN
  mov @hAccelerator,eax
;********************************************************************
; 注册窗口类
;********************************************************************
  invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
  invoke LoadIcon,hInstance,ICO_MAIN
  mov @stWndClass.hIcon,eax
  mov @stWndClass.hIconSm,eax
  invoke LoadCursor,0,IDC_ARROW
  mov @stWndClass.hCursor,eax
  push hInstance
  pop @stWndClass.hInstance
  mov @stWndClass.cbSize,sizeof WNDCLASSEX
  mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
  mov @stWndClass.lpfnWndProc,offset _ProcWinMain
  mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
  mov @stWndClass.lpszClassName,offset szClassName
  invoke RegisterClassEx,addr @stWndClass
;********************************************************************
; 建立并显示窗口
;********************************************************************
  invoke CreateWindowEx,WS_EX_CLIENTEDGE,\
   offset szClassName,offset szCaptionMain,\
   WS_OVERLAPPEDWINDOW,\
   100,100,400,300,\
   NULL,hMenu,hInstance,NULL
  mov hWinMain,eax
  invoke ShowWindow,hWinMain,SW_SHOWNORMAL
  invoke UpdateWindow,hWinMain
;********************************************************************
; 消息循环
;********************************************************************
  .while TRUE
   invoke GetMessage,addr @stMsg,NULL,0,0
   .break .if eax == 0
   invoke TranslateAccelerator,hWinMain,@hAccelerator,addr @stMsg
   .if eax == 0
    invoke TranslateMessage,addr @stMsg
    invoke DispatchMessage,addr @stMsg
   .endif
  .endw
  ret

_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
  call _WinMain
  invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  end start

The AppendMenu function appends a new item to the end of the specified menu bar, drop-down menu, submenu, or shortcut menu. You can use this function to specify the content, appearance, and behavior of the menu item.

The AppendMenu function has been superseded by the InsertMenuItem function. You can still use AppendMenu, however, if you do not need any of the extended features of InsertMenuItem.

BOOL AppendMenu(
  HMENU hMenu,      // handle to menu to be changed
  UINT uFlags,      // menu-item flags
  UINT uIDNewItem,  // menu-item identifier or handle to drop-down menu or submenu
  LPCTSTR lpNewItem // menu-item content
);



The GetMenuState function retrieves the menu flags associated with the specified menu 

item. If the menu item opens a submenu, this function also returns the number of items in the submenu. 

The GetMenuState function has been superseded by the GetMenuItemInfo function. You can still use

 GetMenuState, however, if you do not need any of the extended features of GetMenuItemInfo.
UINT GetMenuState(
  HMENU hMenu, // handle to menu
  UINT uId,    // menu item to query
  UINT uFlags  // menu flags
);


The CheckMenuItem function sets the state of the specified menu item's check mark attribute 

to either checked or unchecked. 

The CheckMenuItem function has been superseded by the SetMenuItemInfo function. You can still use 

CheckMenuItem, however, if you do not need any of the extended features of SetMenuItemInfo.
DWORD CheckMenuItem(
  HMENU hmenu,        // handle to menu
  UINT uIDCheckItem,  // menu item to check or uncheck
  UINT uCheck         // menu item flags
);


The   CheckMenuRadioItem function checks a specified menu item and makes it a radio item. At the 

same time, the function unchecks all other menu items in the associated group and clears the 

radio-item type flag for those items.
BOOL CheckMenuRadioItem(
  HMENU hmenu,
  UINT idFirst,
  UINT idLast,
  UINT idCheck,
  UINT uFlags    
);


The   GetCursorPos function retrieves the cursor's position, in screen coordinates. 
BOOL GetCursorPos(
  LPPOINT lpPoint   // address of structure for cursor position
);


The   TrackPopupMenu function displays a shortcut menu at the specified location and tracks the 

selection of items on the menu. The shortcut menu can appear anywhere on the screen.
BOOL TrackPopupMenu(
  HMENU hMenu,         // handle to shortcut menu
  UINT uFlags,         // screen-position and mouse-button flags
  int x,               // horizontal position, in screen coordinates
  int y,               // vertical position, in screen coordinates
  int nReserved,       // reserved, must be zero
  HWND hWnd,           // handle to owner window
  CONST RECT *prcRect  // ignored
);


The   LoadMenu function loads the specified menu resource from the executable (.EXE) file 

associated with an application instance. 
HMENU LoadMenu(
  HINSTANCE hInstance,  // handle to application instance
  LPCTSTR lpMenuName    // menu name string or menu-resource
                        // identifier
);


The   LoadAccelerators function loads the specified accelerator table. 
HACCEL LoadAccelerators(
  HINSTANCE hInstance,  // handle to application instance
  LPCTSTR lpTableName   // address of table-name string
);

2005年09月23日

  .386
  .model flat,stdcall
  option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include  windows.inc
include  gdi32.inc
includelib gdi32.lib
include  user32.inc
includelib user32.lib
include  kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .data?

hInstance dd  ?
hWinMain dd  ?

  .const

szClassName db ’MyClass’,0
szCaptionMain db ’My first Window !’,0
szText  db ’Win32 Assembly, Simple and powerful !’,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam
  local @stPs:PAINTSTRUCT
  local @stRect:RECT
  local @hDc

  mov eax,uMsg
;********************************************************************
  .if eax == WM_PAINT
   invoke BeginPaint,hWnd,addr @stPs
   mov @hDc,eax

   invoke GetClientRect,hWnd,addr @stRect
   invoke DrawText,@hDc,addr szText,-1,\
    addr @stRect,\
    DT_SINGLELINE or DT_CENTER or DT_VCENTER

   invoke EndPaint,hWnd,addr @stPs
;********************************************************************
  .elseif eax == WM_CLOSE
   invoke DestroyWindow,hWinMain
   invoke PostQuitMessage,NULL
;********************************************************************
  .else
   invoke DefWindowProc,hWnd,uMsg,wParam,lParam
   ret
  .endif
;********************************************************************
  xor eax,eax
  ret

_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
  local @stWndClass:WNDCLASSEX
  local @stMsg:MSG

  invoke GetModuleHandle,NULL
  mov hInstance,eax
  invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;********************************************************************
; 注册窗口类
;********************************************************************
  invoke LoadCursor,0,IDC_ARROW
  mov @stWndClass.hCursor,eax
  push hInstance
  pop @stWndClass.hInstance
  mov @stWndClass.cbSize,sizeof WNDCLASSEX
  mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
  mov @stWndClass.lpfnWndProc,offset _ProcWinMain
  mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
  mov @stWndClass.lpszClassName,offset szClassName
  invoke RegisterClassEx,addr @stWndClass
;********************************************************************
; 建立并显示窗口
;********************************************************************
  invoke CreateWindowEx,WS_EX_CLIENTEDGE,offset szClassName,offset szCaptionMain,\
   WS_OVERLAPPEDWINDOW,\
   100,100,600,400,\
   NULL,NULL,hInstance,NULL
  mov hWinMain,eax
  invoke ShowWindow,hWinMain,SW_SHOWNORMAL
  invoke UpdateWindow,hWinMain
;********************************************************************
; 消息循环
;********************************************************************
  .while TRUE
   invoke GetMessage,addr @stMsg,NULL,0,0
   .break .if eax == 0
   invoke TranslateMessage,addr @stMsg
   invoke DispatchMessage,addr @stMsg
  .endw
  ret

_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
  call _WinMain
  invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  end start

程序的流程也就是常见的windows下的编程,这里简单回顾一下~

GetModuleHandle → RtlZeroMemory → LoadCursor → RegisterClassEx→ CreateWindowEx → ShowWindow → UpdateWindow ;API调用顺序

GetMessage → TranslateMessage → DispatchMessage ;消息循环

(1)得到应用程序的句柄(GetModuleHandle)。
(2)注册窗口类(RegisterClassEx)。在注册之前,要先填写RegisterClassEx 的参数
WNDCLASSEX 结构。
(3)建立窗口(CreateWindowEx)。
(4)显示窗口(ShowWindows)。
(5)刷新窗口客户区(UpdateWindow)。
(6)进入无限的消息获取和处理的循环。首先获取消息(GetMessage),如果有消息到达,则将消息分派到回调函数处理(DispatchMessage),如果消息是WM_QUIT,则退出循环。
        程序的另一半_ProcWinMain 子程序是用来处理消息的,它就是窗口的回调函数(Callback),也叫做窗口过程,之所以是回调函数是因为它是由Windows 而不是我们自己调用的,我们调用DispatchMessage,而DispatchMessage 再回过来调用窗口过程。

        Windows 为每个程序(严格地说是每个线程)维护一个消息队列,Windows 检查系统消息队列里消息的发生位置,当位置位于某个应用程序的窗口范围内的时候,就把这个消息派送到应用程序的消息队列里,如图4.4 中的箭头c 所示。
        当应用程序还没有来取消息的时候,消息就暂时保留在消息队列里,当程序中的消息循环执行到GetMessage 的时候,控制权转移到GetMessage 所在的USER32.DLL 中(箭头1),USER32.DLL 从程序消息队列中取出一条消息(箭头2),然后把这条消息返回应用程序(箭头3)。
应用程序可以对这条消息进行预处理,如可以用TranslateMessage 把基于键盘扫描码的按键消息转换成基于ASCII 码的键盘消息,以后也会用到TranslateAccelerator 把键盘快捷键转换成命令消息,但这个步骤不是必需的。
        然后应用程序将处理这条消息,但方法不是自己直接调用窗口过程来完成,而是通过DispatchMessage 间接调用窗口过程,Dispatch 的英文含义是“分派”,之所以是“分派”,是因为一个程序可能建有不止一个窗口,不同的窗口消息必须分派给相应的窗口过程。当控制权转移到USER32.DLL 中的DispatchMessage 时,DispatchMessage 找出消息对应窗口的窗口过程,然后把消息的具体信息当做参数来调用它(箭头5),窗口过程根据消息找到对应的分支去处理,然后返回(箭头6),这时控制权回到DispatchMessage,最后DispatchMessage 函数返回应用程序(箭头7)。这样,一个循环就结束了,程序又开始新一轮的GetMessage

        应用程序之间也可以互发消息,PostMessage 是把一个消息放到其他程序的消息队列中,如图4.4 中箭头d 所示,目标程序收到了这条消息就把它放入该程序的消息队列去处理;而SendMessage 则越过消息队列直接调用目标程序的窗口过程(箭头I 所示),窗口过程返回以后才从SendMessage 返回(箭头II 所示)。
        窗口过程是由Windows 回调的,Windows 又是怎么知道往哪里回调呢?答案是我们在调用RegisterClassEx 函数的时候告诉了Windows

        hbrBackground  窗口客户区的背景色。前面的 hbr 表示它是一个刷子(Brush)的句柄,"刷子"一词形象地表示了填充一个区域的着色模式。Windows 预定义了一些刷子,如BLACK_BRUSHWHITE_BRUSH 等,可以用下列语句来得到它们的句柄:
invoke GetStockObject, WHITE_BRUSH
        但在这里也可以使用颜色值,Windows 已经预定义了一些颜色值,分别对应窗口各部分的颜色,如COLOR_BACKGROUNDCOLOR_HIGHLIGHTCOLOR_MENU COLOR_WINDOW等,
使用颜色值的时候,Windows 规定必须在颜色值上加1,所以程序中的指令是:

mov @stWndClass.hbrBackground,COLOR_WINDOW + 1

对于不同二进制位组合的计算,“加”和“或”的结果是一样的,在FirstWindow程序中用CS_HREDRAW or CS_VREDRAW 来代表两个组合,若用CS_HREDRAW+CS_VREDRAW 也并没有什么不同,但强烈建议使用or,因为如果不小心指定了两个同样的风格时:CS_HREDRAW or CS_VREDRAW or CS_VREDRAW和原来的数值是一样的,而CS_HREDRAW+CS_VREDRAW+ CS_VREDRAW就不对了,因为1 or 1=1,而1+1 就等于2 了。

窗口间的消息互发

invoke PostMessage,hWnd,Msg,wParam,lParam
invoke SendMessage,hWnd,Msg,wParam,lParam

对于不同的Msg,wParam 和lParam 的含义是不同的,如对于WM_SETTEXT 是:
wParam = 0; // 未定义,必须为0
lParam = (LPARAM)(LPCTSTR)lpsz; // 要设置的字符串地址

具体请看代码:
; Receive.asm
; 从一个程序向另一个窗口程序发送消息 之 消息接收程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff Receive.asm
; Link /subsystem:windows Receive.obj
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .386
  .model flat,stdcall
  option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include  windows.inc
include  gdi32.inc
includelib gdi32.lib
include  user32.inc
includelib user32.lib
include  kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .data?

hInstance dd ?
hWinMain dd ?
szBuffer db 512 dup (?)

  .const
szClassName db ’MyClass’,0
szCaptionMain db ’消息接收窗口’,0

szReceive1 db ’收到 WM_SETTEXT 消息!’,0dh,0ah,0dh,0ah
  db ’字符串地址: %08x’,0dh,0ah
  db ’字符串: %s’,0dh,0ah,0
szReceive2 db ’收到 WM_COPYDATA 消息!’,0dh,0ah,0dh,0ah
  db ’数据地址: %d’,0dh,0ah
  db ’数据: %s’,0dh,0ah,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam

  mov eax,uMsg
;********************************************************************
  .if eax == WM_CLOSE
   invoke DestroyWindow,hWinMain
   invoke PostQuitMessage,NULL
;********************************************************************
; 收到 WM_SETTEXT 消息则将消息字符串和字符串地址显示出来
;********************************************************************
  .elseif eax == WM_SETTEXT
   invoke wsprintf,addr szBuffer,addr szReceive1,\
    lParam,lParam
   invoke MessageBox,hWnd,offset szBuffer,addr szCaptionMain,MB_OK
;********************************************************************
; 收到 WM_COPYDATA 消息将消息附带的数据长度和字符串数据显示出来
;********************************************************************
  .elseif eax == WM_COPYDATA
   mov eax,lParam
   assume eax:ptr COPYDATASTRUCT
   invoke wsprintf,addr szBuffer,addr szReceive2,\
    [eax].lpData,[eax].lpData
   invoke MessageBox,hWnd,offset szBuffer,addr szCaptionMain,MB_OK
   assume eax:nothing
;********************************************************************
  .else
   invoke DefWindowProc,hWnd,uMsg,wParam,lParam
   ret
  .endif
;********************************************************************
  xor eax,eax
  ret

_ProcWinMain endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
  local @stWndClass:WNDCLASSEX
  local @stMsg:MSG

  invoke GetModuleHandle,NULL
  mov hInstance,eax
  invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;********************************************************************
; 注册窗口类
;********************************************************************
  invoke LoadCursor,0,IDC_ARROW
  mov @stWndClass.hCursor,eax
  push hInstance
  pop @stWndClass.hInstance
  mov @stWndClass.cbSize,sizeof WNDCLASSEX
  mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
  mov @stWndClass.lpfnWndProc,offset _ProcWinMain
  mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
  mov @stWndClass.lpszClassName,offset szClassName
  invoke RegisterClassEx,addr @stWndClass
;********************************************************************
; 建立并显示窗口
;********************************************************************
  invoke CreateWindowEx,WS_EX_CLIENTEDGE or WS_EX_TOPMOST,offset szClassName,offset szCaptionMain,\
   WS_OVERLAPPEDWINDOW,\
   50,50,200,150,\
   NULL,NULL,hInstance,NULL
  mov hWinMain,eax
  invoke ShowWindow,hWinMain,SW_SHOWNORMAL
  invoke UpdateWindow,hWinMain
;********************************************************************
; 消息循环
;********************************************************************
  .while TRUE
   invoke GetMessage,addr @stMsg,NULL,0,0
   .break .if eax == 0
   invoke TranslateMessage,addr @stMsg
   invoke DispatchMessage,addr @stMsg
  .endw
  ret

_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
  call _WinMain
  invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  end start


; SendMessage.asm
; 从一个程序向另一个窗口程序发送消息 之 发送程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff SendMessage.asm
; Link /subsystem:windows SendMessage.obj
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .386
  .model flat,stdcall
  option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include  windows.inc
include  user32.inc
includelib user32.lib
include  kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .data
hWnd  dd ?
szBuffer db 512 dup (?)
stCopyData COPYDATASTRUCT <>

  .const
szCaption db ’发送窗口’,0
szDestClass db ’MyClass’,0 ;目标窗口的窗口类
szStart1 db ’按下“确定”用 SendMessage 发送 WM_SETTEXT 消息!’,0dh,0ah,0dh,0ah
  db ’字符串地址:%08x’,0dh,0ah
  db ’字符串:%s’,0
szStart2 db ’SendMessage 返回!’,0dh,0ah,0dh,0ah
  db ’接下来按“确定”用 SendMessage 发送 WM_COPYDATA 消息!’,0dh,0ah,0dh,0ah
  db ’数据地址:%d’,0dh,0ah
  db ’数据:%s’,0
szReturn2 db ’SendMessage 返回!’,0

szText  db ’这是发送的测试文本’,0
szNotFound db ’目标窗口未找到,请先运行Receive.exe!’,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .code
start:
  invoke FindWindow,addr szDestClass,NULL
  .if eax
   mov hWnd,eax ;找到目标窗口则发送消息
;********************************************************************
   invoke wsprintf,addr szBuffer,addr szStart1,addr szText,addr szText
   invoke MessageBox,NULL,offset szBuffer,offset szCaption,MB_OK
   invoke SendMessage,hWnd,WM_SETTEXT,0,addr szText
;********************************************************************
   invoke wsprintf,addr szBuffer,addr szStart2,addr szText,addr szText
   invoke MessageBox,NULL,offset szBuffer,offset szCaption,MB_OK
   mov stCopyData.cbData,sizeof szText
   mov stCopyData.lpData,offset szText
   invoke SendMessage,hWnd,WM_COPYDATA,0,addr stCopyData
   invoke MessageBox,NULL,offset szReturn2,offset szCaption,MB_OK
  .else
   invoke MessageBox,NULL,offset szNotFound,offset szCaption,MB_OK
  .endif
  invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  end start


WM_COPYDATA

The WM_COPYDATA message is sent when an application passes data to another application.

WM_COPYDATA
wParam = (WPARAM) (HWND) hwnd;            // handle of sending window
lParam = (LPARAM) (PCOPYDATASTRUCT) pcds; // pointer to structure with data 

WM_SETTEXT

An application sends a WM_SETTEXT message to set the text of a window.
WM_SETTEXT
wParam = 0;                     // not used; must be zero
lParam = (LPARAM)(LPCTSTR)lpsz; // address of window-text string 

COPYDATASTRUCT

The COPYDATASTRUCT structure contains data to be passed to another application by the WM_COPYDATA message.
typedef struct tagCOPYDATASTRUCT {  // cds
    DWORD dwData;
    DWORD cbData;
    PVOID lpData;
} COPYDATASTRUCT;

FindWindow

The FindWindow function retrieves a handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. This function does not perform a case-sensitive search.
HWND FindWindow(
  LPCTSTR lpClassName,  // pointer to class name
  LPCTSTR lpWindowName  // pointer to window name
);


; MsgWindow.asm
; 将窗口的消息流程显示到 Notepad 进程的编辑窗口中
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff MsgWindow.asm
; Link /subsystem:windows MsgWindow.obj
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .386
  .model flat,stdcall
  option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include  windows.inc
include  gdi32.inc
includelib gdi32.lib
include  user32.inc
includelib user32.lib
include  kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .data?

hInstance dd ?
hWinMain dd ?
  .const
szClassName db 'MyClass',0
szCaptionMain db 'Message Tester',0
;********************************************************************
; 消息ID列表
;********************************************************************
dwMsgTable dd WM_NULL
  dd WM_CREATE
  dd WM_DESTROY
  dd WM_MOVE
  dd WM_SIZE
  dd WM_ACTIVATE
  dd WM_SETFOCUS
  dd WM_KILLFOCUS
  dd WM_ENABLE
  dd WM_SETREDRAW
  dd WM_SETTEXT
  dd WM_GETTEXT
  dd WM_GETTEXTLENGTH
  dd WM_PAINT
  dd WM_CLOSE
  dd WM_QUERYENDSESSION
  dd WM_QUIT
  dd WM_QUERYOPEN
  dd WM_ERASEBKGND
  dd WM_SYSCOLORCHANGE
  dd WM_ENDSESSION
  dd WM_SHOWWINDOW
  dd WM_WININICHANGE
  dd WM_DEVMODECHANGE
  dd WM_ACTIVATEAPP
  dd WM_FONTCHANGE
  dd WM_TIMECHANGE
  dd WM_CANCELMODE
  dd WM_SETCURSOR
  dd WM_MOUSEACTIVATE
  dd WM_CHILDACTIVATE
  dd WM_QUEUESYNC
  dd WM_GETMINMAXINFO
  dd WM_PAINTICON
  dd WM_ICONERASEBKGND
  dd WM_NEXTDLGCTL
  dd WM_SPOOLERSTATUS
  dd WM_DRAWITEM
  dd WM_MEASUREITEM
  dd WM_DELETEITEM
  dd WM_VKEYTOITEM
  dd WM_CHARTOITEM
  dd WM_SETFONT
  dd WM_GETFONT
  dd WM_SETHOTKEY
  dd WM_GETHOTKEY
  dd WM_QUERYDRAGICON
  dd WM_COMPAREITEM
  dd WM_GETOBJECT
  dd WM_COMPACTING
  dd WM_OTHERWINDOWCREATED
  dd WM_OTHERWINDOWDESTROYED
  dd WM_COMMNOTIFY
  dd WM_WINDOWPOSCHANGING
  dd WM_WINDOWPOSCHANGED
  dd WM_POWER
  dd WM_COPYDATA
  dd WM_CANCELJOURNAL
  dd WM_NOTIFY
  dd WM_INPUTLANGCHANGEREQUEST
  dd WM_INPUTLANGCHANGE
  dd WM_TCARD
  dd WM_HELP
  dd WM_USERCHANGED
  dd WM_NOTIFYFORMAT
  dd WM_CONTEXTMENU
  dd WM_STYLECHANGING
  dd WM_STYLECHANGED
  dd WM_DISPLAYCHANGE
  dd WM_GETICON
  dd WM_SETICON
  dd WM_NCCREATE
  dd WM_NCDESTROY
  dd WM_NCCALCSIZE
  dd WM_NCHITTEST
  dd WM_NCPAINT
  dd WM_NCACTIVATE
  dd WM_GETDLGCODE
  dd WM_SYNCPAINT
  dd WM_NCMOUSEMOVE
  dd WM_NCLBUTTONDOWN
  dd WM_NCLBUTTONUP
  dd WM_NCLBUTTONDBLCLK
  dd WM_NCRBUTTONDOWN
  dd WM_NCRBUTTONUP
  dd WM_NCRBUTTONDBLCLK
  dd WM_NCMBUTTONDOWN
  dd WM_NCMBUTTONUP
  dd WM_NCMBUTTONDBLCLK
  dd WM_KEYDOWN
  dd WM_KEYUP
  dd WM_CHAR
  dd WM_DEADCHAR
  dd WM_SYSKEYDOWN
  dd WM_SYSKEYUP
  dd WM_SYSCHAR
  dd WM_SYSDEADCHAR
  dd WM_KEYLAST
  dd WM_INITDIALOG
  dd WM_COMMAND
  dd WM_SYSCOMMAND
  dd WM_TIMER
  dd WM_HSCROLL
  dd WM_VSCROLL
  dd WM_INITMENU
  dd WM_INITMENUPOPUP
  dd WM_MENUSELECT
  dd WM_MENUCHAR
  dd WM_ENTERIDLE
  dd WM_CTLCOLORMSGBOX
  dd WM_CTLCOLOREDIT
  dd WM_CTLCOLORLISTBOX
  dd WM_CTLCOLORBTN
  dd WM_CTLCOLORDLG
  dd WM_CTLCOLORSCROLLBAR
  dd WM_CTLCOLORSTATIC
  dd WM_MOUSEMOVE
  dd WM_LBUTTONDOWN
  dd WM_LBUTTONUP
  dd WM_LBUTTONDBLCLK
  dd WM_RBUTTONDOWN
  dd WM_RBUTTONUP
  dd WM_RBUTTONDBLCLK
  dd WM_MBUTTONDOWN
  dd WM_MBUTTONUP
  dd WM_MBUTTONDBLCLK
  dd WM_MOUSELAST
  dd WM_PARENTNOTIFY
  dd WM_ENTERMENULOOP
  dd WM_EXITMENULOOP
  dd WM_MDICREATE
  dd WM_MDIDESTROY
  dd WM_MDIACTIVATE
  dd WM_MDIRESTORE
  dd WM_MDINEXT
  dd WM_MDIMAXIMIZE
  dd WM_MDITILE
  dd WM_MDICASCADE
  dd WM_MDIICONARRANGE
  dd WM_MDIGETACTIVE
  dd WM_MDISETMENU
  dd WM_DROPFILES
  dd WM_MDIREFRESHMENU
  dd WM_CUT
  dd WM_COPY
  dd WM_PASTE
  dd WM_CLEAR
  dd WM_UNDO
  dd WM_RENDERFORMAT
  dd WM_RENDERALLFORMATS
  dd WM_DESTROYCLIPBOARD
  dd WM_DRAWCLIPBOARD
  dd WM_PAINTCLIPBOARD
  dd WM_VSCROLLCLIPBOARD
  dd WM_SIZECLIPBOARD
  dd WM_ASKCBFORMATNAME
  dd WM_CHANGECBCHAIN
  dd WM_HSCROLLCLIPBOARD
  dd WM_QUERYNEWPALETTE
  dd WM_PALETTEISCHANGING
  dd WM_PALETTECHANGED
  dd WM_HOTKEY
  dd WM_PRINT
  dd WM_PRINTCLIENT
  dd WM_PENWINFIRST
  dd WM_PENWINLAST
  dd WM_MENURBUTTONUP
  dd WM_MENUDRAG
  dd WM_MENUGETOBJECT
  dd WM_UNINITMENUPOPUP
  dd WM_MENUCOMMAND
  dd WM_NEXTMENU
  dd WM_SIZING
  dd WM_CAPTURECHANGED
  dd WM_MOVING
  dd WM_POWERBROADCAST
  dd WM_DEVICECHANGE
  dd WM_ENTERSIZEMOVE
  dd WM_EXITSIZEMOVE
MSG_TABLE_LEN equ ($ - dwMsgTable)/sizeof dword
;********************************************************************
; 消息名称字符串列表
;********************************************************************
MSG_STRING_LEN equ sizeof szStringTable
szStringTable db 'WM_NULL                  ',0
  db 'WM_CREATE                ',0
  db 'WM_DESTROY               ',0
  db 'WM_MOVE                  ',0
  db 'WM_SIZE                  ',0
  db 'WM_ACTIVATE              ',0
  db 'WM_SETFOCUS              ',0
  db 'WM_KILLFOCUS             ',0
  db 'WM_ENABLE                ',0
  db 'WM_SETREDRAW             ',0
  db 'WM_SETTEXT               ',0
  db 'WM_GETTEXT               ',0
  db 'WM_GETTEXTLENGTH         ',0
  db 'WM_PAINT                 ',0
  db 'WM_CLOSE                 ',0
  db 'WM_QUERYENDSESSION       ',0
  db 'WM_QUIT                  ',0
  db 'WM_QUERYOPEN             ',0
  db 'WM_ERASEBKGND            ',0
  db 'WM_SYSCOLORCHANGE        ',0
  db 'WM_ENDSESSION            ',0
  db 'WM_SHOWWINDOW            ',0
  db 'WM_WININICHANGE          ',0
  db 'WM_DEVMODECHANGE         ',0
  db 'WM_ACTIVATEAPP           ',0
  db 'WM_FONTCHANGE            ',0
  db 'WM_TIMECHANGE            ',0
  db 'WM_CANCELMODE            ',0
  db 'WM_SETCURSOR             ',0
  db 'WM_MOUSEACTIVATE         ',0
  db 'WM_CHILDACTIVATE         ',0
  db 'WM_QUEUESYNC             ',0
  db 'WM_GETMINMAXINFO         ',0
  db 'WM_PAINTICON             ',0
  db 'WM_ICONERASEBKGND        ',0
  db 'WM_NEXTDLGCTL            ',0
  db 'WM_SPOOLERSTATUS         ',0
  db 'WM_DRAWITEM              ',0
  db 'WM_MEASUREITEM           ',0
  db 'WM_DELETEITEM            ',0
  db 'WM_VKEYTOITEM            ',0
  db 'WM_CHARTOITEM            ',0
  db 'WM_SETFONT               ',0
  db 'WM_GETFONT               ',0
  db 'WM_SETHOTKEY             ',0
  db 'WM_GETHOTKEY             ',0
  db 'WM_QUERYDRAGICON         ',0
  db 'WM_COMPAREITEM           ',0
  db 'WM_GETOBJECT             ',0
  db 'WM_COMPACTING            ',0
  db 'WM_OTHERWINDOWCREATED    ',0
  db 'WM_OTHERWINDOWDESTROYED  ',0
  db 'WM_COMMNOTIFY            ',0
  db 'WM_WINDOWPOSCHANGING     ',0
  db 'WM_WINDOWPOSCHANGED      ',0
  db 'WM_POWER                 ',0
  db 'WM_COPYDATA              ',0
  db 'WM_CANCELJOURNAL         ',0
  db 'WM_NOTIFY                ',0
  db 'WM_INPUTLANGCHANGEREQUEST',0
  db 'WM_INPUTLANGCHANGE       ',0
  db 'WM_TCARD                 ',0
  db 'WM_HELP                  ',0
  db 'WM_USERCHANGED           ',0
  db 'WM_NOTIFYFORMAT          ',0
  db 'WM_CONTEXTMENU           ',0
  db 'WM_STYLECHANGING         ',0
  db 'WM_STYLECHANGED          ',0
  db 'WM_DISPLAYCHANGE         ',0
  db 'WM_GETICON               ',0
  db 'WM_SETICON               ',0
  db 'WM_NCCREATE              ',0
  db 'WM_NCDESTROY             ',0
  db 'WM_NCCALCSIZE            ',0
  db 'WM_NCHITTEST             ',0
  db 'WM_NCPAINT               ',0
  db 'WM_NCACTIVATE            ',0
  db 'WM_GETDLGCODE            ',0
  db 'WM_SYNCPAINT             ',0
  db 'WM_NCMOUSEMOVE           ',0
  db 'WM_NCLBUTTONDOWN         ',0
  db 'WM_NCLBUTTONUP           ',0
  db 'WM_NCLBUTTONDBLCLK       ',0
  db 'WM_NCRBUTTONDOWN         ',0
  db 'WM_NCRBUTTONUP           ',0
  db 'WM_NCRBUTTONDBLCLK       ',0
  db 'WM_NCMBUTTONDOWN         ',0
  db 'WM_NCMBUTTONUP           ',0
  db 'WM_NCMBUTTONDBLCLK       ',0
  db 'WM_KEYDOWN               ',0
  db 'WM_KEYUP                 ',0
  db 'WM_CHAR                  ',0
  db 'WM_DEADCHAR              ',0
  db 'WM_SYSKEYDOWN            ',0
  db 'WM_SYSKEYUP              ',0
  db 'WM_SYSCHAR               ',0
  db 'WM_SYSDEADCHAR           ',0
  db 'WM_KEYLAST               ',0
  db 'WM_INITDIALOG            ',0
  db 'WM_COMMAND               ',0
  db 'WM_SYSCOMMAND            ',0
  db 'WM_TIMER                 ',0
  db 'WM_HSCROLL               ',0
  db 'WM_VSCROLL               ',0
  db 'WM_INITMENU              ',0
  db 'WM_INITMENUPOPUP         ',0
  db 'WM_MENUSELECT            ',0
  db 'WM_MENUCHAR              ',0
  db 'WM_ENTERIDLE             ',0
  db 'WM_CTLCOLORMSGBOX        ',0
  db 'WM_CTLCOLOREDIT          ',0
  db 'WM_CTLCOLORLISTBOX       ',0
  db 'WM_CTLCOLORBTN           ',0
  db 'WM_CTLCOLORDLG           ',0
  db 'WM_CTLCOLORSCROLLBAR     ',0
  db 'WM_CTLCOLORSTATIC        ',0
  db 'WM_MOUSEMOVE             ',0
  db 'WM_LBUTTONDOWN           ',0
  db 'WM_LBUTTONUP             ',0
  db 'WM_LBUTTONDBLCLK         ',0
  db 'WM_RBUTTONDOWN           ',0
  db 'WM_RBUTTONUP             ',0
  db 'WM_RBUTTONDBLCLK         ',0
  db 'WM_MBUTTONDOWN           ',0
  db 'WM_MBUTTONUP             ',0
  db 'WM_MBUTTONDBLCLK         ',0
  db 'WM_MOUSELAST             ',0
  db 'WM_PARENTNOTIFY          ',0
  db 'WM_ENTERMENULOOP         ',0
  db 'WM_EXITMENULOOP          ',0
  db 'WM_MDICREATE             ',0
  db 'WM_MDIDESTROY            ',0
  db 'WM_MDIACTIVATE           ',0
  db 'WM_MDIRESTORE            ',0
  db 'WM_MDINEXT               ',0
  db 'WM_MDIMAXIMIZE           ',0
  db 'WM_MDITILE               ',0
  db 'WM_MDICASCADE            ',0
  db 'WM_MDIICONARRANGE        ',0
  db 'WM_MDIGETACTIVE          ',0
  db 'WM_MDISETMENU            ',0
  db 'WM_DROPFILES             ',0
  db 'WM_MDIREFRESHMENU        ',0
  db 'WM_CUT                   ',0
  db 'WM_COPY                  ',0
  db 'WM_PASTE                 ',0
  db 'WM_CLEAR                 ',0
  db 'WM_UNDO                  ',0
  db 'WM_RENDERFORMAT          ',0
  db 'WM_RENDERALLFORMATS      ',0
  db 'WM_DESTROYCLIPBOARD      ',0
  db 'WM_DRAWCLIPBOARD         ',0
  db 'WM_PAINTCLIPBOARD        ',0
  db 'WM_VSCROLLCLIPBOARD      ',0
  db 'WM_SIZECLIPBOARD         ',0
  db 'WM_ASKCBFORMATNAME       ',0
  db 'WM_CHANGECBCHAIN         ',0
  db 'WM_HSCROLLCLIPBOARD      ',0
  db 'WM_QUERYNEWPALETTE       ',0
  db 'WM_PALETTEISCHANGING     ',0
  db 'WM_PALETTECHANGED        ',0
  db 'WM_HOTKEY                ',0
  db 'WM_PRINT                 ',0
  db 'WM_PRINTCLIENT           ',0
  db 'WM_PENWINFIRST           ',0
  db 'WM_PENWINLAST            ',0
  db 'WM_MENURBUTTONUP         ',0
  db 'WM_MENUDRAG              ',0
  db 'WM_MENUGETOBJECT         ',0
  db 'WM_UNINITMENUPOPUP       ',0
  db 'WM_MENUCOMMAND           ',0
  db 'WM_NEXTMENU              ',0
  db 'WM_SIZING                ',0
  db 'WM_CAPTURECHANGED        ',0
  db 'WM_MOVING                ',0
  db 'WM_POWERBROADCAST        ',0
  db 'WM_DEVICECHANGE          ',0
  db 'WM_ENTERSIZEMOVE         ',0
  db 'WM_EXITSIZEMOVE          ',0
;********************************************************************
szDestClass db 'Notepad',0
szFormat db 'WndProc: [%04x]%s %08x %08x',0dh,0
szCreateWindow1 db 'Creating Window...',0dh,0
szCreateWindow2 db 'CreateWindow end',0dh,0
szShowWindow1 db 'Showing Window...',0dh,0
szShowWindow2 db 'ShowWindow end',0dh,0
szUpdateWindow1 db 'Updating Window...',0dh,0
szUpdateWindow2 db 'UpdateWindow end',0dh,0
szGetMsg1 db 'Getting Message...',0dh,0
szGetMsg2 db '[%04x]Message gotten',0dh,0
szDispatchMsg1 db 'Dispatching Message...',0dh,0
szDispatchMsg2 db 'DispatchMessage end',0dh,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  .code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_SendtoNotepad proc _lpsz
  local @hWinNotepad
  pushad
  invoke FindWindow,addr szDestClass,NULL
  .if eax
   mov ecx,eax
   invoke ChildWindowFromPoint,ecx,20,20
  .endif
  .if eax
   mov @hWinNotepad,eax
   mov esi,_lpsz
   @@:
   lodsb
   or al,al
   jz @F
   movzx eax,al
   invoke PostMessage,@hWinNotepad,WM_CHAR,eax,1
   jmp @B
   @@:
  .endif
  popad
  ret
_SendtoNotepad endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ShowMessage proc _uMsg,_wParam,_lParam
  local @szBuffer[128]:byte
  pushad
;********************************************************************
; 查找消息的说明字符串
;********************************************************************
  mov eax,_uMsg
  mov edi,offset dwMsgTable
  mov ecx,MSG_TABLE_LEN
  cld
  repnz scasd
  .if ZERO?
   sub edi,offset dwMsgTable + sizeof dword
   shr edi,2
   mov eax,edi
   mov ecx,MSG_STRING_LEN
   mul ecx
   add eax,offset szStringTable
;********************************************************************
; 翻译格式并发送到 Notepad 窗口
;********************************************************************
   invoke wsprintf,addr @szBuffer,addr szFormat,\
    _uMsg,eax,_wParam,_lParam
   invoke _SendtoNotepad,addr @szBuffer
  .endif
  popad
  ret
_ShowMessage endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam
  invoke _ShowMessage,uMsg,wParam,lParam
  mov eax,uMsg
;********************************************************************
  .if eax == WM_CLOSE
   invoke DestroyWindow,hWinMain
   invoke PostQuitMessage,NULL
;********************************************************************
  .else
   invoke DefWindowProc,hWnd,uMsg,wParam,lParam
   ret
  .endif
;********************************************************************
  xor eax,eax
  ret
_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
  local @szBuffer[128]:byte
  local @stWndClass:WNDCLASSEX
  local @stMsg:MSG
  invoke GetModuleHandle,NULL
  mov hInstance,eax
  invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;********************************************************************
; 注册窗口类
;********************************************************************
  invoke LoadCursor,0,IDC_ARROW
  mov @stWndClass.hCursor,eax
  push hInstance
  pop @stWndClass.hInstance
  mov @stWndClass.cbSize,sizeof WNDCLASSEX
  mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
  mov @stWndClass.lpfnWndProc,offset _ProcWinMain
  mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
  mov @stWndClass.lpszClassName,offset szClassName
  invoke RegisterClassEx,addr @stWndClass
;********************************************************************
; 建立并显示窗口
;********************************************************************
  invoke _SendtoNotepad,addr szCreateWindow1
  invoke CreateWindowEx,WS_EX_CLIENTEDGE ,offset szClassName,offset szCaptionMain,\
   WS_OVERLAPPEDWINDOW,\
   50,50,100,100,\
   NULL,NULL,hInstance,NULL
  mov hWinMain,eax
  invoke _SendtoNotepad,addr szCreateWindow2
  invoke _SendtoNotepad,addr szShowWindow1
  invoke ShowWindow,hWinMain,SW_SHOWNORMAL
  invoke _SendtoNotepad,addr szShowWindow2
  invoke _SendtoNotepad,addr szUpdateWindow1
  invoke UpdateWindow,hWinMain
  invoke _SendtoNotepad,addr szUpdateWindow2
;********************************************************************
; 消息循环
;********************************************************************
  .while TRUE
   invoke _SendtoNotepad,addr szGetMsg1
   invoke GetMessage,addr @stMsg,NULL,0,0
   push eax
   invoke wsprintf,addr @szBuffer,addr szGetMsg2,@stMsg.message
   invoke _SendtoNotepad,addr @szBuffer
   pop eax
   .break .if eax == 0
   invoke TranslateMessage,addr @stMsg
   invoke _SendtoNotepad,addr szDispatchMsg1
   invoke DispatchMessage,addr @stMsg
   invoke _SendtoNotepad,addr szDispatchMsg2
  .endw
  ret
_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
  call _WinMain
  invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  end start

2005年09月22日

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Hello.asm
; 使用 Win32ASM 写的 Hello, world 程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff Hello.asm
; Link /subsystem:windows Hello.obj
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.386
.model flat,stdcall
option casemap:none

include  windows.inc
include  user32.inc
includelib user32.lib
include  kernel32.inc
includelib kernel32.lib

  .data

szCaption db ‘A MessageBox !’,0
szText  db ‘Hello, World !’,0

  .code
start:
  invoke MessageBox,NULL,offset szText,offset szCaption,MB_YESNO
  invoke ExitProcess,NULL
end start

  .386  ;指定指令集,若是带有.386p的话,则表示可使用特权指令 

 .model flat,stdcall ;定义程序的工作模式,对于Win32程序来说,只有一种内存模式,即flat模式。如果定义了 .model flat,MASM 自动为各种段寄存器做了如下定义(Assume cs:FlAT,ds:Flat,ss:FALT,es:FLAT,fs:ERROR,gs:ERROR)。也就是说,CS,DS,ES 和SS 段全部使用平坦模式,FS 和GS 寄存器默认不使用,这时若在源程序中使用FS 或GS,在编译时会报错。如果有必要使用它们,只需在使用前用下面的语句声明一下就可以了(Assume fs:nothing,gs:nothing或者是Assume fs:flat,gs:flat)。而stdcall指出了调用子程序或Win32 API 时参数传递的次序和堆栈平衡的方法,相对于stdcall,不同的语言类型还有C,SysCall,BASIC,FORTRAN 和PASCAL,虽然各种高级语言在调用子程序时都是使用堆栈来传递参数,但它们的处理方法各有不同。
  option casemap:none;用option 语句定义的选项有很多,如option language 定义和option segment 定义等,在Win32 汇编程序中,需要的只是定义option casemap:none,这个语句定义了程序中的变量和子程序名是否对大小写敏感,由于Win32 API 中的API 名称是区分大小写的,所以必须指定这个选项,否则在调用API 的时候会有问题。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include  windows.inc;inc文件包含一些函数声明
include  user32.inc
includelib user32.lib ;告诉链接程序使用了哪些函数库
include  kernel32.inc
includelib kernel32.lib

;为了使程序更有移植性,在源程序中一般不直接指明使用Unicode 还是ANSI 版本,而是使用宏汇编中的条件汇编功能来统一替换,如在源程序中使用Messagebox,但在头文件中定义:
if UNICODE
MessageBox equ <MessageBoxW>
else
MessageBox equ <MessageBoxA>
endif
所有涉及版本问题的API 都可以按此方法定义,然后在源程序的头部指定UNICODE=1 或UNICODE=0,重新编译后就能产生不同的版本。

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;Win32 中实际上只有代码和数据之分,.data.data?.const 是数据段.code 是代码段,和DOS 汇编不同,Win32 汇编不必考虑堆栈,系统会为程序分配一个向下扩展的、足够大的段作为堆栈段,所以 .stack 段定义常常被忽略。

;程序中的数据定义一般可以归纳为3 类:

第一类是可读可写的已定义变量。这些数据在源程序中已经被定义了初始值,而且在程序的执行中有可能被更改,如一些标志等,这些数据必须定义在 .data 段中,.data 段是已初始化数据段,其中定义的数据是可读可写的,在程序装入完成的时候,这些值就已经在内存中了.data 段存放在可执行文件的_DATA 节区内。

第二类是可读可写的未定义变量。这些变量一般是当做缓冲区或者在程序执行后才开始使用的,这些数据可以定义在 .data 段中,也可以定义在 .data?段中,但一般把它放到 .data?段中。虽然定义在这两种段中都可以正常使用,但定义在 .data?段中不会增大 .exe 文件的大小。

第三类数据是一些常量。如一些要显示的字符串信息,它们在程序装入的时候也已经有效,但在整个执行过程中不需要修改,这些数据可以放在 .const 段中.const 段是常量段,它是可读不可写的。。一般为了方便起见,在小程序中常常把常量一起定义到 .data 段中,而不另外定义一 .const 段。在程序中如果不小心写了对 .const 段中的数据做写操作的指令,会引起保护错误,Windows 会显示一个如图3.2 所示的提示框并结束程序。

;如果不怕程序可读性不佳的话,把 .const 段中定义的东西混到 .code 段中去也可以正常
使用,因为 .code 段也是可以读的。

  .data

szCaption db ’A MessageBox !’,0
szText  db ’Hello, World !’,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;.code 段是代码段,所有的指令都必须写在代码段中,在可执行文件中,代码段是放在_TEXT 节区中的。Win32 环境中的数据段是不可执行的,只有代码段有可执行的属性。对于工作在特权级3 的应用程序来说.code 段是不可写的。
  .code
start: ;相当于程序入口

;一个源程序不必非要指定入口标号,这时候可以把开始地址忽略不写,这种情况发生在编写多模块程序的单个模块的时候。当分开写多个程序模块时,每个模块的源程序中也可以包括 .data.data?.const .code 段,结构就和上面的Win32 Hello World 一样,只是其他模块最后的end 语句必须不带开始地址。当最后把多个模块链接在一起的时候,只能有一个主模块指定入口地址,在多个模块中指定入口地址或者没有一个模块指定了入口地址,链接程序都会报错。
  invoke MessageBox,NULL,offset szText,offset szCaption,MB_YESNO
  invoke ExitProcess,NULL ;利用invoke伪指令,实现调用API
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  end start ;所有的代码都必须在end 之前完成

   

变量的类型
 字节  byte  db  1
   word  dw  2
 双字  dword  dd  4
 三字  fword  df  6
 四字   qword  dq  8
 十字节BCD 码   tbyte  dt  10
 有符号字节  signbyte  sbyte  1
 有符号字  signword  sword  2
 有符号双字  signdword  sdword  4
 单精度浮点数   real4  real4  4
 双精度浮点数   real8  real8  8
 10 字节浮点数  real10  real10  10

         全局变量的作用域是整个程序,Win32 汇编的全局变量定义在 .data.data?段内,可以
同时定义变量的类型和长度,格式是:
变量名 类型 初始值1,初始值2,……
变量名 类型 重复数量 dup (初始值1,初始值2,……)

         所有使用到变量类型的情况中,只有定义全局变量的时候类型才可以用缩写

         全局变量在定义中既可以指定初值,也可以只用问号预留空间,在 .data?段中,只能用
问号预留空间,因为 .data?不能指定初始值,这里就有一个问题:既然可以用问号预留空间,
那么在实际运行的时候,这个未初始化的值是随机的还是确定的呢?在全局变量中,这个值
就是0,所以用问号指定的全局变量如果要以0 为初始值的话,在程序中可以不必为它赋值。

         局部变量的作用域是单个子程序,在进入子程序的时候,通过修改堆栈指针esp 来预留
出需要的空间,在用ret 指令返回主程序之前,同样通过恢复esp 丢弃这些空间,这些变量就
随之无效了。它的缺点就是因为空间是临时分配的,所以无法定义含有初始化值的变量,对
局部变量的初始化一般在子程序中由指令完成。

         local 变量名1[[重复数量]][:类型],变量名2[[重复数量]][:类型]……

local loc1[1024]:byte ;例1,定义了一个 1 024 字节长的局部变量 loc1。
local loc2 ;例2,
定义了一个名为 loc2 的局部变量,类型是默认值 dword。
local loc3:WNDCLASS ;例3,
定义了一个 WNDCLASS 数据结构,名为 loc3。

         需要注意的是,既然局部变量的空间临时分配的,那么必然需要在堆栈上进行操作,以便完成变量的赋值与释放。下面这两段代码展示了局部变量是如何在堆栈中操作的:

TestProc proc  ;由call调用,这时call指令会把返回地址堆入栈(返回地址存放在ebp+4),之后,原来的ebp值也会入栈,地址为ebp
local @loc1:dword,@loc2:word
local @loc3:byte
mov eax,@loc1;第一个局部变量,入栈地址为ebp-4
mov ax,@loc2;第二个局部变量,入栈地址为ebp-6
mov al,@loc3;第三个局部变量,入栈地址为ebp-7
ret
TestProc endp

:00401000 55 push ebp ;保护ebp值,入栈
:00401001 8BEC mov ebp, esp ;由于进入子程序后,esp值经常会被改变,故程序使用ebp作指针
:00401003 83C4F8 add esp, FFFFFFF8
:00401006 8B45FC mov eax, dword ptr [ebp-04]
:00401009 668B45FA mov ax, word ptr [ebp-06]
:0040100D 8A45F9 mov al, byte ptr [ebp-07]
:00401010 C9 leave ;伪指令,完成mov esp,ebp,pop ebp操作
:00401011 C3 ret

         程序退出的时候,必须把正确的esp 设置回去,否则,ret 指令会从堆栈中取出错误的
地址返回,看程序可以发现,ebp 就是正确的esp 值,因为子程序开始的时候已经有一句mov
ebp,esp
,所以要返回的时候只要先mov esp,ebp,然后再pop ebp,堆栈就是正确的了。
         在80386 指令集中有一条指令可以在一句中实现这个功能,就是leave 指令,所以,编译器在ret 指令之前只使用了一句leave 指令。明白了局部变量使用的原理,就很容易理解使用时的注意点:ebp 寄存器是关键,它起到保存原始esp 的作用,并随时用做存取局部变量的指针基址,所以在任何时刻,不要尝试把ebp 用于别的用途,否则会带来意想不到的后果。
         Win32 汇编中局部变量的使用方法可以解释一个很有趣的现象:在DOS 汇编的时候,如果在子程序中的push 指令和pop 指令不配对,那么返回的时候ret 指令从堆栈里得到的肯定是错误的返回地址,程序也就死掉了。但在Win32 汇编中,push 指令和pop指令不配对可能在逻辑上产生错误,却不会影响子程序正常返回,原因就是在返回的时候esp 不是靠相同数量的pushpop 指令来保持一致的,而是靠leave 指令从保存在ebp中的原始值中取回来的,也就是说,即使把esp 改得一塌糊涂也不会影响到子程序的返回,当然,"窍门"就在ebp,把ebp 改掉,程序就玩完了!

         局部变量是无法在定义的时候指定初始化值的,因为local 伪指令只是简单地把空间给留出来,那么开始使用时它里面是什么值呢?和全局变量不一样,局部变量的起始值是随机的,是其他子程序执行后在堆栈里留下的垃圾,所以,对局部变量的值一定要初始化,特别是定义为结构后当参数传递给API 函数的时候。

sizeof 伪指令可以取得变量数据类型数据结构以字节为单位的长度,lengthof 可以取得变量中数据的项数;注意二者不同的使用范围!

如果为了把Hello 和World 分两行定义,szHello 是这样定义的:
szHello db ‘Hello’,0dh,0ah
db ‘World’,0

那么sizeof szHello 是多少呢?注意!是7 而不是13,MASM 中的变量定义只认一行,后一行db ‘World’,0 实际上是另一个没有名称的数据定义,编译器认为sizeof szHello 是第一行字符的数量。虽然把szHello 的地址当参数传给MessageBox 等函数显示时会把两行都显示出来,但严格地说这是越界使用变量。虽然在实际的应用中这样定义长字符串的用法很普遍,因为如果要显示一屏幕帮助,一行是不够的,但要注意的是:要用到这种字符串的长度时,千万不要用sizeof 去表示,最好是在程序中用lstrlen 函数去计算。

获取变量地址

         对于全局变量,它的地址在编译的时候已经由编译器确定了,它的用法大家都不陌生:
mov 寄存器,offset 变量名
         其中offset 是取变量地址的伪操作符,和sizeof 伪操作符一样,它仅把变量的地址代到指令中去,这个操作是在编译时而不是在运行时完成的。

         但是对于局部变量,它的地址空间是在程序执行时动态给态给定的,所以不可能由offset来取地址!而使用:

lea eax,[ebp-4]
该指令可以在运行时按照ebp 的值实际计算出地址放到eax 中。

如果要在invoke 伪指令的参数中用到一个局部变量的地址,该怎么办呢?参数中是不可
能写入lea 指令的,用offset 又是不对的。MASM 对此有一个专用的伪操作符addr,其格式
为:
addr 局部变量名和全局变量名
addr 后跟全局变量名的时候,用法和offset 是相同的;当addr 后面跟局部变量名的时候,编译器会自动用lea 指令先把地址取到eax 中,然后用eax 来代替变量地址使用。注意addr 伪操作符只能在invoke 的参数中使用,不能用在类似于下列的场合:
mov eax,addr 局部变量名 ;注意:错误用法

假设在一个子程序中有如下invoke 指令:
invoke Test,eax,addr szHello
其中Test 是一个需要两个参数的子程序,szHello 是一个局部变量,会发生什么结果呢?
编译器会把invoke 伪指令和addr 翻译成下面这个模样:
lea eax,[ebp-4]
push eax ;参数2:addr szHello
push eax ;参数1:eax
call Test
发现了什么?到push 第一个参数eax 之前,eax 的值已经被lea eax,[ebp-4]指令覆盖
了!也就是说,要用到的eax 的值不再有效,所以,当在invoke 中使用addr 伪操作符时,
注意在它的前面不能用eax,否则eax 的值会被覆盖掉,当然eax 在addr 的后面的参数中
用是可以的。幸亏MASM 编译器对这种情况有如下错误提示:
error A2133: register value overwritten by INVOKE
否则,不知道又会引出多少莫名其妙的错误!

1、条件测试语句

&&、||、!等各种在C中存在的逻辑符号都可以使用。

除了这些和高级语言类似的条件测试伪操作,汇编语言还有特殊的要求,就是程序中常常要根据系统标志寄存器中的各种标志位来做条件跳转,这些在高级语言中是用不到的,所以又增加了以下一些标志位的状态指示,它们本身相当于一个表达式:
CARRY? 表示Carry 位是否置位
OVERFLOW? 表示Overflow 位是否置位
PARITY? 表示Parity 位是否置位
SIGN? 表示Sign 位是否置位
ZERO? 表示Zero 位是否置位
要测试eax 等于ebx 同时Zero 位置位,条件表达式可以写为:
(eax==ebx) && ZERO?
要测试eax 等于ebx 同时Zero 位清零,条件表达式可以写为:
(eax==ebx) && ! ZERO?

.if 条件表达式1
表达式1 为“真”时执行的指令
[.elseif 条件表达式2]
表达式2 为“真”时执行的指令
[.elseif 条件表达式3]
表达式3 为“真”时执行的指令

[.else]
所有表达式为“否”时执行的指令
.endif

注意:关键字if/elseif/else/endif 的前面有个小数点,如果不加小数点,就变成宏汇编中
的条件汇编伪操作了,结果可是天差地别。

.while 条件测试表达式
指令
[.break [.if 退出条件]]
[.continue]
.endw


.repeat
指令
[.break [.if 退出条件]]
[.continue]
.until 条件测试表达式 (或.untilcxz [条件测试表达式])

        .while/.endw 循环首先判断条件测试表达式,如果结果是"真",则执行循环体内的指令,结束后再回到 .while 处判断表达式,如此往复,一直到表达式结果为"假"为止.while/.endw指令有可能一遍也不会执行到循环体内的指令,因为如果第一次判断表达式时就遇到结果为"假"的情况,那么就直接退出循环。
        .repeat/.until 循环首先执行一遍循环体内的指令,然后再判断条件测试表达式,如果结果为"真"的话,就退出循环,如果为"假",则返回 .repeat 处继续循环,可以看出.repeat/.until不管表达式的值如何,至少会执行一遍循环体内的指令。
        也可以把条件表达式直接设置为固定值,这样就可以构建一个无限循环,对于.while/.end直接使用TRUE,对 .repeat/.until 直接使用FALSE 来当表达式就是如此,这种情况下,可以使用 .break 伪指令强制退出循环,如果 .break 伪指令后面跟一个 .if 测试伪指令的话,那么当退出条件为"真"时才执行 .break 伪指令。
        在循环体中也可以用 .continue 伪指令忽略以后的指令,遇到 .continue 伪指令时,不管下面还有没有其他循环体中的指令,都会直接回到循环头部开始执行。

2005年09月01日

windows日志的保护与伪造

原创:netone(cexo)

windows日志的保护与伪造
日志对于系统安全的作用是显而易见的,无论是网络管理员还是黑客都非常重视日志,一个有经验的管理员往往能够迅速通过日志了解到系统的安全性能,而一个聪明的黑客往往会在入侵成功后迅速清除掉对自己不利的日志。下面我们就来讨论一下日志的安全和创建问题。
一:概述:Windows2000的系统日志文件有应用程序日志,安全日志、系统日志、DNS服务器日志等等,应用程序日志、安全日志、系统日志、DNS日志默认位置:%systemroot%\system32\config,默认文件大小512KB。
安全日志文件:%systemroot%\system32\config\SecEvent.EVT
系统日志文件:%systemroot%\system32\config\SysEvent.EVT
应用程序日志文件:%systemroot%\system32\config\AppEvent.EVT
这些LOG文件在注册表中的:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog
有的管理员很可能将这些日志重定位。其中EVENTLOG下面有很多的子表,里面可查到以上日志的定位目录。

 

二:作为网络管理员:
1。日志的安全配置:
默认的条件下,日志的大小为512KB大小,如果超出则会报错,并且不会再记录任何日志。所以首要任务是更改默认大小,具体方法:注册表中HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog对应的每个日志如系统,安全,应用程序等均有一个maxsize子键,修改即可。
下面给出一个来自微软站点的一个脚本,利用VMI来设定日志最大25MB,并允许日志自行覆盖14天前的日志:
该脚本利用的是WMI对象, WMI(Windows Management Instrumentation)技术是微软提供的Windows下的系统管理工具。通过该工具可以在本地或者管理客户端系统中几乎一切的信息。很多专业的网络管理工具都是基于WMI开发的。该工具在Win2000以及WinNT下是标准工具,在Win9X下是扩展安装选项。所以以下的代码在2000以上均可运行成功。

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate,(Security)}!\\" & _
strComputer & "\root\cimv2") ‘获得VMI对象
Set colLogFiles = objWMIService.ExecQuery _
("Select * from Win32_NTEventLogFile")
For each objLogfile in colLogFiles
strLogFileName = objLogfile.Name
Set wmiSWbemObject = GetObject _
("winmgmts:{impersonationLevel=Impersonate}!\\.\root\cimv2:" _
& "Win32_NTEventlogFile.Name=’" & strLogFileName & "’")
wmiSWbemObject.MaxFileSize = 2500000000
wmiSWbemObject.OverwriteOutdated = 14
wmiSWbemObject.Put_
Next
将上述脚本用记事本存盘为vbs为后缀的即可使用。
另外需要说明的是代码中的strComputer="."在windows脚本中的含义相当于localhost,如果要在远程主机上执行代码,只需要把"."改动为主机名,当然首先得拥有对方主机的管理员权限并建立IPC连接.本文中的代码所出现的strComputer均可作如此改动.
2。日志的查询与备份:
一个优秀的管理员是应该养成备份日志的习惯,如果有条件的话还应该把日志转存到备份机器上或直接转储到打印机上,笔者还有一篇文章《利用脚本编程格式化输出系统日志》,详细的讲述了利用windows脚本把日志转储并输出成html页已便于查询,有兴趣的可以查看,在这里推荐微软的resourceKit工具箱中的dumpel.exe,他的常用方法:
dumpel -f filename -s \\server -l log
-f filename 输出日志的位置和文件名
-s \\server 输出远程计算机日志
-l log log 可选的为system,security,application,可能还有别的如DNS等
如要把目标服务器server上的系统日志转存为backupsystem.log可以用以下格式:
dumpel \\server -l system -f backupsystem.log
再利用计划任务可以实现定期备份系统日志。
另外利用脚本编程的VMI对象也可以轻而易举的实现日志备份:
下面给出备份application日志的代码:
backuplog.vbs
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate,(Backup)}!\\" & _
strComputer & "\root\cimv2") ‘获得 VMI对象
Set colLogFiles = objWMIService.ExecQuery _
("Select * from Win32_NTEventLogFile where LogFileName=’Application’") ‘获取日志对象中的应用程序日志
For Each objLogfile in colLogFiles
errBackupLog = objLogFile.BackupEventLog("f:\application.evt") ‘将日志备份为f:\application.evt
If errBackupLog <> 0 Then
Wscript.Echo "The Application event log could not be backed up."
else Wscript.Echo "success backup log"
End If
Next
程序说明:如果备份成功将窗口提示:"success backup log" 否则提示:"The Application event log could not be backed up",此处备份的日志为application 备份位置为f:\application.evt,可以自行修改,此处备份的格式为evt的原始格式,用记事本打开则为乱码,这一点他不如dumpel用得方便。


三:作为黑客
1。日至清除
一个入侵系统成功后的黑客第一件事便是清除日志,如果以图形界面远程控制对方机器或是从终端登陆进入,删除日志不是一件困难的事,由于日志虽然也是作为一种服务运行,但不同于http,ftp这样的服务,可以在命令行下先停止,再删除,在m命令行下用net stop eventlog是不能停止的,所以有人认为在命令行下删除日志是很困难的,实际上不是这样,下面介绍几种方法:
1.借助第三方工具:如小榕的elsave.exe远程清除system,applicaton,security的软件,使用方法很简单,首先利用获得的管理员账号与对方建立ipc会话,net use \\ip pass /user: user
然后命令行下:elsave -s \\ip -l application -C,这样就删除了安全日志。
其实利用这个软件还可以进行备份日志,只要加一个参数 -f filename就可以了,在此不再详述。
2.利用脚本编程中的VMI,也可以实现删除日志,首先获得object对象,然后利用其clearEventLog() 方法删除日志。源代码:
cleanevent.vbs
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate,(Backup)}!\\" & _
strComputer & "\root\cimv2")
dim mylogs(3)
mylogs(1)="application"
mylogs(2)="system"
mylogs(3)="security"
for Each logs in mylogs
Set colLogFiles = objWMIService.ExecQuery _
("Select * from Win32_NTEventLogFile where LogFileName=’"&logs&"’")
For Each objLogfile in colLogFiles
objLogFile.ClearEventLog()
Next
next
在上面的代码中,建立一个数组,为application,security,system如果还有其他日志也可以加入数组。
然后用一个for 循环,删除数组中的每一个元素,即各个日志.
2。创建日志:
删除日志后,任何一个有头脑的管理员面对空空的日志,马上就会反应过来被入侵了,所以一个聪明的黑客的学会如何
伪造日志:
1。利用脚本编程中的eventlog方法是创造日志变得非常简单;下面看一个代码
createlog.vbs
set ws=wscript.createobject("Wscript.shell")
ws.logevent 0 ,"write log success" ‘创建一个成功执行日志
这个代码很容易阅读,首先获得wscript的一个shell对象,然后利用shell对象的logevent方法
logevent的用法:logevent eventtype,"description" [,remote system]
eventtype 为日志类型,可以使用的如下:0 代表成功执行,1 执行出错 ,2 警告 , 4,信息 ,8 成功审计 16 故障审计
所以上面代码中,把0改为1,2,4,8,16均可,引号下的为日志描述。
这种方法写的日志有一个缺点,只能写到应用程序日志,而且日至来源只能为wsh,即windows scripting host,所以不能起太多的隐蔽作用。
2,微软为了方便系统管理员和程序员,在xp下有个新的命令行工具,eventcreate.exe,利用它,创建日志更加简单。
eventcreate -s server -l logname -u username -p password -so source -t eventtype -id id -d description
含义:-s 为远程主机创建日志: -u 远程主机的用户名 -p 远程主机的用户密码
-l 日志;可以创建system和application 不能创建security日志,
-so 日志来源,可以是任何日志 -t 日志类型 如information信息,error错误,warning 警告,
-d 日志描述,可以是任意语句 -id 自主日志为1-1000之内
例如,我们要本地创建一个系统日志,日至来源为admin,日志类型是警告,描述为"this is a test",事件ID为500
可以用如下参数
eventcreate -l system -so administrator -t warning -d "this is a test" -id 500
这个工具不能创建安全日志。至于如何创建安全日志,希望大家能够找到一个好方法!

2005年08月17日

        今天,随便看了看高中同学群里面的一些同学的资料,无意发现一个很久都没有上过线的头像“帅帅鼠”,这才突然想起来——他是一位我已经故去的同学。

       “帅帅鼠”,名叫陈帅。确实也是一个很帅的小伙儿。他是一个为人处世都很不错的男生,即使是我这样一个在别人眼中不怎么好相处的人,也是很佩服他的。但是很可惜,我一直跟他的关系不算太好,至多也就是一个点头之交,到如今他已经不在了,我也再没有机会去结交他。

          今天看到了他的QQ资料,一句话使我久久不能平静——“请相信,我还在你们身边”。当他写下这句话的时候,是怎样一种心情我不得而知。但是在我现在看来,我马上回忆起与他少的可怜的一些交往片断,发现我始终无法对他作出什么深刻的回忆,只是因为他已经不我们的世界了。

         时间的流逝,很可能我以后没有机会看到这份资料,这里来了一个快照。以作纪念。

                                                 ——深刻怀恋陈帅,我的同学

2005年08月12日

来源:网络

文章难度似乎高于CCNA要求,放这里权当参考,可以加深一下这个链路状态路由协议的理解。

DR:designated router.多路访问网络中,为避免router间建立完全相邻关系而引起大量开销,ospf在区域中选举一个DR,每个router都与之建立完全相邻关系。router用hello协议选举一个DR。在广播型网络里,hello报文使用多播地址224.0.0.5周期性广播,并发现邻居。在NBMA中,DR负责向其他路由器逐一发送hello报文。

BMA:广播式多点接入网络。如常见的以太网。在BMA中,必须有一个DR、BDR。
NBMA:非广播式多点接入网络。如FrameRelay,x.25,ATM等。在NBMA中,需要special ospf configuration,并且需要定义neighbor关系,也要有DR、BDR。
点到点网络(ptp):有一个物理上的串行电路连接;或者是逻辑上的,如FR中由VC连接起来。

ptp拓扑中不需要DR or BDR,邻居是自动发现的。
点到多点网络(PtMP):同样不需要DR or BDR。

协议操作:

1、建立邻接关系。
router发送含有自身ID(如loopback端口or最大的IP地址)的hello报文。若是点到点或点到多点网络则跳过下一步,直接进入第三步。若为multiaccess网络,则进入选举步骤。
2、选举DR/BDR。
(不同类型的网络选举DR/BDR的方式不同。)
multiaccess网络,ospf需要建立起作为link state和lsa更新的中心节点。选举利用hello报文内priority字段值和router id确定,这两个均可以直接设置。
3、发现路由。
multiaccess内,DR与BDR互换信息,并同时与本子网内其他路由器交换link state信息。point to piont or point to multipoint网络中,相邻路由器间互换链路状态信息。
4、选择路由。
根据cost,用spf算法决定best路由。
5、维护路由。
当link state变化时,通过flooding过程通告其他路由器,更新数据库,重新计算路由表。另外,ospf路由信息也会自动更新,默认时间30分钟。

OSPF中的四种router:
在ospf多区域网络中,路由器可以按不同的需要同时成为以下四种路由器的几种。
1.内部路由器,所有端口在同一区域的路由器,维护一个link state database。
2.主干路由器,具有连接主干区域端口的路由器。
3.区域边界路由器(ABR),具有连接多区域端口的router,一般作为一个area的出口,ABR为

每个连接的区域建立link state database,负责将所连接的区域的路由摘要信息发送到主干区域,而主干区域上的ABR则负责将这些信息发送到各个区域。
4.自治系统边界路由器ASBR,至少拥有一个连接外部自治域网络端口的路由器,负责将非ospf

网络信息传入ospf网络。

在eigrp中,router会与neighbor交换路由信息。而ospf中,却picky得多,建立了adjacency才会交换。not all the neighbors will become adjacent.不是所有的neighbor都能adjacent.

如果路由器在不同的域有多个接口,路由器会为每个area创建一个单独的树来运行spf运算。if router have multiple interfaces in different areas,then it will contruct separate trees for each area for spf caculation.

ospf中的path metric是bandwidth.(来自于ospf 和rip对照的表)
ospf uses a metric refered to as cost.cisco uses a simple equation of 10^8/

bandwidth.

配置最简单的ospf需要两步:
router(config)#router ospf process-id
router(config-router)#network 10.0.0.0 0.255.255.255 area 0

其中process-id只有本地意义,不同的router可以设成不同的值。
area后的数字可以十进制的,也可以是点分十进制,如0.0.0.0 同于0。//这个是ccna的要求
当一台优先级更高的路由器被加到了网络中,原来的DR和BDR不变,只有原来的DR和BDR失效时才会改变。

OSPF启动过程:
1.交换过程(exchange process) 
当一个路由器A启动时,它处于DOWN状态,它从其各个接口通过224.0.0.5发送HELLO数据包到其它运行OSPF的路由器,其它路由器收到这个H ELLO包后就会把它加入自己的邻居列表中,这叫"init"状态,之后发送一个单点传送回复HELLO包,其中包含着自己的和其它相邻路由器的信息,路由器A 收到这个HELLO后,会把其中有相邻关系数据库加入到自己的库中这叫"two-way"状态,此时就建立了双向通信。
2.发现路由
在选出了DR和BDR之后,路由器就被认为是处于"准启动(exstart)状态",并且已准备好发现有关网络的链路状态信息,以及生成它们的链路状态数据库。用来发现网络路由的这个过程称为交换协议,它被执行来使用权路由器达到通信的" 全(FULL)"状态。在这个协议中的第一步是让DR和BDR建立起与其它各路由器的毗邻关系。当毗邻的路由器处于"全"状态时,它们不会重复执行交换协议,除非" 全"状态发生了变化。 

3.选择路由 
当路由器有了一个完整的链路状态数据库时,它就准备好要创建它的路由表以便能够转发数据流。CISCO路由器上缺省的开销度量是基于网络介质的带宽。要计算到达目的地的最低开销,链路状态型路由选择协议(比如OSPF)采用Dijkstra算法,OSPF路由表中最多保存6条等开销路由条目以进行负载均衡,可以通过"maximum-paths"进行配置。
如果链路上出现fapping翻转,就会使路由器不停的计算一个新的路由表,就可能导致路由器不能收敛。路由器要重新计算客观存它的路由表之前先等一段落时间,缺省值为5 秒。在CISCO配置命令中 "timers spf spf-delay spy-holdtime"可以对两次连续SPF计算之间的最短时间(缺省值10秒)进配置。 
4.维护路由信息 
在链路状态型路由环境中,所有路由器的拓朴结构数据库必须保持同步这一点很重要。当链路状态发生了变化时,路由器通过扩散(flooding)过程将这一变化通知给网络中其他路由器,链路状态更新数据包提供了扩散LSA的技术。各LSA都有有它自己的老化计时器,承载在LS寿命域内。缺省值为30分钟。

修改路由器的优先级:router(config)#ip ospf priority number 
number是1-255的数,缺省是1,0表示不能被选举为DR或BDR. 

修改链路开销要通过"ip ospf cost cost"命令覆盖分配给一个OSPF接口的缺省开销值.


当一个网段上的DR和BDR选择产生后,该网段上的其余所有路由器都只与DR及BDR建立相邻关系。

对于ABR来说,由于一个区域边界路由器同时与几个区域相联,因此一个区域边界路由器上会同时运行几套OSPF计算方法,每一个方法针对一个OSPF区域。
 

2005年08月10日

来源:www.net130.com

hostname Router1 ;路由器名称
enable secret xxxx ;特权访问口令为 xxxx
interface serial 0 ;定义接口
deion To Internet ; 目的描述
ip address 162.70.73.33 255.255.255.248 ;设置IP地址
ip access-list 101 in ; 定义入站过滤器
ip access-list 102 out ;定义出站过滤器
access-list 101 permit tcp any any established Note 1 ;允许所有tcp业务流入,会话始于园区网内
access-list 101 permit tcp any host 144.254.1.3 eq ftp ;允许 ftp 到不洁网(dirty net )中的ftp服务器
access-lsit 101 permit tcp any host 144.254.1.3 eq ftp-data ! ;允许 ftp 数据到不洁网中的ftp服务器
access-list 101 deny ip 127.0.0.0 0.255.255.255 any ;阻止来自Internet并以RFC
access-list 101 deny ip 10.0.0.0 0.255.255.255 any ;保留地址为源的数据包入站
access-list 101 deny ip 172.16.0.0 0.240.255.255 any
access-list 101 deny ip 192.168.0.0 0.0.255.255 any
access-list 101 deny icmp any any echo-reply ;拒绝任何应答
access-list 101 deny icmp any any host-unreachable ;拒绝任何无法接通的主机
access-list 101 deny udp any any eq snmp ;拒绝引入的SNMP
access-list 101 deny udp any eq 2000 ;拒绝引入的openwindows
access-list 101 deny udp any any gt 6000 ;拒绝引入的X-windows
access-list 101 deny tcp any any eq 2000 ; 拒绝引入的openwindows
access-list 101 deny tcp any any gt 6000 ;拒绝引入的X-windows
access-list 101 deny udp any any eq 69 ; 拒绝引入的tftpd
access-list 101 deny udp any any eq 111 ; 拒绝引入的SunRPC
access-list 101 deny udp any any eq 2049 ;拒绝引入的NFS
access-list 101 deny tcp any any eq 111 ; 拒绝引入的SunRPC
access-list 101 deny tcp any any eq 2049 ; 拒绝引入的 NFS
access-list 101 deny tcp any any eq 87 ; 拒绝引入的连接
access-list 101 deny tcp any any eq 512 ; 拒绝引入的 BSD UNIX “r”指令
access-list 101 deny tcp any any eq 513 ; 拒绝引入的 BSD UNIX “r”指令
access-list 101 deny tcp any any eq 514 ; 拒绝引入的 BSD UNIX “r”指令
access-list 101 deny tcp any any eq 515 ; 拒绝引入的 lpd
access-list 101 deny tcp any any eq 540 ; 拒绝引入的 uucpd
access-list 101 permit ip any any ; 其它均允许
access-list 102 permit ip 144.254.0.0 0.0.255.255 any ; 只允许有源的包
access-list 102 deny ip any any ;园区网到Internet的地址

aaa new-model ; 在全范围实现AAA
aaa authentication login default tacacs+ ;默认登录方法经由 tacacs+
aaa authentication login staff tacacs+ local ;通过tacacs+鉴别工作人员用户名… ; 如果无法连接服务器,退而求其次的方法是本地鉴别
aaa authorization exec tacacs+ local ; 鉴别通过后,授权运行 exec shell
aaa authorization commands 0 tacacs+ none ;鉴别与指定特权等级相关的运行模式指令
aaa authorization commands 1 tacacs+ none ; 如果无可用的tacacs+ 服务器,
aaa authorization commands 15 tacacs+ local ; 15级权限指令就需要本地鉴别,其它不需要任何鉴别
aaa accounting update newinfo ; 每当有新的记帐信息需要报告时,中间记帐记录将被送到服务器
aaa accounting exec start-stop tacacs+ ; 对终端会话进行记帐
aaa accounting network start-stop tacacs ; 对所有 PPP, SLIP和ARAP连接记帐
username user1 password 7 user1 ;创建本地口令并以加密格式存储
tacacs-server host 244.252.5.9 ; 定义tacacs+ 服务器地址
tacacs-server key xxxxxxx ; 定义共享的 tacacs+ 密码
line con 0
exec-timeout 5 30 ; 确认控制台会话结束时间
login authentication user1 ;只有用户名工作人员可接入控制台
line aux 0
transport input none ;没有telnet进入
no exec ;该端口没有得到运行提示
line vty 0 3
exec-timeout 5 30 ; 确认 telnet 会话结束时间
login authentication default ; 通过 tacacs+ 登录鉴别
privilege level 15 ; 获得15 级权限
line vty 4
exec-timeout 5 30 ; 确认 telnet 会话结束时间
login authentication user1 ; 鉴别为工作人员
rotary 1
privilege level 1
logging on ; 开启syslog
logging 244.252.5.5 ;定义syslog服务器地址
logging console information ; 定义登录的信息

2005年08月09日

A:You’ve been assigned the CIDR (classless inter domain routing) block of
115.64.4.0/22 from your ISP. Which of the IP addresses below can you use for a
host? (Select all valid answers)
A. 115.64.8.32
B. 115.64.7.64
C. 115.64.6.255
D. 115.64.3.255
E. 115.64.5.128
F. 115.64.12.128
Answer: B, C, E
Explanation:
115.64.4.0 = 01110011.01000000.00000100.00000000
Subnet mask = 11111111.11111111.11111100.00000000= 255.255.252.0
Subnet number = 01110011.01000000.00000100.00000000= 115.64.4.0
Broadcast = 01110011.01000000.00000111.11111111= 115.64.7.255
Valid address range = 115.64.4.1 – 115.64.7.254

B:You have a Class C network, and you need ten subnets. You wish to have as many
addresses available for hosts as possible. Which one of the following subnet masks
should you use?
A. 255.255.255.192
B. 255.255.255.224
C. 255.255.255.240
D. 255.255.255.248
E. None of the above
Answer: C
Explanation:
Using the 2n-2 formula, we will need to use 4 bits for subnetting, as this will provide for
24-2 = 14 subnets. The subnet mask for 4 bits is then 255.255.255.240.

上面这两道题,单独理解都很容易,但是,我结合两道题的答案看起来,觉得有点问题。题B说了,能够利用的子网为24-2,很明显,它是考虑将子网全“0”和全“1”排除了。那么,在题A中,为什么又把全“0”和全“1”的情况(即115.64.4.0和115.64.7.0两个子网)包括进去了?

2005年08月08日

持续更新中

使用cisco技术设计简单LAN(15问)

  1. router,switch和bridge能够划分LAN。
  2. router能够包交换,包过滤,网络间通信。增加路由器能够减少碰撞冲突,但是无法是碰撞消失。增加路由器使一个网络的广播域增加,但是广播域大小变小。路由器不转发广播。
  3. 一个hub的所有接口是一个冲突域,一个广播域。一个交换机的所有接口是一个广播域,但是每个接口是一个冲突域。一个路由器的每个接口是一个广播域,也是一个冲突域。
  4. 增加一个路由器会使一个网络的广播域从一变为二,而这时网段一到网段二的链路层的数据包传输将需要路由,虽然整个网络的传输量减少了(广播域变小),但是网段间的传输效率确实降低了。由于路由器的增加,冲突将大大减少(见2)。
  5. 路由器不转发广播,能够基于第三层的信息进行包过滤。增加任何一个网络设备都会增加时延。交换机的处理速度快于路由器,因为交换机工作于第二层,而路由器工作于第三层,需要寻找三层目的地址,效率低于交换机。
  6. 见3
  7. hub支持半双工模式,所有接口是一个冲突域,其接口的主机间通信经常发生碰撞。
  8. 见3
  9. unicast address 单播地址,是指向一个特定主机发送广播,一般就是一个主机地址。multicast address组播地址,所有的D段ip都是组播地址,他们不区分网络id和主机id。eg:172.16.128.255/18她的实际广播地址是172.16.191.255,所以172.16.128.255是一个单播地址。
  10. 网桥和交换机都工作于第二层,交换机的端口数量一般比网桥多。
  11. hub和repeater能够扩大一个网络的覆盖面积。而交换机,路由器等虽然也能扩大面积但是同时也划分了网络。
  12. CDP是一个cisco特有的协议。为了转发CDP消息,接口必须支持SNAP头部。任何LAN接口,HDLC,帧中继和ATM都支持CDP。交换机和路由器都能够发现邻接路由器的三层地址细节,即使没有配置三层协议——因为CDP并不依靠任何三层协议。CDP能够发现的邻接路由器的细节有:1〉设备标识号——如主机名;2〉地址列表——网络层和链路层地址;3〉端口标识号——端口的别名;4〉功能列表——设备能干什么,如路由或者交换;5〉平台——model和OS类型
  13. 如果考虑只有一个缺省vlan的话,无论是几个交换机无连,网络也只有一个广播域。交换机和hub并不划分广播域,只有路由器才划分广播域。

按设计需求分配IP地址(54问)

  1. 私有ip地址范围:A类:10.0.0.0——10.255.255.255;B类:172.16.0.0——172.31.255.255;C类:192.168.0.0——192.168.255.255
  2. http://blog.donews.com/dugu/archive/2005/08/09/501761.aspx
  3. vlan间路由的知识,路由器的连接交换机的接口必须no ip address,然后在其每个vlan对应的子接口上设置各自的ip(这里可以理解为设置为网关ip)

停止更新

三种广播地址

  1. Local Network Broadcast:255.255.255.255
  2. Directed Broadcast:172.16.3.255//假设掩码为255.255.255.0
  3. All Subnet Broadcast:172.16.255.255

       在老版本路由器中,除第一种广播会被隔离外,其他两种广播都会被转发。而新版本路由器中,所有类型广播都被隔离。

区别交叉线和直连线

培训的时候,老师说过的,但是还是没怎么记住,今天看了视屏,又巩固了一遍:

Use a crossover cable when BOTH ports are designed with an "x" or neither port is designed with an "x"!

一般来说,交换机或者hub的接口都带有"x",如1x,2x……,而PC机的网卡和路由器的接口都没有"x"。

实际上,从原理上解释是因为,交换机的接口RJ45,管脚1,2是用于接收,管脚3,6用于发送。而PC机的网卡和路由器的接口,管脚1,2用于发送,管脚3,6用于接收。所以,对于交换机连接交换机,用交叉线。而交换机连接PC或者路由器,就要用直连线。

综上,相同设备(交换机和hub,PC机和路由器)之间连接用交叉线,不同设备连接用直连线。

routing Protocol和routed Protocol

前者是路由协议,是用来路由和维持路由表的协议,如rip等;后者是被路由协议,是用来承载用户数据的协议,如ip,ipx等。

路由协议

IGRPEIGRP都为CISCO的转有协议,不允许其他厂商使用。

OSPF是flooded链路状态信息出去,LSA(链路状态信息公告)只是在一个area进行flooded,路由器收集area上的所有lsa信息,形成一个连路状态信息数据库,OSPF使用SPF算法计算最佳路径。使用OSPF需要分层,需要有骨干网和普通网,比较适合分层的结构化网络。它使用COST来计算最佳路径:10*8/带宽

RouterID:为路由器启动过程当中,所有端口中能够激活的最大的ip地址为RouterID。不过,为了稳定,一般将loopback口优先考虑设置为RouterID。

Autonomous Systems:自制系统

他是一个范围,具有相同管理域下的所有网络设备的组合,称为一个自制系统。如:网通,电信和铁通都可以理解为一个自制系统。在一个自制系统内部运行的协议,成为IGPs(内部网关协议):RIP,IGRP等。运行在不同自制系统之间的协议称为EGPs(外部网关协议):BGP。

Administrative Distance:管理距离

他是用来衡量路由协议好坏的一个标准。其值越小,说明可行度越高。RIP的缺省管理距离是120,而IGRP为100。

路由协议的分类:

  1. Distance Vector(距离矢量路由协议):定期的把自己的整个路由表广播给邻居,如RIP和IGRP。
  2. Hybrid Routing(杂合协议):具有1,3两者特点的协议,如:EIGRP。EIGRP相比IGRP:1〉快速收敛;2〉减少了带管占用;3〉支持更多的高层协议;4〉类似的Metric计算方法;5〉相同的负载均衡方式
  3. Link State(链路状态路由协议):只有网路拓扑结构发生变化的时候,才会向网上所有的路由器发送变化部分的更新,如:OSPF。链路状态路由协议具有三个特点:1〉发送自己的端口发连接的链路状态信息;2〉以组播方式发送;3〉触发式更新,无更新周期;4〉路由的获取是自己计算的(SPF)。

Major Classful Network(主有类网络)

Rip和Igrp都只支持主有类网络的路由,即路由信息不带有子网掩码。主有类网络路由协议,将在网络边界汇总。他不支持不连续的网络。

在不连续网路中,使用静态路由的时候,需要使用ip classless命令以支持无类路由,否则,网络将无法连通。

负载均衡

RIP和IGRP默认支持4条路径的负载均衡,最大可以支持到6条。但是,RIP只能支持等效负载均衡,而IGRP却可以支持非等效的负载均衡:1〉使用命令variant multiplier 来确定一个倍数n(即为multiplier值),假设最佳路经的Metrics值为X,而另一条可行路经的Metrics值为Y+Z(其中Y为源地址到下一跳的Metrics值,而Z为下一跳的值到目的地值的Metrics值),那么X,Y,Z和n必须满足的关系:Z<X且Y+Z<n*X

Metrics

距离矢量用来选择最佳路径的依据。对于RIP:仅仅用Hop Count,跳数来衡量Metrics值。对于IGRP:用带宽,时延,负载,可靠性和最大传输单元来确定Metrics值(但是缺省只用带宽和时延来确定)。

Routing Loop(路由环路)

由谣言传送的特性所导致,当然此特性仅在距离矢量路由协议上存在。解决方法有:

  1. 定义一个最大值,RIP的最大跳数定义为15跳。
  2. Split Horizon(水平分割):a发送更新信息给b后,b不能再发送同样的更新信息给a。
  3. Route Poisoning(路由毒杀):当路由器C的直连网络无法到达时,路由器C并不立即删除对应网络的路由条目,而是将Metrics值标识为无限大,即不可到达。在C下次更新信息的时候,将把这个不可到达信息发送给邻接路由器。以避免由于C直接删除路由条目,而导致的路由环路。
  4. Poison Reverse(反转毒杀):在3的情况下,C发送某个网路不可到达的信息给邻接路由器B后,B并不立即更新路由条目为不可到达,而是设置对应路由条目为Possible Down,然后再次将这个Possible Down信息发送给C(这种情况已经打破了水平分割的原则,但是也仅允许这种特例才能打破水平分割原则),其目的在于,要求C确认是否真的不可到达,同时也是防止C收到的是其他路由器的错误信息。
  5. Holddown Timers:在3的情况下,当C发送不可到达信息给B时,B由于以前的路由条目为能够到达,而现在收到的不可到信息比以前的信息更坏,将进入holddown状态,此时不再接收信息直到收到比以前更好的信息或者时间到达后,才会退出holddown状态(注意:仅仅是不再接收对应路由条目的信息,但是会发送这个possible down路由条目出去,同时其他的路由条目的发送与接收也不会被影响)。
  6. Triggered Updates(触发更新):在3的情况下,路由器C将立刻把网络不可到达的信息发送给邻接路由器,而邻接路由器也会立刻将此信息发送出去,此时是立刻发送信息,路由器B没有机会产生谣言,无法形成路由环路。

ACL配置

在配置Acl的时候,应该注意:标准acl应该设置在目的网络的路由器上,而扩展acl则要设置在源地址网络附近的路由器上。因为对于标准acl,如果设置在源地址网络附近的路由器上,将会扩大acl的应用范围;而若把扩展acl设置在目的网络附近的路由器上,将会增加沿途路由器对于不必要的数据转发的负担。

交换机的三大功能

  1. Address Learning
  2. Forward / Filter decision
  3. Loop avoidance

广播风暴

由于冗余拓扑结构的存在,会导致广播风暴。当在存在冗余交换机B的网络中,某台pc发送一个广播后,交换机A.B的MAC表中都没有目的地址条目,就会flood到自己的所有接口上,而flood出去的帧又会被其他交换机再次flood,由此形成广播风暴。

Multiple Frame Copies多帧复制

同样在存在冗余交换机的网络中,当交换机刚刚启动后,地址表中没有任何条目时,某x发送一个unicast帧给同一个网段的y,这个时候,交换机A本应丢弃这个单播帧的,但是由于还没有学习到地址条目,就会将这个帧flood到所有端口,而此时冗余交换机B收到这个帧后,由于同样的没有学习到mac条目,他也会再次flood到所有的端口,而这个时候,目的地址y收到了来自x,A,B的三个同样的帧。造成了帧的重复接收,这种情况,对计数问题有很大的影响。

MAC DATABASE Instability Mac地址的不稳定性

在上一种的情况下,同样在交换机刚启动的时候,x发送一个单播帧给y的时候,交换机A会先会在port0端口接收到来自x的帧,此时mac表中,将x的地址和port0建立关联,接下来,冗余交换机B同样会将x地址和自己的port0建立关联,同时也会flood这个帧到自己的port1端口,这个时候,A的port1端口收到的来自B 的port1端口的帧,交换机A又会认为x的地址和port1也建立了关联,可是,这种情况显然是不应该存在的。这就造成了MAC地址的不稳定。

Spanning-Tree Protocol 

交换机间每两秒种发送一个Bpdu(Bridge Protocol Data Unit),它包含有桥的状态信息和身份标志。Root Bridge 是指有最小Bridge ID的桥,而Bridge ID的大小由两部分决定:前半部分为Bridge Priority(交换机缺省为32768),后部分为Mac Address。此时其他交换机就为NonRoot Bridge,他选取和Root Bridege通信速度更快的接口为root port。 Designated Port由端口优先级(默认相同)和端口地址决定。Root Bridge在一个广播域下决定,而Root Port和Designated Port在一个冲突域下决定。

改变一些参数,使得stp更快捷,就成为了Rapid Spanning-Tree Protocol 。

本征VLAN(NATIVE VLAN)

它是指在802.1Q封装的Trunk中,不加上tag的vlan。这样的vlan由于不需要封装就可以在trunk上传输,因此可以通过hub(hub没有封装数据帧的能力)。一般vlan1缺省为本征vlan。

Per-Vlan Spanning Tree

PVST:他是指每一个vlan都能够启动一个STP。

VTP(Vlan Trunk Protocol)

它是用来将一个vtp域下的vlan配置信息进行同步,周期性的在域间利用Trunk通告vlan的配置信息。在一个VTP域下的交换机具有相同的域名和密码。VTP有三种模式:Server,Client和Transparent。VTP通告以组播的方式每五分钟发送一次,Server和Client将会同步信息到最新版本号。

VTP Prunning(VTP的修整)

由于一个VTP域中,不是每个端口都有端口对应所有的VLAN,实际上对于某个制定的VLAN,有很多交换机没有任何端口加入的这个VLAN的,如果仍然在这些交换机相连的Trunk上传输这个vlan的信息,将占用一些不必要的带宽。所以,就有了VTP域的修整,也就是在这些不必要的传输VLAN信息的Trunk上过滤掉flooded信息以节省带宽。

配置VLAN

  1. 首先考虑是否创建VTP域:一般在很多交换机的时候才需要创建域。在建立一个vtp域的时候,需要:VTP域名,VTP模式,是否启用VTP修整,密码和VTP Trap。需要注意的是,在添加一个新的交换机到一个vtp域的时候,为了避免新的交换机由于配置信息的版本号过高而将其错误的配置信息传播出去,应该将新的交换机的vtp模式设置为Client模式。 delete vtp命令可以将vtp配置信息版本号调整至0。
  2. 建立Trunk:两台以上交换机互连的时候建立Trunk。
  3. 创建VLAN
  4. 分配端口给VLAN

19系列交换机中,在全局模式下直接建立vtp的一些设置,而在29系列中,需要在特权模式下,命令vlan database进入vlan设置。

NAT配置

NAT配置的时候,需要注意相对于内网的外部路由器在建立路由表的时候,应该创建路由到inside Global ip address,而非inside Local ip address。

广域网

连接方式:

  1. 专线(leased line):24小时服务,64K基本,T1(1.5M),E1(2M), T3(45M),E3(34M)。
  2. 交换式电路:异步DSTN;同步ISDN(Serial同步口) //1,2两种方式都属于点对点的电路连接方式
  3. 包交换:发出的数据包头有特定信息,网络会根据包头中的特定信息来确定包往哪儿发送。虚电路,可以实现点对多。

使用协议:

广域网在二层使用的协议:

  1. 专线:HDLC(分为cisco hdlc:支持多协议——所有的cisco产品默认支持和标准hdlc:只支持单协议ip),PPP(由hdlc发展起来的,由NCP支持多协议,LCP支持链路捆绑,身份验证CHAP和PAP,数据加密等),SLIP(早期的只用于服务IP协议的一种协议)
  2. 包交换:X.25(帧中继前生),Frame Relay(主要用于接入网),ATM(主要用于骨干网)

PPP的认证方式

PAP:两次握手,直接明文发送用户名和密码

CHAP:三次握手(A<————>B):

1〉A先发送一个challage|编号01|id|random|hostname(routerB)|

2〉B发送一个Respond   |编号02|id|Hash|hostname(routerA)|,其中Hash值是B用A发送过来的random值和他查询hostname对应的密码一起用MD5计算出来的。

3〉A根据B发送过来的hostname来查找一个密码和原来备份得random值一起计算一个Hash值,用这个计算出来的Hash值,和B发送过来的Hash值比较,如果一致,则A向B发送一个Welcome信息(编号03);如果不一致则发送Refuse(编号04)。

帧中继

几个术语:

PVC:永久式虚电路

SVC:交换式虚电路

DLCI:数据链路连接标志,只用于本地虚电路的一个编号

LMI:帧中继信令信号,查询维护DLCI状态信息的工具

CIR:可保障信息速率

EIR:网络通行情况下最大的峰值速率

FECN / BECN:前 / 后 向拥塞通告信息

帧中继是一个非广播型网络结构,它的数据帧有两种类型:

1〉CISCO(所有CISCO设备采用);

2〉IETF。

        由于数据帧格式所限,他的广播包只能到达一个唯一目的地。他一般采用星型网络拓扑结构,有点对点和点对多点两种结构。

LMI工作过程

CISCO产品支持三种LMI标准:

CISCO;ANSI T1.617 Annex D;ITU-T Q.933 Annex A

帧中继反转Arp和LMI信号A〈——Frame Relay Cloud——〉B

  1. A向帧中继交换机发送一个hello包,请求咨询A的网络状态
  2. 帧中继交换机查询虚电路表,将A的接口对应的虚电路的DLCI全部发送给A
  3. A发送一个包给B,B将把本地DLCI号和A的IP地址对应(称作反转ARP)

帧中继能够和ATM,ATM终端互连

ISDN

ISDN有两类接入方式:

  1. BRI:有2B(64K*2, 使用PPP或者HDLC协议)和1D(16K,信令信号,LAPD协议)信道
  2. PRI:欧洲标准:30B+1D(64K*30+64K=2M,E1);美洲标准:23B+1D(23K*64+64=1.5M,T1)

ISDN网络结构:

NT-1连接U口(两根线)和T口(四根线);

NT-2将一个T口分为许多相同功能的S口(增加接口类似HUB的功能)

TA:终端适配器,接口为R口,对于本身不支持ISDN的I,E,Q系列协议的设备,需要这个终端适配器来转换信号

U口:将TE1和NT-1接口合在一起

Dial-on-Demand Router(按需拨号路由)

他使用一个isdn物理链路远程接入路由器。在需要的时候连接,结束时断开连接,使用isdn或者pstn。

DDR操作过程

  1. 定义到目的路由器的静态路由
  2. 定义感兴趣流量
  3. 拨号信息查询
  4. 数据传输
  5. 呼叫终止

拨号的几个概念

Dialer Interface:为了设置拨号参数而虚拟的一个接口

Dialer Pool:组织物理接口的一个平台,并不实际存在

Physical Interface:加入到拨号池中