안녕하세요, 환영합니다.
이 동영상에서는 네이티브 EXE 파일의 멀웨어 분석을 위한 어셈블리 언어의 기본 사항에 대해 설명합니다.
스택이란 무엇인가요?
스택은 LIFO, 라스트 인, 퍼스트 아웃 데이터 구조의 약자입니다.
로컬 변수와 함수의 반환 주소를 저장합니다.
푸시, 팝, 콜, 레트 명령어를
통해 액세스하며 스택의 RAM 메모리 레이아웃은 다음과 같습니다.
여기 다이어그램과 같이 높은 주소에서 시작하여 더 많은 값을 푸시할수록 점점
더 작은 주소가 사용되며, 낮은 주소가 스택의 맨 위에 있습니다.
그래서 이 모든 것이 스택입니다.
여기는 RAM 메모리의 일부이며, -는 베이스 포인터로 스택 하단의 주소를 저장하는
레지스터입니다.
는 스택 상단의 주소를 저장하는 또 다른 포인터입니다.
따라서 이들은 모두 스택을 만드는 데 유용한 레지스터입니다.
힙이란 무엇인가요?
힙은 메모리를 전역적으로 저장하는 데 사용됩니다.
전역에 저장되므로 모든 함수가 액세스할 수 있습니다.
일반적으로 프로그램의 데이터 섹션에 저장됩니다.
이 API에서는 RTL
할당 힙을 사용하여 힙을 생성할 수 있습니다.
멀웨어는
힙을 사용할 모든 것을 저장하는 영역으로 사용하며, 이 API 기능에 대한 Google 설명에서
MSDN 도움말을 찾아볼 수 있습니다.
다음으로 CPU 레지스터를 살펴봅니다.
CPU 레지스터는 다음과 같은
레지스터로 구성됩니다. - 는 일반적으로 산술 연산을 위한 누산기로 알려져 있습니다.
- 는 데이터를 가리키는 기본 포인터입니다.
- 는 일반적으로 명령어 이동, 회전 및 반복을 위한 카운터입니다.
- 는 데이터 연산 및 입출력용입니다. - 는 소스 인덱스이며,
문자열 연산에서 소스에
대한 포인터입니다. - 는 문자열 연산에서 대상에 대한 대상 인덱스 포인터입니다.
- 는 앞서 살펴본 스택의 바닥을 가리키는 베이스 포인터입니다.
- 는 스택의 맨 위를 가리키는 스택 포인터이고, -는 실행할 다음 명령어의 주소를
저장하는 명령어 포인터입니다.
세그먼트 레지스터도 있습니다.
- 는 스택 포인터입니다.
- 는 코드 포인터입니다.
- 데이터 포인터 등을 사용할 수 있습니다.
중요한 것은 -, -, -이며, 이를 세그먼트 레지스터라고 합니다.
예를 들어 금전 등록기와 같은 등록기의 일부에 액세스할
수 있습니다.
이 예는 32비트 레지스터로, 4바이트로 구성되며 DWORD라고도 하는 4바이트, 32비트라고
합니다.
실제로 단어의 절반인 아래쪽 2바이트(16비트)에만 액세스하려는
경우 이 기호를 대신 사용할 수 있습니다.
가장 아래쪽 바이트, 즉 8비트에 액세스하려면 -를 사용하면 됩니다.
따라서 하위 바이트에 액세스합니다.
- 는 더 높은 바이트에 액세스합니다.
예를 들면 다음과 같습니다.
가 1, 2, 3, 4, 5, 6, 7, 8의 16진수 값을 가지고 있다고 가정합니다.
에 액세스하려는 경우 DWORD입니다.
단어에 액세스하려면 -를 사용합니다.
상위 바이트에 액세스하려면 -를 사용하고 하위 바이트에 액세스하려면 -를 사용합니다.
이것이 바로 여러분이 얻게 될 것입니다.
- 를 누르면 5, 6, 7, 8이 나옵니다.
- 를 누르면 5, 6, -를 누르면 7, 8이 나옵니다.
다른 모든 레지스터에도 동일하게 적용됩니다.
예를 들어 -에 액세스하는 경우 단어에 -을 사용하고, 낮은 바이트는 -, 높은 바이트는
-을 사용하는 식입니다.
깃발 등록부도 있습니다.
각 비트가 1 또는 0을 포함하는 플래그 역할을 하는 레지스터입니다.
중요한 것은 다음과 같습니다.
CF는 캐리 플래그입니다.
연산 결과가 대상 피연산자에 비해 너무 클 때 설정됩니다.
0 플래그는 연산
결과가 0과 같을 때 설정됩니다.
연산 결과가 음수인 경우 부호 플래그가 설정됩니다.
또한 단계별 디버깅을 수행하는 경우 트랩 플래그라고도
하는 DF 플래그가 설정되며, 멀웨어에 안티 디버깅 기능이 있는 경우 가끔 이를 감지할 수 있습니다.
어셈블리 언어 지침, 세 가지
주요 카테고리.
데이터 흐름(예:
이동 명령어).
제어 흐름(예: 푸시,
호출, 점프)을
제어할 수
있습니다.
산술 및 논리(예:
XOR, OR,
MUL, ADD).
데이터 전송 명령의 예는 이동입니다.
예를 들어 소스를
목적지로 이동하는 것이 목적입니다.
예를 들어 주소에 저장된 값을 - 레지스터로 이동합니다.
예를 들어 이동 제로 확장, 즉 소스에서 대상으로 이동을 의미하는 MOVZX입니다.
따라서 16진수 123을 -로 이동하고 상위 비트는 0으로 채웁니다.
LEA, 유효 주소(예: 소스에서 목적지까지)를 로드합니다.
에서 40인치 16진수를 뺀 뒤의 주소를 로드하고 결과
주소를 -에 저장합니다. XCHG는 두 레지스터 간의 값을 교환하는 것입니다.
예를 들어, 소스 및 대상 교환은 소스에 있는 모든 것을 대상으로, 대상에 있는 모든 것을
소스로 바꿉니다.
제어 흐름 명령의 예: 점프.
따라서 이것은 무조건 점프이며, 그 이전의 조건이 무엇이든 상관없이 점프합니다.
예: 이 주소에 저장된 값으로 이동합니다.
JZ는 조건부 점프입니다.
0 플래그가 1인 경우에만 점프합니다.
예: JZ 주소. 0이 아닌 점프, 0이 아닌 점프는 0 플래그가 0이면 점프합니다.
점프 앞에 테스트 또는 비교 명령어가 오는 경우.
그러나 점프는 무조건적인 점프이며 테스트나 비교를 전제로 하지 않습니다.
다음은 점프의 예입니다.
따라서 이 점프는 여기서 비교가 0과 같을 때만 발생합니다.
예를 들어 - 마이너스 -가 0이면 점프합니다.
이 JB는 아래 점프입니다.
따라서 아래의 점프는 -에서 저장된 값을 뺀 값과 C를 더한 값이 0보다 작을 경우에만 발생합니다.
그러면 점프합니다.
그리고 여기에서는 점프가 동일하지
않습니다.
점프가 같지
않음은 - 마이너스가 0이 아닌 경우에만 발생합니다.
그러면 해당 항목만 점프합니다.
산술 명령의 예: ADD, SUB, IMUL 및 INC.
예를 들어 소스에 대상을
추가하면 소스에 있는 내용을 대상에 추가하고 그 결과를 대상에
저장하는 것입니다.
SUB와 동일하지만 마이너스만 빼면 됩니다.
IMUL은 소스에 값을 곱한 다음 그 결과를 대상에 저장하는 곳입니다.
증분, INC는 레지스터에 있는 모든 항목을 1씩 증가시키는 것을 의미합니다.
다음은 그 예입니다.
로직 명령의 예
XOR은 소스, 소스, 대상과 같이 비트 단위의
XOR을 수행합니다.
그리고 여기서 왼쪽으로 이동합니다.
즉, 소스에 표시된 비트 수만큼 모든 비트를 왼쪽으로 이동합니다.
예를 들어 -가 1이면 여기서 모든 비트를 왼쪽으로 1씩 이동하고 비트 단위로 AND를 수행합니다.
따라서 여기서는 소스 및 대상을 사용하여 AND 연산을 수행합니다.
지침을 테스트하고 비교합니다.
여기서 두 피연산자에 대해 비트 단위 AND를 수행합니다.
결과가 0이면 0 플래그가 설정됩니다.
조건부 점프와 함께 자주 사용됩니다.
CMP는 첫 번째 연산과 두 번째 연산을 뺄셈으로 비교하는 것입니다.
여기서는 인자 1에서 인자 2를 뺀 예제입니다. 앞서 설명한 것처럼 점프 지침은 항상 테스트
또는 비교 직후에 나타납니다. 반환값: - 레지스터는 함수 호출의
반환값을 저장하는 데 사용됩니다.
반환 값은 정수이거나 0 또는 음수일 수 있으며, 모든 F는 16진수로 표시됩니다.
예를 들어 이 16진수 주소와 같은 주소일 수도 있습니다.
예를 들어 x64dbg의 이 출력을 살펴보겠습니다.
여기 푸시 7이 표시됩니다.
따라서 7을 누르면 7을 여기 스택으로 밀어넣은 다음 호출합니다. 이 함수를 호출하면 호출 결과를
-로 반환합니다.
여기까지 어셈블리 언어에 대한 입문서입니다.
시청해 주셔서 감사합니다.