0x00
This chapter is dedicated to daily learning and note sharing to help everyone learn assembly language. Why learn assembly language? Because in red-blue confrontations, our tools are often detected and eliminated due to the presence of AV/EDR. Therefore, we need to counter AV, which involves evasion techniques. To learn evasion techniques, we must start from the basics. In the future, I may also share notes on C++, PE file structures, etc. Additionally, I might introduce knowledge related to reverse engineering.
0x01
Assignment 1:
assume cs:code
code segment
mov ax,0
mov cx,236
s:
add ax,123
loop s
mov ax,4c00h
int 21h
code ends
end
Assignment 2:
assume cs:codesg
codesg segment
mov ax,200h
mov ds,ax
mov bx,0
mov cx,64
s:
mov ds:[cx],cx
inc bx
loop s
mov ax,4c00h
int 21h
codesg ends
end
5. Programs with Multiple Segments
The previous programs only had one code segment. If a program needs to use other space to store data, the safe space 0:200~02ff is only 256 bytes, which may not be sufficient.
In the operating system environment, the space obtained legally through the operating system is safe. One way is to allocate space for the program when loading, and another is to request space from the system during execution. We will not discuss the second method in our learning.
If we want a program to obtain the required space when loaded, we must specify this in the source program by defining a segment to acquire memory space.
Using Data in the Code Segment
Consider the problem: programming calculation 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h The sum of these eight numbers, with the result stored in ax.
In previous lessons, we accumulated data from memory units without caring about the data itself. We want to accumulate the values of the given data, so we hope to use a loop to accumulate. Therefore, we need to store the data in a group of contiguous memory units.
To achieve this, we can use instructions to send them one by one into contiguous memory units. But where do we find this segment of memory? We cannot decide ourselves; we should let the operating system allocate it for us.
We can define the data we want to process in the program. This data is compiled and linked as part of the executable file. When the executable file is loaded into memory, this data is also loaded into memory, and the data to be processed naturally obtains storage space.
assume cs:code
code segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
mov bx,0
mov ax,0
mov cx,8
s:
add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end
In the first line of the program, dw means define word, which defines 8 word-sized data, occupying a total of 16 bytes of memory space.
Instructions in the program will accumulate these 8 data. Since they are in the code segment, the segment address of the code segment is stored in cs during program execution, so we can obtain their segment address from cs. Since the data defined by dw is located at the beginning of the code segment, the offset addresses are 0, 2, 4, 6, 8, A, C, and E respectively.
In the program, bx stores the incremented offset address by 2, and a loop is used to accumulate. Before the loop starts, (bx) is set to 0, and cs:bx points to the first data word. In each loop, (bx) is updated to (bx)+2, and cs:bx points to the next data word.
However, the entry point of the program is not the command we want to execute. We can specify the entry point in the source program.
assume cs:code
code segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
start:
mov bx,0
mov ax,0
mov cx,8
s:
add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
We add the label start before the first instruction of the program. This label appears after the pseudo-instruction end, which means that end not only indicates the end of the program but also informs the compiler that the entry point is at start.
In a single-task system, the execution process of the program in the executable file is as follows:
- Since other programs load the executable file into memory
- Set CS:IP to execute the program’s entry point
- After the program ends, return to the loader
Thus, we can arrange the framework of the program:
assume cs:code
code segment
Data
start:
Code
code ends
end start
Using Segments in the Code Segment
Complete the following program, using the stack to store the defined data in reverse order.
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
?
codesg ends
end
The general idea is as follows:
When the program runs, the defined data is stored in CS:0~CS:F units. We sequentially push the data from these 8 word units onto the stack, and then pop them back into these 8 word units to achieve the reverse storage of data. The problem is that we first need a segment of memory space that can be used as stack space, which should be allocated by the operating system. We can obtain a segment of space by defining data in the program and then use it as stack space.
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // Define 16 word-sized data with dw, which will obtain 16 words of memory space after the program is loaded.
start:
mov ax,cs
mov ss,ax
mov sp,30h // Set the stack top ss:sp to point to cs:30
mov bx,0
mov cx,8
s:
push cs:[bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0:
pop cs:[bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
Placing Data, Code, and Stack in Different Segments
Placing code and data in one segment seems chaotic. If the stack and the space required by code and data exceed 64KB, they cannot be stored in one segment. Therefore, just as we define a code segment, we define multiple segments, within which we define the required data or define data to obtain stack space.
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,20h
mov ax,data
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0: pop [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
code ends
end start
Referencing Segment Addresses
Now the program has multiple segments, and the segment name acts as a label representing the segment address. Therefore, mov ax, data means to load the segment address of the data segment into ax. When referencing the address of 0abch in the data segment, which is data:6, to load it into bx, we need:
mov ax,data
mov ds,ax
mov bx,ds:[6]
The code segment, data segment, and stack segment are all arranged by us, and the CPU does not recognize them. The assume directive is a pseudo-instruction, and cs, ds, ss will not connect with code, data, and stack segments. If we change it to the following form, the computer will process the result the same:
assume cs:b,ds:a,ss:c
a segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
a ends
c segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
c ends
b segment
d: mov ax,c
mov ss,ax
mov sp,20h
mov ax,a
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0: pop [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
b ends
end d
0x02
Previous Notes:
Assembly Language Day 04
Assembly Language Day 03
Assembly Language Day 02
Assembly Language Day 01
Basic Knowledge of Assembly Language
Previous Practices:
Domain Forest Breach from the Red Team Perspective: A Cross-Domain Control Attack and Defense Triggered by Shiro Deserialization
Practice – From Shiro Deserialization to Domain Control
“Nuclear Explosion Effect” After Domain Control Takeover: Permission Harvesting of 1600 Hosts Based on DCSync and Golden Ticket
Share
Collect
View
Like

Scan to Follow UsBecome an Excellent Network Security Guard