어셈블리 프로그래밍 09(09-01,09-02,10-01)
1. Runtime Stack
Stack은 procedure(함수), parameter parsing, local variable을 사용할때 Stack을 사용하게 된다.
Stack (Last In First Out(LIFO))
- Stack (Built-in Stack으로 하드웨어에 만들어져 있는 스택이다.)
- Objects are only added to the top
- Objects are only removed from the top top bottom
- PUSH 하면 ESP 감소, pop하면 ESP 증가
- low addr, high addr 위치를 잘 확인하자 교수님은 low addr이 위쪽인데 교과서는 low addr이 아래쪽이다.
Runtime Stack
-
A stack managed by the CPU using two registers
-
SS (stack segment)
-
ESP (stack pointer, protected mode) or SP (real-address mode)
- Stack top을 가리키고 있음.
-
Stack 크기는 가변 아니고 고정.
- .stack directive로 설정 (.lst 파일에 크기 보임). -> Irvine.inc에 포함되어 있다. 그런데 이걸 조절할 수 있다.
- .stack directive로 설정 (.lst 파일에 크기 보임). -> Irvine.inc에 포함되어 있다. 그런데 이걸 조절할 수 있다.
-
PUSH Operation
- A 32-bit push operation
- decrements the stack pointer by 4 and
- copies a value into the location pointed to by the stack pointer.
- A 32-bit push operation
-
After pushing two more integers:
여기에서는 ESP가 PUSH시 감소하고 POP시 증가한다.(교과서적) -
The stack grows downward (address가 감소하는 방향).
-
The area below ESP is always available (unless the stack has overflowed).
-
POP Operation
- Copies value at stack[ESP] into a register or variable.
- Adds n to ESP, where n is either 2 or 4.
- The value of n depends on the attribute of the operand receiving the data.
-
PUSH and POP Instructions
- PUSH syntax:
- PUSH r/m16 : register or memory 16bit
- PUSH r/m32 : register or memory 32bit (ESP <- ESP - 4)
- PUSH imm32 : immediate 32bit 가능
- POP syntax:
- POP r/m16 : register or memory 32bit (ESP <- ESP + 2)
- POP r/m32 : register or memory 32bit (ESP <- ESP + 4)
- PUSH syntax:
Using PUSH and POP
가장 많이 사용하는 것은 데이터를 save하기 위해서 사용한다.
- Save and restore registers when they contain important values.
- PUSH and POP instructions occur in the opposite order.
DumpMem를 사용하기 위해서는 esi, ecx, ebx의 값들을 각각 채워주어야 한다. 그런데 만약에 esi, ecx, ebx에 중요한 값이 들어가 있다면 함부로 이것을 바꿀 수 없다. 그래서 이 값을 미리 다른 곳에 저장하기 위해서 stack을 사용하게 된다.
그래서 주요 사용처는 1. 함수 호출 할때, 2. 중첩 loop 돌때라고 생각할 수 있다.
또한 할 수 있으면 함수를 저장할 때 document(주석)을 통해 영향받는 register들을 명시해 주어야 한다.
-
Nested Loop
주의할것은 push를 했다면 반드시 pop가 있어야 한다. 만약에 pop의 짝이 안맞는다면 프로그램이 죽는다. -
Reversing a String
- Use a loop with indexed addressing
- Push each character on the stack
- Start at the beginning of the string, pop the stack in reverse order, insert each character back into the string
-
Algorithm(String을 거꾸로 하고 싶은 경우)
- 입력 예 : aName BYTE "I like BATTLEGROUNDS", 0
- 출력 : aName BYTE "SDNUORGELTTAB ekil I", 0
- Algorithm
- 문자열의 문자를 차례로 stack에 넣는다.
- Stack에서 문자를 꺼내 aName에 차례로 저장한다.
-
Reversing a String(source code(1))
단점은 추가 메모리를 사용한다.(srack에 있는 메모리 사용) -
Stack을 이용한 두 레지스터(또는 변수) 값 교환
- eax와 ebx 값 교환
xchg eax, ebx
와 동일 단 xchg는 두 operand가 memory operand일때 사용이 불가능했다. 하지만 push pop은 상관없이 사용가능하다.
- 변수/register <-> 변수/register 값 교환이 가능하다.
- eax와 ebx 값 교환
-
Reversing a String
- 만일 string size가 너무 커서 많은 양의 stack 메모리를 사용하고 싶지 않다면, 어찌 할까?
- C-like pseudocode로 작성한 알고리즘을 아래 보인다.(Inplace Algorithm)
- 위 방법은 두 값 교환에 임시 변수를 하나 사용하거나, 약간의 stack 메모리만 사용하면 되므로 효율적이다.
- 다음 쪽에 위 알고리즘을 사용한 어셈블리 코드를 보인다.
-
Reversing a String (using a little extra memory)
Related Instructions
- PUSHFD and POPFD
- push and pop the EFLAGS register : EFLAG를 저장할때 사용하는 함수이다. 중간에 연산으로 flag 값이 바뀌어야 할때 이 함수를 이용해서 값을 잠시 저장해놓을 수 있다.
- PUSHAD
- Pushes the 32-bit general-purpose registers on the stack
- Push order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI : 여기에 있는 레지스터를 모두 push한다.
- POPAD
- Pops the same registers off the stack in reverse order : 모두 pop한다.
- PUSHA and POPA
- Do the same for 16-bit registers : 16bit 레지스터인경우 PUSHAD, POPAD의 기능을 대체한다.
2. Defining and Using Procedures(함수)
Creating Procedures
- Large problems can be divided into smaller tasks to make them more manageable : 큰 문제를 작은 일로 나누어서 관리하기 쉽게끔 만든다.
- A procedure is the ASM equivalent of a Java or C++ function
- Following is an assembly language procedure named sample: sample PROC
이때 PROC-ENDP와 :
는 같은 것으로 보인다.
Documenting Procedures
어셈블리는 굉장히 기능 이해가 난해하다. 그래서 프로그램의 초반부에 함수기능, code 내에 detail한 과정을 작성해야 한다.
-
A description of all tasks accomplished by the procedure.
-
Inputs : Input parameters, their usage and requirements. : 입력 parameter을 받는 방법 작성
-
Outputs : Values returned by the procedure. : 출력은 어떻게 return하는가를 반드시 적어주어야 한다.
-
Requires: Optional list of requirements that must be satisfied before the procedure is called. : 필요한 사항이 있으면 반드시 적어주어야 한다.
-
Example
최소한 기능, 입력전달하는 방법, 출력받는 방법은 작성해주자, 기타주의사항, 함수내에서 사용하는 register까지
CALL and RET Instructions
- The CALL instruction calls a procedure
- Pushes offset of next instruction on the stack : 1. CALL은 return addr를 push
- Copies the address of the called procedure into EIP : 2. jump to function을 실행
- The RET instruction returns from a procedure
- Pops top of stack into EIP : pop return addr -> EIP
- Example
- 00000025 is the offset of the instruction immediately following the CALL instruction : 00000025를 push해주고 EIP를 00000040으로 만들어준다.
- 00000040 is the offset of the first instruction inside MySub
이러한 이유로 recursion이 가능하다.
Nested Procedure Calls
왠만하면 iterative이 있으면 바꾸려고 노력한다. recursion를 사용하기에는 스택 사이즈가 4k밖에 안된다.
만약에 recursion으로 짠다면 반드시 종료조건이 자기자신을 호출하기 전에 체크되어야 한다.
Local and Global Labels
- A local label is visible only to statements inside the same procedure.
- A global label is visible everywhere.
- Example
::
으로 표시
Global label은 unique해야 하다. 하지만 가급적 사용하지 말아야한다. 남이 이해하기 힘듬
Procedure Parameters
-
A good procedure might be usable in many different programs, but not if it refers to specific variable names
-
Parameters help to make procedures flexible because parameter values can change at runtime
-
Parameter Passing
- 데이터 segment의 변수 이름 사용 : data label 사용(별로 좋은 방법은 아님 - 변수이름에서 충돌이 날 수 있다.)
- 함수 reusing을 고려하면 최악의 선택.
- Register 사용 : 가장 좋은 방법
- Stack 사용 : 파라미터를 push, pop하기
- C 언어와의 호환을 위해 사용(차후 설명)
- 데이터 segment의 변수 이름 사용 : data label 사용(별로 좋은 방법은 아님 - 변수이름에서 충돌이 날 수 있다.)
-
Return Value
- 단일 값일 경우 일반적으로 eax를 사용한다.
- 64 bit 데이터인 경우 edx, eax를 사용.
- 배열을 return할 경우 배열의 포인터를 eax에 저장하여 return. : parameter에 있는 것은 무조건 stack에 들어간다.
-
Example: A procedure which is not flexible
위와 같이 변수명 myArray, theSum을 사용하는 경우 별로 좋은 해결방법이 아니다. -
Using Registers
USES Operator
혹은 push, pop하는게 귀찮다면 아래와 같이 USES라는 directive를 사용하는 방법도 존재한다.
- Lists the registers that will be preserved
- Example
- MASM generates the code shown in gold:
그러면 어셈블러가 자동으로 위와 같은 코드를 작성해준다.
When not to push a register
- The sum of the three registers is stored to EAX at line (3). : 세개의 레지스터를 더해서 eax에 더하는 프로그램
- But the POP instruction replaces it with the starting value of EAX at line (4):
위의 프로그램은 eax값에 pop하다보니 좋은 프로그램은 아니다.
3. Link Library
What is a Link Library?
A file containing procedures that have been compiled into machine code
constructed from one or more OBJ files
To build a custom library, . . .
Start with one or more ASM source files
Assemble each into an OBJ file
Create an empty library file (extension .LIB)
Add the OBJ file(s) to the library file, using the Microsoft LIB utility
- LIB 명령은 VS2017에 대한 개발자 명령 프롬프트에서 실행. : LIB 명령어 사용하면 VS2017 에서 라이브러리에 집어넣을 수 있나보다.(~.libm, ~.dll(dynamic linnk library))
See help file by typing “LIB /HELP”.
보다 자세한 사용법은 MSDN library document를 검색, 참조.
Linking to a Library
- Your programs link to Irvine32.lib using the linker command inside a batch file named make32.bat.
- Notice the two LIB files: Irvine32.lib, and kernel32.lib
- kernel32.lib is part of the Microsoft Win32 Software
Development Kit (SDK)
kernel32.lib에 linking information이 들어가 있다. kernel32.dll에는 I/O function들이 포함되어 있어서 이 함수들을 실행한다. 문제는 얘들이 너무 사용하기가 어려워서 Irvine32.lib이 이걸 쉽게 만들어 주었다.
여기에서 한가지 더 차이점이 표준 입출력을 바꾸는 리다이렉션과 같은 경우 Irvine32에서는 지원을 안하지만 kernel32에서는 지원을 해준다.
lib와 dll의 차이점 https://it4all.tistory.com/16
The Book’s Link Library
- 어떤 함수들이 있는지 살펴보려면 Table 5-1 (pp. 188 ~ 189)을 참조한다.
- 배포한 help 파일 c05_IrvineLibHelp.chm를 더블 클릭하여 각 함수의 기본적인 사용법을 알 수 있다. : Help파일에 함수의 기본적인 사용법을 확인할 수 있다.
- 이 help 파일이 부족하면 5.4.3. Individual Procedure Descriptions를 참조한다(pp. 190 ~ 202).
- 필요한 함수에 대해 입력, 출력, 필요사항 등을 정확히 파악하고 사용하도록 한다.
- 연습용 프로그램을 작성하여 보는 것도 아주 좋은 방법이다.
Using The Book’s Link Library: An Example
- Problem Description
Write a program that
(i) prompts the user for multiple 32-bit integers, : 32bit를 입력받아서 합치고 출력하는 프로그램을 만들고 싶다.
(ii) stores them in an array,
(iii) calculates the sum of the array, and
(iv) displays the sum on the screen. - Input/Output Format
Enter a signed integer: 550
Enter a signed integer: -23
Enter a signed integer: -96
The sum of the integers is: +431 (출력함수 : WriteInt - Signed한 함수이다. Dec는 Unsigned하게 출력해준다.)
-
Procedure Design
-
Stub Program
- 앞 쪽의 프로그램 구상에 따라 main 함수, 필요한 procedure 등의 윤곽을 먼저 작성한다. : 필요한 함수들을 미리 작성만 해놓는다.
- 함수 간의 parameter passing(입출력 정의), 각 함수가 해야할 일 등을 주석으로 기록.
-
Stub 프로그램을 작성할 때도 어셈블 오류가 나오지 않도록 한다(나중의 편의를 위하여)
-
각자 프로그램을 완성해 보자.
64-Bit Assembly Programming(교과서 읽어보기만)
- The Irvine64 Library
- 교재 5.5.1 The Irvine64 Library를 살펴보자(pp. 210 ~ 211).
- Calling 64-Bit Subroutines
- Place input parameters in registers and execute the CALL instruction.
- Example:
- Use the PROTO directive at the top of the program to identify each procedure you plan to call .
- Example:
The x64 Calling Convention
- 교재 5.5.1 The Irvine64 Library를 살펴보자(pp. 210 ~ 211).
- Place input parameters in registers and execute the CALL instruction.
- Example:
- Use the PROTO directive at the top of the program to identify each procedure you plan to call .
- Example:
Microsoft follows a consistent scheme for passing parameters and calling procedures in 64-bit programs.
This is used by C/C++ compilers, as well as by the Windows Application Programming Interface (API).
When we call a function in the Windows API, or a function in C or C++, we need to follow this calling convention.
We do not need to use the Microsoft x64 calling convention when calling subroutines in the Irvine64 library.
The Calling Convention
1. The CALL instruction subtracts 8 from the RSP (stack
pointer) register, since addresses are 64-bits long.
2. The first four parameters passed to a procedure are placed in the RCX, RDX, R8, and R9, registers, in that order. Additional parameters are pushed on the stack, in left-to-right order.
3. It is the caller's responsibility to allocate at least 32 bytes of shadow space on the runtime stack, so the called procedures can optionally save the register parameters in this area.
4. When calling a subroutine, the stack pointer (RSP) must be aligned on a 16-byte boundary (a multiple of 16). The CALL instruction pushes an 8-byte return address on the stack, so the calling program must subtract 8 from the stack pointer.
- An Example Program
Before the OS called main, we assume the stack pointer was aligned on a 16-byte boundary.
When the OS called main, the CALL instruction pushed an 8-
byte return address.
Subtracting another 8 from the stack pointer(Line 10) drops it
down to a multiple of 16.
Line 11 reserves 32 bytes for shadow parameters.
An Example Program (계속)
Run time stack 내용
An Example Program (계속)
Rather than calling ExitProcess to end the program, we may execute a RET instruction.
In this case we must restore the stack pointer to the way it was when the main procedure began to execute.
Lines 21-22 can be changed as follows:
Author And Source
이 문제에 관하여(어셈블리 프로그래밍 09(09-01,09-02,10-01)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@tonyhan18/어셈블리-프로그래밍-0909-0109-02저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)