From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on gnuweeb.org X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM, NICE_REPLY_A,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) by gnuweeb.org (Postfix) with ESMTPS id A16A47E704 for ; Thu, 24 Mar 2022 06:31:54 +0000 (UTC) Authentication-Results: gnuweeb.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=qd0TcbMu; dkim-atps=neutral Received: by mail-pl1-f173.google.com with SMTP id c23so3788949plo.0 for ; Wed, 23 Mar 2022 23:31:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:date:mime-version:user-agent:content-language:to :references:cc:from:subject:in-reply-to:content-transfer-encoding; bh=wx7uMvQ+aSD5JlsyKxNV+eFRNUffjcTcNX0k+6eQcdc=; b=qd0TcbMuv0m6TKAQNlx1Zlx3SPltAkjsWMZNSVYrJZhFWAD8Dm+tcMzr+uld0rXttO MuQKKD/dfrnWJLW35mPvD56NO8ns1fRZQOlEwyZjFzKQWOmmjbKweITBtO9TDUzA6kok IxzZJhip1MUu7qygFwd0Vdl3C5c7GPLpSjJF9aUsaVOYmlbTcvCLQWS0NJwssLv7jqxE KTPgSqROjnddp7f499gUSsjJXXbJ04qbk5g/4l5I7IwNBl7lZiYPLHxgCw2WMOmjs+HG E43TyvA9YZ79luBcnmTb8+Ep0H6ZNdjEtAwRnlqCvCyaDa6J0yspz2fh5cmTpz8ONaIb AG5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent :content-language:to:references:cc:from:subject:in-reply-to :content-transfer-encoding; bh=wx7uMvQ+aSD5JlsyKxNV+eFRNUffjcTcNX0k+6eQcdc=; b=5sf4ZDr3g2hvY6Ip9ePfdmHCuk2dKzzqdEzuNzn9jGDrT8rGWf9YSG3kGAMsgP9AuX gulSv/fF05TRO1Dwwjq2eaoEiuSgwm7h5pGw89ZpFmlk3DtsyrnhnSGN69wkpZFMgR2u 0KJVZ8ODpZJfwMtfgB9kl2BPzrXkAuvLwQ0GNALWdoPftaGdaWs0BiVoKhWbkKr1PSzJ +n4JIHZJmZRMn0N+7IgWlA7/tvCeP1gDvCduDveIYAPMVIxWgfFJ70liZYhBC+hYc/RD O2ZMGcdWgQ7Cr5Fe0vWVLa5elxEv1orM2o/BnYwhIhq6lcQtUmyjlLB7z1EEw6/yOl/Q Wdpw== X-Gm-Message-State: AOAM531J+N2nUD/+o6dbSY5GBbg53Gg9Ow9BXnMU9eoZzT/93mmq7wLp unikq3fGUKWTjDwG+du7ptA= X-Google-Smtp-Source: ABdhPJwJ5aHmQ61YSw7DYMeBF4q4flZSlse6xJsom66jl9DCg0i9bozrBH6cDvBwrqUPSI5ikWRbGQ== X-Received: by 2002:a17:902:e801:b0:154:19ec:53a2 with SMTP id u1-20020a170902e80100b0015419ec53a2mr4040145plg.151.1648103513767; Wed, 23 Mar 2022 23:31:53 -0700 (PDT) Received: from [192.168.12.80] ([182.2.71.236]) by smtp.gmail.com with ESMTPSA id e3-20020a636903000000b003863620133bsm1123334pgc.77.2022.03.23.23.31.50 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 23 Mar 2022 23:31:52 -0700 (PDT) Message-ID: Date: Thu, 24 Mar 2022 13:31:48 +0700 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.5.0 Content-Language: en-US To: Anisa Elezi <19177366@brookes.ac.uk> References: Cc: Ammar Faizi , GNU/Weeb Mailing List From: Ammar Faizi Subject: Re: Please give me some help with assembly code! In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: On 3/24/22 12:32 AM, Anisa Elezi wrote: > Hi Ammar, > Hope you are well! I am good :)) > I am Anisa, a student of Computer Science. I am studying malware analysis and one of our coursework has to do with assembly language. I was looking at your videos but still I am really struggling with some points. Please use plain text mode and always CC the relevant list next time. Read also: https://people.kernel.org/tglx/notes-about-netiquette > 1. If I put the user ID as this format pXXXXXXX, which has p char and 7 digits, how can I check whether the input has this format or not ? Simply do a loop and check all of those characters. Start traversing from the first byte, make sure it contains char 'p', increment the pointer, deference in a byte, do cmp, and so on... That being said, the string consists of 9 bytes:    - char 'p'.    - 7 digit numbers.    - A NUL char (this is the mark for the end of string). Playing a bit with this on my Linux x86-64 System V ABI, the validate function looks like this (note, the registers usage may differ on a different platform/OS): ; bool validate_name(const char *name); ; ; --- System V ABI --- ; - The first argument goes in rdi. ; - The return value goes in rax. ; - rax, rdi, rsi, rdx, rcx are call-clobbered. ; - Returns 1 if username matches pXXXXXXX pattern, ; otherwise returns 0. validate_name: xor eax, eax ; Set return value to false. cmp byte [rdi], 'p' ; Is the first byte is 'p'? jne .out_ret ; Jump to .out_ret if the first ; character is not p, bail out! inc rdi ; Increment the pointer, start moving ; to the second byte. xor esi, esi ; Prepare a loop counter. .do_loop: mov dl, [rdi] ; Take one byte. cmp dl, '0' ; If it's below '0', then it's not ; a number. jb .out_ret ; Return false. cmp dl, '9' ; If it's above '9', then it's not ; a number. ja .out_ret ; Return false. inc esi ; Increment the loop counter. inc rdi ; Increment the string pointer. cmp esi, 7 jl .do_loop ; Jump back to the top if esi < 6 cmp byte [rdi], 0 ; Make sure the 8th byte is a NUL ; char (end of string). jne .out_ret ; Return false if it isn't a NUL char. mov eax, 1 ; Return true .out_ret: ret > 2. If I have added some user in an array , how can I delete a user with a specific ID ? Recall in C, we have a function called memmove(), this can do it. First, you need to find the index of that user data. After that you can simply do something like: if (num_of_elements == (idx + 1)) { /* The index is located at the end, just drop the counter */ num_of_elements--; } else { /* Shift all elements after that index to the front. */ memmmove(&array[idx], &array[idx - 1], sizeof(*arr) * num_of_elements - idx); num_of_elements--; } Feel free to use "rep movsb" for the memmove() implementation. > Here are the requirements of my coursework: > > Your system must be able to store the following information about computers - > /● Computer name > ● IP address > ● OS (can be any one of Linux, Windows, or Mac OSX) > ● User ID of main user > ● Date of purchase/ > / > /Your system must be able to store the following information about people - > ● /Surname name > ● First Name > ● Dept (can be any one of Development, IT Support, Finance, or HR) > ● User ID > ● Email address/ > / > /Your system must allow the following operations – > ● /Add/delete user > ● Add/delete computer > ● Search for computer given a computer name > ● Search for a user’s given a user ID > ● List all users > ● List all computers/ > / > /You should make the following assumptions about the system - > /● First names and surnames, all have a maximum size of 64 chars > ● Computer names are unique, and are in the form of cXXXXXXX where XXXXXXX is any 7 digit number > ● User IDs are unique, and are in the form of pXXXXXXX where XXXXXXX is any > 7 digit number > ● Email addresses are in the form @helpdesk.co.uk > ● There is a maximum of 100 users and 500 computers on the system/ > /--------------------------------------------------------------------------------------------------------/ > /Here is also a basic code that I found related to this coursework but i really struggled on this:/ > /; This include line is an absolute path to the I/O library. You may wish to change it to suit your own file system. > %include "/home/malware/asm/joey_lib_io_v6_release.asm" > > ; "global main" defines the entry point of the executable upon linking. > ; In other words, "main" defines the point in the code from which the final executable starts execution. > global main > > ; The ".data" section is where initialised data in memory is defined. This is where we define strings and other predefined data. > ; This section is read/write but NOT executable. If it were executable, someone could modify the data to be malicious executable code and then execute it. > ; We don't want that! See "Data Execution Prevention (DEP)". > ; "db" means "Define Byte", which allocates 1 byte. > ; We could also use: > ; "dw" = "Define Word", which allocates 2 bytes > ; "dd" = "Define Doubleword", which allocates 4 bytes > ; "dq" = "Define Quadword, which allocates 8 bytes > section .data >     str_main_menu db 10,\ >                             "Main Menu", 10,\ >                             " 1. Add User", 10,\ >                             " 2. List All Users", 10, \ >                             " 3. Count Users", 10,\ >                             " 4. Exit", 10,\ >                             "Please Enter Option 1 - 4", 10, 0 >     ; Note - after each string we add bytes of value 10 and zero (decimal). These are ASCII codes for linefeed and NULL, respectively. >     ; The NULL is required because we are using null-terminated strings. The linefeed makes the console drop down a line, which saves us having to call "print_nl_new" function separately. >     ; In fact, some strings defined here do not have a linefeed character. These are for occations when we don't want the console to drop down a line when the program runs. >     str_program_exit db "Program exited normally.", 10, 0 >     str_option_selected db "Option selected: ", 0 >     str_invalid_option db "Invalid option, please try again.", 10, 0 >     str_enter_surname db "Enter surname:", 10, 0 >     str_enter_forename db "Enter forename:", 10, 0 >     str_enter_age db "Enter age:", 10, 0 >     str_enter_id db "Enter ID:", 10, 0 >     str_array_full db "Can't add - storage full.", 10, 0 >     str_number_of_users db "Number of users: ", 0 > >     ; Here we define the size of the block of memory that we want to reserve to hold the users' details >     ; A user record stores the following fields: >     ; forename = 64 bytes (string up to 63 characters plus a null-terminator) >     ; surname = 64 bytes (string up to 63 characters plus a null-terminator) >     ; Age = 1 byte (we're assuming that we don't have any users aged over 255 years old. Although if we entered Henry IV, this may be a problem!) >     ; User ID = 64 bytes (string up to 63 characters plus a null terminator) > >     ; Total size of user record is therefore 64+64+64+1 = 193 bytes >     size_user_record equ 193 >     max_num_users equ 100 ; 100 users maximum in array (we can make this smaller in debugging for testing array limits etc.) >     size_users_array equ size_user_record*max_num_users ; This calculation is performed at build time and is therefore hard-coded in the final executable. >     ; We could have just said something like "size_users_array equ 19300". However, this is less human-readable and more difficult to modify the number of users / user record fields. >     ; The compiled code would be identical in either case. > >     current_number_of_users dq 0 ; this is a variable in memory which stores the number of users which have currently been entered into the array. > > ; The ".bss" section is where we define uninitialised data in memory. Unlike the .data section, this data does not take up space in the executable file (apart from its definition, of course). > ; Upon execution, this data is initialised to zero. This section is read/write but NOT executable, for the same reasons as .data section above. > ; The syntax differs slightly from that of the .data section: > ; resb = Reserve a Byte (1 byte) > ; resw = Reserve a Word (2 bytes) > ; resd = Reserve a Doubleword (4 bytes) > ; resq = Reserve a Quadword (8 bytes) > section .bss >     users: resb size_users_array; space for max_num_users user records. "resb > > ; The ".text" section contains the executable code. This area of memory is generally read-only so that the code cannot be mucked about with at runtime by a mischievous user. > section .text > > add_user: > ; Adds a new user into the array > ; We need to check that the array is not full before calling this function. Otherwise buffer overflow will occur. > ; No parameters (we are using the users array as a global) >     push rbx >     push rcx >     push rdx >     push rdi >     push rsi > >     mov rcx, users ; base address of users array >     mov rax, QWORD[current_number_of_users] ; why QWORD >     ;value of current_number_of_users >     mov rbx, size_user_record ; 193 bytes >     ;size_user_record is an immediate operand since it is defined at build time. >     mul rbx ; calculate address offset (returned in RAX). >     ; RAX now contains the offset of the next user record. We need to add it to the base address of users record to get the actual address of the next empty user record. >     add rcx, rax ; calculate address of next unused users record in array >     ; RCX now contins address of next empty user record in the array, so we can fill up the data. > >     ; get forename >     mov rdi, str_enter_forename >     call print_string_new ; print message >     call read_string_new ; get input from user >     mov rsi, rax ; address of new string into rsi >     mov rdi, rcx ; address of memory slot into rdi >     call copy_string ; copy string from input buffer into user record in array >     ; get surname >     add rcx, 64 ; move along by 64 bytes (which is the size reserved for the forename string) >     mov rdi, str_enter_surname >     call print_string_new ; print message >     call read_string_new ; get input from user >     mov rsi, rax ; address of new string into rsi >     mov rdi, rcx ; address of memory slot into rdi >     call copy_string ; copy string from input buffer into user record in array >     ; get age >     add rcx, 64 ; move along by 64 bytes (which is the size reserved for the surname string) >     mov rdi, str_enter_age >     call print_string_new ; print message >     call read_uint_new ; get input from user >     ; inputted number is now in the RAX register >     mov BYTE[rcx], al ; we are only going to copy the least significant byte of RAX (AL), because our age field is only one byte >     ; get user id >     inc rcx ; move along by 1 byte (which is the size of age field) >     mov rdi, str_enter_id >     call print_string_new ; print message >     call read_string_new ; get input from user >     mov rsi, rax ; address of new string into rsi >     mov rdi, rcx ; address of memory slot into rdi >     call copy_string ; copy string from input buffer into user record in array > >     inc QWORD[current_number_of_users] ; increment our number of users counter, since we have just added a record into the array. >     pop rsi >     pop rdi >     pop rdx >     pop rcx >     pop rbx >     ret ; End function add_user > > > list_all_users: > ; Takes no parameters (users is global) > ; Lists full details of all users in the array >     push rbx >     push rcx >     push rdx >     push rdi >     push rsi > >     lea rsi, [users] ; load base address of the users array into RSI. In other words, RSI points to the users array. >     mov rcx, [current_number_of_users] ; we will use RCX for the counter in our loop > >     ;this is the start of our loop >   .start_loop: >     cmp rcx, 0 >     je .finish_loop ; if the counter is a zero then we have finished our loop >     ;display the user record >     mov rdi, rsi ; put the pointer to the current record in RDI, to pass to the print_string_new function >     ;display forename >     call print_string_new >     mov rdi, ' ' ; space character, between forename and surname. >     call print_char_new ; print a space >     ;display surname >     lea rdi, [rsi + 64] ; move the pointer along by 64 bytes from the base address of the record (the size of the forename string) >     call print_string_new >     call print_nl_new >     ;display age >     movzx rdi, BYTE[rsi + 128] ; dereferrence [RSI + 128] into RDI. 128 bytes is the combined size of the forename and surname strings. >                                                 ;We need to zero extend (movzx) because the age in memory is one byte and the RDI register is 8 bytes. >     call print_uint_new ; print the age >     call print_nl_new >     lea rdi, [rsi + 129] ; move the pointer along by 129 bytes from the base address of the record (combined size of the forename and surname strings, and age) >     call print_string_new >     call print_nl_new >     call print_nl_new >     add rsi, size_user_record ; move the address to point to the next record in the array >     dec rcx ; decrement our counter variable >     jmp .start_loop ; jump back to the start of the loop (unconditional jump) >   .finish_loop: > >     pop rsi >     pop rdi >     pop rdx >     pop rcx >     pop rbx >     ret ; End function list_all_users > > display_number_of_users: > ; No parameters > ; Displays number of users in list (to STDOUT) >     push rdi >     mov rdi, str_number_of_users >     call print_string_new >     mov rdi, [current_number_of_users] >     call print_uint_new >     call print_nl_new >     pop rdi >     ret ; End function display_number_of_users > > display_main_menu: > ; No parameters > ; Prints main menu >     push rdi >     mov rdi, str_main_menu >     call print_string_new >     pop rdi >     ret ; End function display_main_menu > > main: >     mov rbp, rsp; for correct debugging >     ; We have these three lines for compatability only >     push rbp >     mov rbp, rsp >     sub rsp,32 > >   .menu_loop: >     call display_main_menu >     call read_int_new ; menu option (number) is in RAX >     mov rdx, rax ; store value in RDX >     ; Print the selected option back to the user >     mov rdi, str_option_selected >     call print_string_new >     mov rdi, rdx >     call print_int_new >     call print_nl_new >     ; Now jump to the correct option >     cmp rdx, 1 >     je .option_1 >     cmp rdx, 2 >     je .option_2 >     cmp rdx, 3 >     je .option_3 >     cmp rdx, 4 >     je .option_4 >     ; If we get here, the option was invalid. Display error and loop back to input option. >     mov rdi, str_invalid_option >     call print_string_new >     jmp .menu_loop > >   .option_1: ; 1. Add User >     ; Check that the array is not full >     mov rdx, [current_number_of_users] ; This is indirect, hence [] to dereference >     cmp rdx, max_num_users ; Note that max_num_users is an immediate operand since it is defined at build-time >     jl .array_is_not_full ; If current_number_of_users < max_num_users then array is not full, so add new user. >     mov rdi, str_array_full ; display "array is full" message and loop back to main menu >     call print_string_new >     jmp .menu_loop >   .array_is_not_full: >     call add_user >     jmp .menu_loop > >   .option_2: ; 2. List All Users >     call display_number_of_users >     call print_nl_new >     call list_all_users >     jmp .menu_loop > >   .option_3: ; 3. Count Users >     call display_number_of_users >     jmp .menu_loop > >   .option_4: ; 4. Exit >     ; In order to exit the program we just display a message and return from the main function. >     mov rdi, str_program_exit >     call print_string_new > >     xor rax, rax ; return zero > >     ; and these lines are for compatability >     add rsp, 32 >     pop rbp > >     ret ; End function main/ > / > / > / > / > / > / -- Ammar Faizi