본문 바로가기

해킹 공부

[NOOBHACK] 어셈블리어 읽는 방법

자 오늘은 어셈블리어를 쉽사리 못 읽는 초보들을 위해 어셈블리어를 쉽게 읽는 방법을 알려주겠다

그전에 내 게시물을 제대로 안 본 사람들이 있다면 전 게시물을 보고 오시길 바랍니다

https://noobhack.tistory.com/25

 

[NOOBHACK] C언어를 어셈블리어로 변환하기

자 어셈블리어는 겁나 어렵고 읽기도 버겁다고 했습니다 하지만 포너블이나 해킹을 위해서는 꼭 필요한 요소로 작용하는데요 그런 어셈블리어를 읽는 방법을 알려주겠습니다 먼저 코드부터 보

noobhack.tistory.com

 

먼저 어셈블리어를 C언어 처럼 읽는건 짧은 코드에 한 해서만 그렇지 긴 코드는 약간의 노하우가 필요하다

어셈블리어 코드는 위에 링크에 있다 그 코드를 어셈블리어로 읽을 것이다

 

여기서 중요한 요령은 어셈블리어의 전체 코드를 이해 하는 것이 아니라 필요한 부분은 쏙 골라내서 이해하는 능력이 필요하다

 

읽기 전에 아래의 표를 보면서 읽으면 좋다

명령어   설명
MOV Move 데이터 이동 (전송)
PUSH Push 오퍼랜드의 내용에 스택을 쌓는다.
POP Pop 스택으로부터 값을 뽑아낸다.
XCHG Exchange Register / memory with Register 첫번째 오퍼랜드와 두번째 오버랜드 교환
IN Input from AL/AX to Fixed port 오퍼랜드로 지시된 포트로부터 AX에 데이터 입력
OUT Output from AL/AX to Fixed port 오퍼랜드가 지시한 포트로 AX의 데이터 출력
XLAT Translate byte to AL BX:AL이 지시한 데이블의 내용을 AL로 로드
LEA Load Effective Address to Register 메모리의 오프셋값을 레지스터로 로드
LDS Load Pointer to DS REG←(MEM), DS←(MEM+2)
LES Load Pointer ti ES REG←(MEM), ES←(MEM+2)
LAHF Load AH with Flags 플래그의 내용을 AH의 특정 비트로 로드
SAHF Store AH into Flags AH의 특정 비트가 플래그 레지스터로 전송
PUSHF Push Flags 플래그 레지스터의 내용을 스택에 쌓음
POPF Pop Flags 스택으로부터 플래그 레지스터로 뽑음
명령어   설명
ADD Add 캐리를 포함하지 않은 덧셈
SBB Subtract with Borrow 캐리를 포함한 뺄셈
DEC Decrement 오퍼랜드 내용을 1 감소
NEG Change Sign 오퍼랜드의 2의 보수, 즉 부호 반전
CMP Compare 두 개의 오퍼랜드를 비교한다
ADC Add with Carry 캐리를 포함한 덧셈
INC Increment 오퍼랜드 내용을 1 증가
AAA ASCII adjust for Add 덧셈 결과 AL값을 UNPACK 10진수로 보정
DAA Decimal adjust for Add 덧셈 결과의 AL값을 PACK 10진수로 보정
SUB Subtract 캐리를 포함하지 않은 뺄셈
AAS ASCII adjust for Subtract 뺄셈 결과 AL값을 UNPACK 10진수로 보정
DAS Decimal adjust for Subtract 뺄셈 결과의 AL값을 PACK 10진수로 보정
MUL Multiply (Unsigned) AX와 오퍼랜드를 곱셈하여 결과를 AX 또는 DX:AX에 저장
IMUL Integer Multiply (Signed) 부호화된 곱셈
AAM ASCII adjust for Multiply 곱셈 결과 AX값을 UNPACK 10진수로 보정
DIV Divide (Unsigned) AX 또는 DX:AX 내용을 오퍼랜드로 나눔. 몫은 AL, AX 나머지는 AH, DX로 저장
IDIV Integer Divide (Signed) 부호화된 나눗셈
AAD ASCII adjust for Divide 나눗셈 결과 AX값을 UNPACK 10진수로 보정
CBW Convert byte to word AL의 바이트 데이터를 부호 비트를 포함하여 AX 워드로 확장
CWD Convert word to double word AX의 워드 데이터를 부호를 포함하여 DX:AX의 더블 워드로 변환
레지스터 이름 용도  
*AX 누산기 산술 연산에 사용(함수의 반환값 저장)  
*BX 베이스 레지스터 특정 주소 저장(주소를 담기위한 인덱스)  
*CX 카운트 레지스터 반복되는 특정 명령에 카운트 해준다(C언어의 for문 i?)  
*DX 데이터 레지스터 일반 자료 저장  
       
*BP 베이스 포인터 스택 내의 변수 값을 읽는데 사용  
*SP 스택 포인터 스택의 가장 끝 주소  
*IP 명령 포인터 다음 명령의 오프셋을 저장하며 CS 레지스터와 합쳐져 다음에 수행될 명령의 주소 형성

 

이제 어셈블리어를 한번 보자

오늘 읽을 어셈블리어 코드다

 

 

위에서 부터 차례대로 보자면 중요한 것들을 묶어 놓았다

왜 다 알아보지 않냐면 코드를 실행할 때 우리가 필요한 부분만 이해를 하는 것이 중요하기 때문이다

 

빨간줄을 친 대로 차례로 알아보자

sub    rsp, 128h

먼저 어셈블리어를 실행하기 전에 sub이라는 빼기 명령어가 rsp에다가 128h를 빼는 것이 나오는데 숫자 옆에 붙어 있는 h는 hex라는 뜻으로 16진수로 128이라는 것이다.

이는 자료를 담고, 코드를 실행하기 위해 먼저 메모리 공간을 확보하기 위한 코드다 128h의 메모리 주소가 할당이 되는데 128h 만큼 메모리 공간이 확보 된 것이다

 

 

mov   dword ptr [a],0Ah

mov   dword ptr [b],28h

 

이 두가지 코드는 변수를 할당하는 코드인 것 이다.

근데 전 강의에서는 변수명 대신 주소로 변수를 구별하는 것 아니냐는 소리를 할 수 있겠지만

이 비주얼 스튜디오에서 지원하는 어셈블리어는 가독성을 위해 변수 구별을 해주는 것 같다 다른 칼리 리눅스에서는 주소로 각각 구별한다

 

어쨌든 이 두 코드는 각각 a는 10를 할당하고 b는 40를 할당하는 것인데 mov라는 명령어를 사용한다 mov는 

두번째 인자값을 첫번째 인자값 변수에 할당하라는 의미다

 

dword ptr은 해당 주소에서 32비트를 읽겠다는 뜻이다

 

 

 

mov   eax, dword ptr [b]

cmp   dword ptr[a], eax

jge     main+3Dh

이제 여기서 부턴 if문을 어셈블리어로 변환한 것이다 먼저 mov 명령어로 eax변수에 b의 값을 담고

cmp라는 명령어로 a와 eax(b)를 비교한다

그리고 만약 크기가 크거나 같을 시 main+3Dh로 점프하라는 명령어다

근데 main+3Dh로 점프하라는 소리가 대체 뭐냐면

 

끝 부분 2자리 숫자가 3Dh로 점프하라는 의미다

jge는 비교하는 숫자가 같거나 클 때에만 점프하므로 만약에 참이 아닐 시에는 점프를 안하고 계속 아래로 실행 하며 

printf문을 실행하는데 lea로 rcx에 "okay"라는 자료의 주소를 담아 call이라는 함수호출 명령어로 출력한다

 

그리고 나머지 위에 있는 함수의 어셈블리어는 연습 겸 읽어보길 바란다

 

이제 코드가 전부 끝나면 이렇게 마무리를 한다

참고 j**의 명령어의 모음이다

https://anow.tistory.com/94