자 오늘은 어셈블리어를 쉽사리 못 읽는 초보들을 위해 어셈블리어를 쉽게 읽는 방법을 알려주겠다
그전에 내 게시물을 제대로 안 본 사람들이 있다면 전 게시물을 보고 오시길 바랍니다
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**의 명령어의 모음이다
'해킹 공부' 카테고리의 다른 글
[NOOBHACK] off by One (OBO), 취약점 (0) | 2023.02.08 |
---|---|
[NOOBHACK] Out of Boundary (0) | 2023.02.08 |
[NOOBHACK] C언어를 어셈블리어로 변환하기 (0) | 2022.08.17 |
[NOOBHACK] 2진수의 메모리 한계점 (0) | 2022.08.17 |
[NOOBHACK] 어셈블리어 구동 방식과 명령어 (0) | 2022.08.17 |