حتماٌ زماني كه درس زبان ماشين را پاس ميكرديد ،در مقايسه با زبانهايي چون C++ , Object Pascal(Delphi) آنرا فوسيل ? ميناميديد و هرگز فكرنميكرديدكه MASM بتواند فايلهاي .EXE, .DLL, .sys تحت ويندوز را بسازد.
اما حقيقت اين است كه اكنون اسمبلرهاي فراواني وجود دارند كه به شما امكان ايجاد فايلهاي اجرايي با فرمت PE را ميدهند.اگر نميدانيد PE(Portable Executable) چيست بهتر است به مقالات موجود در MSDN مراجعه كنيد.
اسمبلرهاي زير جزء معروف ترينها هستند:
• MASM(Macro Assembler) from Microsoft
• TASM(Turbo Assembler)from Borland
• NASM(Netwide Assembler)
• FASM
(در اين مقاله صرفاٌ از MASM استفاده خواهم كرد.)
اين مقاله براي چه كساني مناسب است؟
براي آنكه بتوانيد از مطالب اين مقاله استفاده كنيد (به نظر من) بايد با مطالب زير آشنايي داشته باشيد:
• زبان Assembly
• WIN32/64 API يا شايد بتوان گفت: Visual C++ بدون MFC ويا Delphi بدون VCL
براي شروع بهتره كلماتي مثل MASM و Download را در GOOGLE جستجو كنيد و MASM را Download كنيد.
توجه:به شما پيشنهاد ميكنم براي هماهنگي با من Package آقاي HUTCH را از آدرس زير Download كنيد:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ] (ورژن Package من 8 است اما ورژنهاي پايين تر هم مناسب خواهند بود.) در ضمن به MSDN هم احتياج پيدا خوايد كرد.(البته اگر نداشته باشيدش هم No Problem ?)
اگر دلفي يا BCB را نصب كرده ايد ميتواند از فايل win32sdk.hlp در پوشه MSHelp بجاي MSDN استفاده كنيد.(هرچند مال زمان پادشاه وزوزه?)
خوب حالا كه MASM را Download كرديد بايد اقدام به نصب آن كنيد.
آقاي HUTCH فرد دانايي است و براي كاهش حجم Package زيركي خاصي را بكار برده اما در عوض زمان نصب بسيار افزايش پيداكرده پس بهتر است در زمان نصب صبور باشيد.
شروع!!
تمامي برنامه ها تحت ويندرز در حالت Protected Mode اجرا ميشوند و اين يعني هربرنامه 4GB Address Space, خواهد داشت(4 گيگا بايت يعني زندگي!!)البته توجه كنيد كه گفتم Address Space نه Memory Space .با اينحال بدليل طراحي عالي NT تقريباً هيچ وقت با كمبود حافظه مواجه نخواهيد شد.
همه اينا رو به اين جهت گفتم كه شما را براي يك خبر خوشحال كننده آماده كنم:
? عمر Segment هاي اعصاب خورد كن بسر آمده.(يوهو!!)
فكر كنم همين اندازه تئوري كافي باشه ، بهتره دست بكار بشيم(يا بشم).كد زير را كپي كرده و در فايلي بنام test.asm پيست!! كنيد.
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.code
start:
invoke ExitProcess,0
end start
حالا ببينيم كد فوق چگونه عمل ميكند:
.386
اين دستور به اسمبلر ميگويد كه ما قصد استفاده از دستورات پردازندههاي 386 را داريم.
سوال:بابا مگه 386 تاريخ نشده؟
جواب:معماري 386 استاندارد مورد استفاده تمامي پردازندههاي 32بيتي بالاتر حتي p4 است.
.model flat, stdcall
با .model كه از روزگار گذشته آشنايي داريد.
Flat براي تمامي برنامه هاي تحت ويندوز استفاده ميشود و معني اش همان 4GB است.
Stdcall يا Standard Call ، Calling Convention مورد استفاده ما است.
Stdcall يعني آرگومان ها را از راست به چپ داخل stack گذاشته و در ضمن تابع فراخوانده شده مسئول خالي كرد (pop) stac است. (فرقش هم با روش cdecl در همين مورد آخره)
اگه قات زديد, بهتره Stdcall رو فعلاً بيخيال شيد ، بعداً ميفهميد چيه.!!
option casemap:none
حتماً ميدانيد كه MASM مانند Delphi(object Pascal) و بر خلاف C/C++ ،case sensitive نيست اما شما براي استفاده از MASM تحت ويندوز مجبوريد آنرا case sensitive كنيد .دستور فوق دقيقاً اين كار را انجام ميدهد.بشما توصيه ميكنم حتماً از اين دستور در ابتداي هر برنامه خود استفاده كنيد.
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
دوتا خط اول كه واضح است اما يك توضيح كوچك در مورد فايلهاي .inc :
فايلهاي include موجود در package آقاي HUTCH به اين شكلند كه :
windows.inc :شامل تمامي ثوابت مانند WM_PAINT ميباشد.
DllName.inc : شامل prototype توابع DllName ميباشد.بطور مثال kernel32.inc شامل prototype توابع export شده از kernel32.dll ميباشد.
Includelib هم براي link كردن با فايلهاي .lib استفاده ميشود.(اگر نميدانيد فايلهاي .lib چه هستند به من ربط نداره!!همه چي رو كه من نبايد توضيح بدم?)
.code
نشان دهنده نقطه شروعSection (ونه Segment) كد است.
invoke ExitProcess,0
invoke دستور جديدي است كه به MASM (ونه CPU) اضافه شده و به شما امكان فرا خواني توابع بدون Push كردن دستي آرگومانهاي تابع را ميدهد.مثلاً كد فوق برابر است با:
Push 0
Call ExitProcess
end start
اين دستور 2 كار انجام ميدهد:
• پايان Section كد را مشخص ميكند.
• Entry Point برنامه را تعيين ميكند.
حال براي Assemble كردن اين كد ابتدا يك پنجره كنسول باز كنيدوابتدا Assembler و سپس Linker را اجرا كنيد:
ml /c /coff /Cp test.asm
ml.exe نام Assembler است.
/c به Assembler ميگويد كه فقط فايل .obj ايجاد كند (نه .exe)
/coff فايل .obj با استاندارد coff (استاندارد unix) ميسازد.
/Cp :معادل همان option casemap:none است.
پس از اينكه همه كار بخوبي پيش رفت بايد فايلي بنام test.obj براي شما ايجاد شده باشد.
اكنون بايد اين فايل را Link كنيم(كه البته بهتره آنرا Dynamic Linking بناميم)
Link /SUBSYSTEM:WINDOWS test.obj
/SUBSYSTEMمهمترين سويچ Linker است و به آن ميگويد كه شما قصد داريد چه جور exe اي را ايجاد كنيد.كلاً دو حالت مهم دارد:
/SUBSYSTEM:WINDOWS
/SUBSYSTEM:CONSOLE
(در VC++ اين سويچ Entry point برنامه را از ميان main و WinMain انتخاب ميكند)
بعد از اينكه Linker را اجرا كرديد فايل test.exe براي شما ايجاد ميشود وهمانطور كه از سورس برنامه معلوم است ،با اجراي اين برنامه اتفاق خاصي نخواهد افتاد.
سلام دنيا!!
اكنون بهتر است يك برنامه بنويسيم كه پيام Hello World را نمايش دهد.
.386
.model flat,stdcall
include windows.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib
.data
Mes BYTE "Hello Wolrd",0.
.code
WinMain:
invoke MessageBox,NULL,offset Mes,NULL,0
invoke ExitProcess,0
end WinMain
كد فوق ساده است واحتياجي به توضيح ندارد.اين فايل را همانطوري كه قبلاً توضيح دادم assemble و link كنيد.پس از پايان كار يه نگاهي به حجم فايل exe ايجاد شده بيندازيد...حتماً تعجب خواهيد كرد!!!
پنجره اي رو به آفتاب!!
شايد وقتش شده باشه كه با هم يك پنجره بسازيم!!!
در مورد كد زير فكر ميكنم توضيحات درون خود كد كافي باشد اما متذكر ميشوم كه اگر نحوه ساختن يك پنجره بوسيله Win32 API را نميدانيد ممكن است كد زير كمي براي شما پيچيده باشد (براي اطلاعات بيشتر به كتاب Programming Windows نوشته Charles Petzoldمراجعه كنيد.)
.386
.model flat, stdcall
option casemap:none
include \masm32\include\WINDOWS.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
WinMain proto :HWND,WORD,WORD,WORD
.DATA ; initialized data
ClassName db "SimpleWinClass",0 ; the name of our window class
AppName db "Our First Window",0 ; the name of our window
;-------------------------------------------------------------------
.DATA? ; Uninitialized data
hInstance HINSTANCE ? ; Instance handle of our program
CommandLine LPSTR ?
;-------------------------------------------------------------------
.CODE ; Here begins the code
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR, CmdShowWORD
; creating local variables on stack
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX ; fill values in members of wc
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc ; register our window class
invoke CreateWindowEx,NULL,\
ADDR ClassName,\
ADDR AppName,\
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
NULL,\
NULL,\
hInst,\
NULL
mov hwnd,eax
invoke ShowWindow, hwnd,CmdShow ; display our window on desktop
invoke UpdateWindow, hwnd ; refresh the client area
; Enter message loop
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam ; return exit code in eax
ret
WinMain endp
;----------------------------------------------------------------------
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY ;if the user closes our window
invoke PostQuitMessage,NULL ; quit
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ;Default message processing
ret
.ENDIF
xor eax,eax
ret
WndProc endp
;-----------------------------------------------------------------------
start:
invoke GetModuleHandle, NULL ;get the instance handle of our program.
mov hInstance,eax
invoke GetCommandLine ; get the command line. You don't have to call this
; function IF your program doesn't process the command line.
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine,SW_SHOWDEFAULT ;call the main ;function
invoke ExitProcess, eax ;quit our program. The exit code is returned in eax ;from WinMain.
end start
نتيجه برنامه را در شكل زير مشاهده ميكنيد:
آيا فكر ميكنيد اين برنامه هيچكاري جز نمايش يك پنجره خالي انجام نميدهد؟
نكته: در كد فوق از عملگرد جديد MASM با نام ADDR استفاده شده است .اين عملگرآدرس effective يك متغييررا محاسبه ميكند ولي مشكل آن اينست كه تنها به همراه invoke قابل استفاده است ،پس در حالات ديگرنميتوانيد ADDR را بكار بريدو بايد از همان دستور LEA استفاده كنيد.
نظرات و پيشنهادات و سوالات خود را به ادرس [ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ] ارسال كنيد.
Real Programmers code in asm.