| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| en:edu:zmitac:s1:apl:lab:lab1 [2024/11/18 21:25] – ktokarz | en:edu:zmitac:s1:apl:lab:lab1 [2026/09/16 11:58] (current) – Accessibility: add WCAG-compliant alt texts to all nine figures mcp_agent |
|---|
| |
| ===== Laboratory 1 ===== | ===== Laboratory 1 ===== |
| Programming in Assembler Laboratory, exercise 1 | Configuration of the Visual Studio for simple assembler application written in MASM. |
| |
| ==== Instroduction ==== | ===== Introduction ===== |
| In this exercise, you will create a console application in pure assembler for x64 (Intel/AMD processors, 64 bits) that will interact with the Windows operating system (Windows 7, 8, 8.1, 10, 11) by calling its functions via the Windows API. | In this exercise, you will create a console application in pure assembler for x64 (Intel/AMD processors, 64 bits) that will interact with the Windows operating system (Windows 7, 8, 8.1, 10, 11) by calling its functions via the Windows API. |
| |
| Create a new project: a command-line (console) application for C++, native (__not C++ .NET__) figure {{ref>jalabstep1}} | Create a new project: a command-line (console) application for C++, native (__not C++ .NET__) figure {{ref>jalabstep1}} |
| <figure jalabstep1> | <figure jalabstep1> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_01_48.png?1000|}} | {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_01_48.png?1000|Visual Studio "Create a new project" dialogue with the C++ language filter applied and the "Console App" template highlighted in the template list; the Next button is located in the bottom right corner.}} |
| <caption>Creating a C++ project, native code</caption> | <caption>Creating a C++ project, native code</caption> |
| </figure> | </figure> |
| Delete the generated .cpp file (from VS) and then add a new .asm file (this can be done by clicking on the project (not the solution!) and selecting to add a new element figure {{ref>jalabstep21}}, then select the text file and change its name and extension figure {{ref>jalabstep22}}: | Delete the generated .cpp file (from VS) and then add a new .asm file (this can be done by clicking on the project (not the solution!) and selecting to add a new element figure {{ref>jalabstep21}}, then select the text file and change its name and extension figure {{ref>jalabstep22}}: |
| <figure jalabstep21> | <figure jalabstep21> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_07_27.png?1000|}} | {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_07_27.png?1000|Visual Studio project context menu opened from the Solution Explorer, with the "Add" submenu expanded and the "New Item..." entry (shortcut Ctrl+Shift+A) highlighted.}} |
| <caption>Adding a new element</caption> | <caption>Adding a new element</caption> |
| </figure> | </figure> |
| <figure jalabstep22> | <figure jalabstep22> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_08_50.png?1000|}} | {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_08_50.png?1000|Visual Studio "Add New Item" dialogue with the Visual C++ Utility category selected, the "Text File (.txt)" template chosen and the Name field set to source.asm; the Add button is located in the bottom right corner.}} |
| <caption>Adding a new element - asm file</caption> | <caption>Adding a new element - asm file</caption> |
| </figure> | </figure> |
| |
| <figure jalabstep31> | <figure jalabstep31> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_17_17.png?1000|}} | {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_17_17.png?1000|Visual Studio project context menu with the "Build Dependencies" entry highlighted and its submenu expanded, showing the single option "Build Customizations...".}} |
| <caption>Choosing the project build customization menu</caption> | <caption>Choosing the project build customization menu</caption> |
| |
| |
| <figure jalabstep32> | <figure jalabstep32> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_17_40.png?1000|}} | {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_17_40.png?1000|Visual Studio "Visual C++ Build Customization Files" dialogue listing the available customisation files with their paths; only the "masm(.targets, .props)" check box is ticked, and the OK button is located at the bottom.}} |
| <caption>Choosing the MASM compiler</caption> | <caption>Choosing the MASM compiler</caption> |
| |
| Next, change the type of application being built from ''x86'' (32-bit application) to ''x64'' (64-bit application): Build->Configuration Manager menu figure {{ref>jalabstep4}}. | Next, change the type of application being built from ''x86'' (32-bit application) to ''x64'' (64-bit application): Build->Configuration Manager menu figure {{ref>jalabstep4}}. |
| <figure jalabstep4> | <figure jalabstep4> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_21_07.png?1000|}} | {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_21_07.png?1000|Visual Studio "Configuration Manager" dialogue with the active solution configuration set to Debug and the active solution platform set to x64; the project row shows the AssemblerLab1 project with configuration Debug, platform x64 and the Build check box ticked.}} |
| <caption>Environment configuration - building the 64-bit version</caption> | <caption>Environment configuration - building the 64-bit version</caption> |
| </figure> | </figure> |
| === Creating a starting point and completing libraries === | === Creating a starting point and completing libraries === |
| In this step, we will inform the linker what starting point to enter into the executable file. | In this step, we will inform the linker what starting point to enter into the executable file. |
| In high-level languages, this is the ''main()'' function by default. In the case of assembler, there is no such assumption, so the source code must be consistent with the value set here. | In high-level languages, this is the ''main()'' function by default. In the case of an assembler, there is no such assumption, so the source code must be consistent with the value set here. |
| |
| <note important>The name of the code entry point function given to the linker must match the source code. In case of discrepancies, the program will work incorrectly, will not start or will throw an exception at startup!</note> | <note important>The name of the code entry point function given to the linker must match the source code. In case of discrepancies, the program will work incorrectly, will not start or will throw an exception at startup!</note> |
| |
| In the given example, the name of the function is ''myFunctionAsm''. To get to the configuration window, you should select the project (not the solution!) in the project tree, then right-click on the project, select ''Properties'', or use the shortcut [Alt+Enter]. A window will appear as in the figure {{ref>jalabstep51}}. Then in the ''Linker'' section, select the ''Advanced'' option and in the ''Entry Point'' field enter the name of the function. | In the given example, the name of the function is ''myFunctionAsm''. To get to the configuration window, you should select the project (not the solution!) in the project tree, then right-click on the project, select ''Properties'', or use the shortcut [Alt+Enter]. A window will appear as in the figure {{ref>jalabstep51}}. Then in the ''Linker'' section, select the ''Advanced'' option and in the ''Entry Point'' field enter the name of the function. In our case, it will be ''hello_world_asm''. |
| |
| <figure jalabstep51> | <figure jalabstep51> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_09_06_12.png?1000|}} | {{:en:edu:zmitac:s1:apl:lab:virtualbox_msedge_-_win10_18_11_2024_09_06_12.png?1000|Visual Studio project Property Pages dialogue for the Active(Debug) configuration and Active(x64) platform, with the Configuration Properties, Linker, Advanced section selected; the "Entry Point" property in the first row is highlighted and contains the value hello_world_asm.}} |
| <caption>Configuration of the program entry point</caption> | <caption>Configuration of the program entry point</caption> |
| </figure> | </figure> |
| Details in figure {{ref>jalabstep52}}. | Details in figure {{ref>jalabstep52}}. |
| <figure jalabstep52> | <figure jalabstep52> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_50_25.png?1000|}} | {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_12_03_2021_08_50_25.png?1000|Visual Studio project Property Pages dialogue with the Configuration Properties, Linker, Input section selected and the "Additional Dependencies" editor window open; the upper text field contains legacy_stdio_definitions.lib and legacy_stdio_wide_specifiers.lib, one per line, and the "Inherited values" list below starts with kernel32.lib and user32.lib.}} |
| <caption>Configuring additional libraries</caption> | <caption>Configuring additional libraries</caption> |
| </figure> | </figure> |
| In some cases, the ''.asm'' file is not automatically recognized as a source file with assembler code. Therefore, you must explicitly inform the environment about this. To do this, right-click the file name in the project tree and select ''Properites'', then in the ''General'' section, select ''Item Type'' and set its value to ''Microsoft Macro Assembler''. Details in figure {{ref>jalabstep6}}. | In some cases, the ''.asm'' file is not automatically recognized as a source file with assembler code. Therefore, you must explicitly inform the environment about this. To do this, right-click the file name in the project tree and select ''Properites'', then in the ''General'' section, select ''Item Type'' and set its value to ''Microsoft Macro Assembler''. Details in figure {{ref>jalabstep6}}. |
| <figure jalabstep6> | <figure jalabstep6> |
| {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_27_03_2021_19_44_41.png?1000|}} | {{:pl:edu:zmitac:n1z:ja:virtualbox_msedge_-_win10_27_03_2021_19_44_41.png?1000|Visual Studio Property Pages dialogue for the file source.asm with the Configuration Properties, General section selected; the "Item Type" drop-down list is expanded and the "Microsoft Macro Assembler" entry is selected.}} |
| <caption>Marking an ASM file as a source file with assembler code</caption> | <caption>Marking an ASM file as a source file with assembler code</caption> |
| </figure> | </figure> |
| |
| ==== Practice: Task to be performed ==== | ==== Practice: Task to be performed ==== |
| Implement reading of the date (use the function ''GetSystemTime''(sysinfoapi.h)((https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime)) reading the structure ''_SYSTEMTIME''(minwinbase.h)((https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime)). The structure consists of 8 16-bit values. | Implement reading of the current date (use the function ''GetSystemTime''(sysinfoapi.h)((https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime)) reading the structure ''_SYSTEMTIME''(minwinbase.h)((https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime)). The structure consists of 8 16-bit values. |
| |
| <note important>Note - do not declare the structure as such, just for simplicity declare a target, static array of 8 Word (16bit) values in the data segment.</note> | <note important>Note - do not declare the structure as such, just for simplicity declare a target, static array of 8 Word (16bit) values in the data section.</note> |
| |
| Convert the year, month and day to text form ASCII and display on the screen using: | Convert the year, month and day to ASCII text form and display on the screen using: |
| * in the command line (cmd) using the ''WriteConsoleA'' function; | * in the command line (cmd) using the ''WriteConsoleA'' function; |
| * in the dialog box, using the ''MessageBoxA'' function; | * in the dialog box, using the ''MessageBoxA'' function; |
| |
| <note tip>Pay attention to the position of individual words in the structure and their corresponding values.</note> | <note tip>Pay attention to the position of individual words in the structure and their corresponding values.</note> |
| |
| === Code fragments to use === | === Code fragments to use === |
| Remember to declare the code and data segments using the ''.data'' and ''.code'' operators. At the end of the code, place the ''END'' clause. | Remember to declare the code and data sections using the ''.data'' and ''.code'' directives. At the end of the code, place the ''END'' clause. |
| | <note> |
| | Code and Data sections historically were named segments. You can still find such names in some sources. |
| | </note> |
| |
| == Data segment == | == Data section == |
| Each text string must end with a token with binary value 0. We do not use Polish characters because we use the ASCII character set (8-bit), not Unicode.\\ | Each text string must end with a byte with binary value 0. It is named the NULL character. We do not use international characters because we use the ASCII character set (8-bit), not Unicode.\\ |
| A sample data segment that was used in the presentation looks like this:\\ | A sample data segment that was used in the presentation looks like this:\\ |
| <code> | <code> |
| info_msg db "Info", 0 | info_msg db "Info", 0 |
| title_msg db "Window Title JA",0 | title_msg db "Window Title JA",0 |
| text_msg db "And this is a very long text to display in the console, but without Polish chars!",13,10 | text_msg db "And this is a very long text to display in the console!",13,10 |
| dummy dq 0 | dummy dq 0 |
| |
| </code> | </code> |
| |
| To use external Windows API functions, you need to tell the assembler what they are called, so that the linker can then match the names from your code to those in the library.\\ | To use Windows API functions, you have to tell the assembler how they are named so the linker can match the names from your code to those in the library.\\ |
| To do this, you need to use the ''EXTERN'' and ''PROC'' directives:\\ | To do this, you need to use the ''EXTERN'' and ''PROC'' directives:\\ EXTERN means that the functions are not defined in your source code but present in external modules - here libraries. PROC specifies that the symbols represent the names of the functions. |
| | |
| | <note> |
| | In assembler there is no difference between function and procedure - every subroutine is named procedure. |
| | </note> |
| <code> | <code> |
| |
| We also usually include information about the library. | We also usually include information about the library. |
| |
| == Code segment == | == Code section == |
| After running the program, we need to save the values of certain registers, in order to restore them later. At the very end, you should also call a function that ends the program and returns a so-called return code to the operating system.\\ | After running the program, we need to save the values of certain registers, in order to restore them later. At the very end, you should also call a function that ends the program and returns a so-called return code to the operating system.\\ |
| |
| </note> | </note> |
| |
| As you can see, the whole thing comes down to reserving and putting space on the stack for the so-called shadow register, i.e. for saving the values of registers and restoring them after the program has finished executing. | As you can see, the whole thing comes down to reserving and putting space on the stack for the so-called shadow space, i.e. for saving the values of registers and restoring them after the program has finished executing. |
| |
| In the program, we use interaction with graphic elements such as the main window (Foreground Window) and the data stream (STD_OUT). When calling the function, it is necessary to clearly indicate which graphic object we want to interact with or create a child object (e.g. MessageBox).\\ | In the program, we use interaction with graphic elements such as the main window (Foreground Window) and the data stream (STD_OUT). When calling the function, it is necessary to clearly indicate which graphic object we want to interact with or create a child object (e.g. MessageBox).\\ |
| To do this, we need to get two handles: | To do this, we need to get two handles: |
| * a handle to the current main application window at the operating system level (so-called Foreground Window) | * a handle to the current main application window at the operating system level (so-called Foreground Window) |
| * a handle to a terminal-type data stream, into which information can be entered. | * a handle to a terminal-type data stream, into which information can be entered. |
| <note important>Note! To display something on the console, you should always press Enter, even ... virtually. Therefore, any string of characters redirected to STD_OUT will appear on the console as long as ... there is a special code at the end indicating the ENTER character. These are two numeric values, in the order 13 and 10. Hence, in the example in the data segment, after the content there is '',13,10''. If you don't do this, you will need to go to the window and manually press ENTER to display the string of characters redirected there from your program. Otherwise, the window will remain empty and the text will not appear!</note> | <note important>Note! To display something on the console, you should always press Enter, even virtually. Therefore, any string of characters redirected to STD_OUT will appear on the console as long as there is a special code at the end indicating the ENTER character. These are two numeric values, in the order 13 and 10. Hence, in the example in the data segment, after the content there is '',13,10''. If you don't do this, you will need to go to the window and manually press ENTER to display the string of characters redirected there from your program. Otherwise, the window will remain empty and the text will not appear!</note> |
| |
| To read the handles, two functions from the Windows API will be useful, which will return pointers (handles) to the appropriate objects. These values should be saved and can be used multiple times. | To read the handles, two functions from the Windows API will be useful, which will return pointers (handles) to the appropriate objects. These values should be saved and can be used multiple times. |
| |
| Reading the handle to the main window, this handle should be used when invoking a dialog window of the Message Box type (and any other): | Reading the handle to the main window, this handle should be used when invoking a dialogue window of the Message Box type (and any other): |
| <code> | <code> |
| ... | ... |
| ... | ... |
| </code> | </code> |
| | |
| | In the above example, the value returned by the function (via the RAX register) is saved in the variable ''handler'', of the DQ type (64 bits, i.e. simply an address in memory). |
| | |
| | To read the handle to the inside of the terminal, you need to use another function: |
| | <code> |
| | ... |
| | mov rcx, STD_OUTPUT_HANDLE |
| | call GetStdHandle |
| | mov stdHandle, rax |
| | ... |
| | </code> |
| | However, this function requires one argument, which data stream we want. There are three possibilities: |
| | * STD_INPUT_HANDLE = -10 - standard input - it can be used to read data from the console |
| | * STD_OUTPUT_HANDLE = -11 - standard output - it is used to write something in the terminal; remember the ending ENTER, i.e. ''13,10''!; |
| | * STD_ERROR_HANDLE = -12 - stream for errors; |
| | |
| | An example code that displays the text in the variable (at the address) ''text_msg'' on the console is below. The first parameter is the console handle read above. In the third parameter, you need to specify how many characters should be displayed. The fourth parameter is a pointer to where the function will return information about how many characters it actually displayed (it's not entirely clear what this is for). |
| | <code> |
| | ... |
| | ; WINAPI WriteConsoleA( |
| | ; RCX => _In_ HANDLE hConsoleOutput, |
| | ; RDX => _In_ const VOID *lpBuffer, |
| | ; R8 => _In_ DWORD nNumberOfCharsToWrite, |
| | ; R9 => _Out_opt_ LPDWORD lpNumberOfCharsWritten, |
| | mov rcx, stdHandle |
| | lea rdx, text_msg |
| | mov r8, sizeof text_msg |
| | mov r9, dummy |
| | call WriteConsoleA |
| | ... |
| | </code> |
| | |
| | To display a dialogue box, use a pointer to the main application window. The parameters passed in this order are a handle, two pointers, to texts (they must end with a zero, RDX is a pointer to the content that the dialogue box will display, and R8 is a pointer to the window header that will be given to the window) that are to appear in header and dialogue body and finally, a binary mask specifying which buttons and icons to use in the dialogue box ((https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxa)). |
| | <code> |
| | ... |
| | ; WINUSERAPI int WINAPI MessageBoxA( |
| | ; RCX => _In_opt_ HWND hWnd, |
| | ; RDX => _In_opt_ LPCSTR lpText, |
| | ; R8 => _In_opt_ LPCSTR lpCaption, |
| | ; R9 => _In_ UINT uType); |
| | mov rcx, handler |
| | mov rdx, offset hello_msg |
| | mov r8, offset info_msg |
| | mov r9, 0 ; MB_OK |
| | and rsp, not 8 |
| | call MessageBoxA |
| | ... |
| | </code> |
| | |
| | <note important>Note - in the above case, the stack was explicitly aligned to a value divisible by 16 with the instruction (''and rsp, not 8''). It is a requirement of the x64 Windows calling convention. If you call the system function you should take care to align the stack pointer so that it is divisible by 16.</note> |
| | |
| | ==== Tasks to be done for the report ==== |
| | Let's start wth debugging. If the program runs correctly please set the breakpoint at the beginning of assembly code. Open register window and observe the content of registers after instruction execution. Please do not step into Windows functions including WriteConsoleA() and MessageBoxA(), use step over instead. In the report answer the following questions. |
| | * RIP - note how its content differs with every instruction in the series. Do changes depend on the type of instruction executed? Compare changes with the disassembly window and the number of bytes instructions occupy. |
| | * While calling WriteConsoleA look at the memory dump window at the address stored in RDX. What is there in the memory? You can try different view modes. |
| | * While calling MessageBoxA function look at the memory dump window at the address stored in RDX and in R8. What is there in the memory? You can try different view modes. |
| | * Include in the program the instruction which sets the RAX to some chosen value e.g. 00FFh before calling previously mentioned functions. Does the RAX register content change after returning from these functions? |
| | * Try to replace the instruction "mov rdx, offset hello_msg" with "lea" instruction (of course with proper argument). Explain if they are identical or not. |
| | |
| | |
| | ==== Method of results verification ==== |
| | The texts and windows that appear on the screen should correspond to the algorithm. Pay special attention to whether random strings of characters are generated, which may suggest that you forgot about the ending 0. |