반응형

C 코드를 어셈블리 코드로, 어셈블리 코드를 기계 코드로 바꿔보자.

 

콘솔 명령어 CMD를 실행하는 C 코드를 작성하고 빌드한다.

 

실행하면 콘솔 화면이 나타난다.

 

브레이크 포인트를 걸고 디버깅한다.

 

C 코드가 디스어셈블리된 코드를 확인할 수 있다.

 

 

디버거를 이용해 kernel32.dll의 WinExec() 주소를 확인한다. (0x75E3E120)

디버거로 확인한 WinExec() 주소는 재부팅 할 때마다 변경된다.

 

확인한 어셈블리 코드와 WinExec() 주소를 적당히 편집해 어셈블리 코드를 작성한다. 빌드하고 실행하면 콘솔 화면이 실행된다.

 

WinExec() 가 실행되기 전 코드에 브레이크 포인트를 걸고 디버깅한다. 이번엔 기계 코드를 확인한다.

 

DEP(Data Execution Prevention)를 No로 세팅한다.

 

 

기계 코드를 작성하고 빌드한다. 실행하면 콘솔 화면이 실행된다.

 

반응형
Posted by J-sean
:
반응형

C/C++과 어셈블리 모듈을 사용해 보자.

 

빈 프로젝트를 만들고 위와 같이 어셈블리 모듈 소스를 입력한다.

원래 0~4,294,967,295 사이의 정수만 반환하는 함수를 목적으로 작성되었으나 오류가 있는거 같다. 다시 확인해 보자.

 

C 소스를 입력한다.

 

이 상태에서 빌드하면 에러가 발생한다.

 

Solution Explorer - 어셈블리 소스 우클릭 - Properties를 클릭한다.

 

 

Property Pages에서 General - Item Type - Custom Build Tool을 선택하고 적용을 클릭한다.

 

Custom Build Tool - General에서 Command Line과 Outputs에 위와 같이 입력한다.

Command Line: ml /c /Cx /coff %(FileName).asm
Outputs: %(FileName).obj

 

빌드하고 실행한다.

 

반응형
Posted by J-sean
:
반응형

어셈블리 언어로 작성된 프로그램 실행 시 인수를 확인해 보자.

 

소스를 입력하고 빌드한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
include masm32rt.inc
 
.data
    strTitle db "Command Line Arguments",0
    strMessage db 128 dup(?)
 
.code
start:
    invoke GetCL, 1, ADDR strMessage
    invoke MessageBox, 0, ADDR strMessage, ADDR strTitle, MB_OK
    invoke ExitProcess, 0
end start
 

 

GetCL 함수의 첫 번째 인수로 1을 주면 프로그램의 첫 번째 인수가 저장된다.

 

첫 번째 인수 'abcd'가 출력된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
include masm32rt.inc
 
.data
    strTitle db "Command Line Arguments",0
    strMessage db 128 dup(?)
 
.code
start:
    invoke GetCL, 0, ADDR strMessage
    invoke MessageBox, 0, ADDR strMessage, ADDR strTitle, MB_OK
    invoke ExitProcess, 0
end start
 

 

GetCL 함수의 첫 번째 인수로 0을 주면 프로그램 파일명이 저장된다.

 

파일 이름 First.exe가 출력된다.

 

반응형
Posted by J-sean
:

RadASM Debugging 디버깅

Assembly 2023. 2. 2. 15:33 |
반응형

RadASM으로 어셈블리 소스를 디버깅 해 보자.

 

소스를 입력한다.

 

Project - Project Options를 클릭한다.

 

Debug를 선택한다.

 

빌드한다. Make - Build

 

 

원하는 위치에 브레이크 포인트를 설치한다. Make - Debug - Toggle Breakpoint

 

디버깅을 시작한다. Make - Debug - Run

 

레지스터등 디버깅 정보가 표시되고 브레이크 포인트에서 실행이 정지된다.

 

Run, Break, Stop, Step Into, Step Over등을 사용하여 디버깅 한다.

 

반응형
Posted by J-sean
:
반응형

비주얼 스튜디오로 어셈블리 디버깅 시 레지스터 창 플래그 정보가 이상하게 표시되어 그 의미를 알기 어렵다. 어떤 의미를 가지는지 확인해 보자.

 

 

Flags Set value
Overflow OV = 1
Direction UP = 1
Interrupt EI = 1
Sign PL = 1
Zero ZR = 1
Auxiliary carry AC = 1
Parity PE  = 1
Carry CY = 1

 

※ 참고 1

About the Registers Window in Visual Studio

 

※ 참고 2

디버깅시 레지스터 윈도우가 뜨지 않는다면 아래와 같이 설정을 변경한다.

 

Debug - Windows - Registers 선택

 

Registers 윈도우 우클릭 - Flags 선택

 

반응형
Posted by J-sean
:
반응형

비주얼 스튜디오로 어셈블리 프로젝트를 시작해 보자.

 

Empty Project를 만든다.

 

프로젝트에 적당한 파일을 add한다. 확장자만 asm으로 지정하면 된다.

 

Solution Explorer - Project Name 우클릭 - Build Dependencies - Build Customizations...

 

masm(.targets, .props)를 선택한다.

 

 

소스파일 우클릭 - Properties

 

Item Type - Microsoft Macro Assembler를 선택한다.

 

32비트 프로그램을 개발한다면 x86을 선택한다.

 

별도의 라이브러리를 사용한다면 아래와 같이 세팅한다.

Irvine.zip
1.30MB

 

Solution Explorer - Project Name 우클릭 - Properties

 

 

사용할 라이브러리의 디렉토리 경로를 입력한다.

 

라이브러리 이름을 입력한다.

 

Microsoft Macro Assembler - General - Include Paths에 라이브러리 경로를 한 번 더 입력한다.

 

소스를 입력한다.

 

 

빌드하고 실행한다.

 

Syntax Highlighter는 AsmDude나 ChASM을 사용한다.

 

ChASM - works with Visual Studio 2022

AsmDude - works with 2015, 2017, 2019

 

ChASM.vsix
0.27MB

 

※ 참고

 

Release 모드에서 SAFESEH 관련 LNK1281, LNK2026 에러가 발생할 수 있다.

 

Microsoft Macro Assembler - Advanced - Use Safe Exception Handlers를 Yes (/safeseh)로 바꾼다.

 

 

작업중인 Sorce.obj 파일의 에러는 해결되었지만 라이브러리 파일의 에러는 해결되지 않았다.

 

Linker - Advanced - Image Has Safe Exception Handlers를 No (/SAFESEH:NO)로 바꾼다.

 

에러가 사라졌다.

 

※ 참고

리스트 파일을 생성해야 한다면 아래와 같이 세팅한다.

 

Microsoft Macro Assembler - Listing File - Assembled Code Listing File - 파일 이름을 지정한다.

 

 

프로젝트를 빌드하면 리스트 파일이 생성된다.

 

반응형
Posted by J-sean
:
반응형

CMOVcc (Conditional Move) 명령어를 사용해 보자.

 

CMOVE - 같으면(ZF=1) 이동한다.

 

CMOVNE - 같지 않으면(ZF=0) 이동한다.

 

eax와 ebx가 같지 않기 때문에 ecx에 eax 값 1이 저장된다.

 

eax와 ebx가 같지 않기 때문에 ecx에 eax 값 1이 저장되지 않는다.

 

반응형
Posted by J-sean
:
반응형

Intel® 64 and IA-32 Architectures Software Developer’s Manual을 확인해 보자.

 

 

RDTSC—Read Time-Stamp Counter

Reads the current value of the processor’s time-stamp counter (a 64-bit MSR) into the EDX:EAX registers. The EDX register is loaded with the high-order 32 bits of the MSR and the EAX register is loaded with the low-order 32 bits. (On processors that support the Intel 64 architecture, the high-order 32 bits of each of RAX and RDX are cleared.) The processor monotonically increments the time-stamp counter MSR every clock cycle and resets it to 0 whenever the processor is reset.

RDTSC 인스트럭션은 프로세서의 time-stamp counter를 EDX:EAX 레지스터로 가져온다. EDX에는 상위 32비트가 저장되고 EAX에는 하위 32비트가 저장된다.

 

EDX, EAX에 저장되는 값을 확인해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
 
    PRINT_STRING 'High-order 32 bits of Time Stamp Counter: '
    PRINT_HEX 4, edx
    NEWLINE
    NEWLINE
    
    PRINT_STRING 'Low-order 32 bits of Time Stamp Counter: '
    PRINT_HEX 4, eax
    NEWLINE
    NEWLINE
    
    PRINT_STRING 'Time Stamp Counter: '    
    shl rdx, 32
    xor rdx, rax
    PRINT_HEX 8, rdx
 
    xor rax, rax
   ret
 

 

 

EDX, EAX의 값을 확인한다.

 

EAX 레지스터에 저장된 값을 이용해 1~10까지의 랜덤 넘버를 추출해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
    
    ;eax에 저장되는 값을 사용한다.
    PRINT_STRING 'Low-order 32 bits of Time Stamp Counter (dividend): '
    PRINT_HEX 4, eax
    NEWLINE
    NEWLINE
    
    ;2byte 이상 나누기 연산시 div 는 dx, ax를 사용한다.
    ;나누어지는 값을 dx:ax에 나누어 삽입해야 하므로 edx를 0으로 만들고 eax의
    ;값 중 2바이트만 사용한다.
    xor edx, edx
    and eax, 0x0000ffff
    PRINT_STRING 'DX: '
    PRINT_HEX 2, dx
    NEWLINE
    PRINT_STRING 'AX: '
    PRINT_HEX 2, ax
    NEWLINE
    NEWLINE
    
    mov bx, 0xa ;나누는 수를 10으로 설정한다.
    PRINT_STRING 'Divisor: '
    PRINT_HEX 2, bx
    NEWLINE
        
    div bx ;bx(10)로 나눈다. 나머지는 0~9가 나온다.
    PRINT_STRING 'Quotient: '
    PRINT_HEX 2, ax ;몫은 ax에 저장된다.
    NEWLINE
    PRINT_STRING 'Remainder: '
    inc dx
    ;나머지는 dx에 저장된다. 1을 더해서 범위를 1~10으로 맞춘다.
    PRINT_HEX 2, dx
    
    xor rax, rax
   ret
 

 

 

 

Remainder로 1~10의 숫자가 무작위로 추출된다.

 

그런데 실제로 여러번 해 보면 홀수만 나오는 것을 알 수 있다. 내 컴퓨터에서만 그러는건지는 모르겠지만 rdtsc 명령어가 (EAX 레지스터에)짝수만 반환하기 때문이다. (마지막에 1을 더하므로 결국 홀수가 된다)

 

EDX 레지스터의 값을 사용해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
    
    ;edx에 저장되는 값을 사용한다.
    PRINT_STRING 'High-order 32 bits of Time Stamp Counter (dividend): '
    PRINT_HEX 4, edx
    NEWLINE
    NEWLINE
    
    ;2byte 이상 나누기 연산시 div 는 dx, ax를 사용한다.
    ;나누어지는 값을 dx:ax에 나누어 삽입해야 하므로 edx의 값을 eax에 저장하고
    ;edx를 0으로 만든다. 그리고 eax의 값 중 2바이트만 사용한다.
    mov eax, edx    
    xor edx, edx
    and eax, 0x0000ffff
    PRINT_STRING 'DX: '
    PRINT_HEX 2, dx
    NEWLINE
    PRINT_STRING 'AX: '
    PRINT_HEX 2, ax
    NEWLINE
    NEWLINE
    
    mov bx, 0xa ;
    PRINT_STRING 'Divisor: '
    PRINT_HEX 2, bx
    NEWLINE
        
    div bx
    PRINT_STRING 'Quotient: '
    PRINT_HEX 2, ax
    NEWLINE
    PRINT_STRING 'Remainder: '
    inc dx
    PRINT_HEX 2, dx
 
    xor rax, rax
   ret
 

 

 

홀수, 짝수 상관없이 1~10의 숫자가 무작위로 추출된다.

하지만 EDX 레지스터의 값은 일반적인 시간의 '초' 단위와 비슷한 간격으로 바뀐다. 프로그램을 빠르게 반복 실행하면 동일한 숫자가 반복되어 나오는걸 알 수 있다. 빠른 반복 추출이 필요하다면 EAX 레지스터의 값 중 하위 2바이트가 아닌 중간 2바이트 숫자를 이용하는 등 다른 방법으로 랜덤 넘버를 추출한다.

 

※ 주의

DIV 연산시 나누는 수가 작아 몫이 너무 크게 되면 ax가 저장할 수 있는 용량(2바이트)을 넘어서게 되고 Program received signal SIGFPE, arithmetic exception 메세지가 나타난다. Floating-Point Exception을 의미하지만 0으로 나누는 연산과 같은 모든 산술 연산 에러를 포함한다.

Program Error Signals

 

SIGFPE 예외 발생시 프로그램이 멈추게 된다. 디버깅해야 메세지를 확인 할 수 있다.

 

반응형
Posted by J-sean
: