Plage2000.c
Cholera.Bacterim.c
IISWormByQuantumG1999.c
GiftOfFuryByBumbleLee.c
프로세스가 메모리와 각종 자원에 대한 구별을 가진다면 스레드는 목적한 코드를 일정시간 동안 수행하는 실행 단위로 구별할 수 있으며,각각의 스레드가 가지는 독립적인 요소중 하나는 에러 핸들러이다. 각각의 스레드는 자신이 목적하는 코드를 수행하게 되며, 만약 이코드가 수행하던 중 예외적인 상황이 발생하게 되면 운영체제와 컴파일러는 이 예외적 상황을 처리할 수 있는 기회를 스레드에게 제공 하고 있다.C나 C++와 같은 컴파일러에서는 우리에게 _try,_except,catch,thro와 같은 키워드를 제공함으로써 예외적 상황에 대해 프로그래머가 처리할 수 있는 기회를 제공해 주고 있다. 하지만 여기서 간과해서는 안되는 일은 이는 컴파일만으로는 되지 않는다는 점이다. 즉, 어떠한 스레드에서 잘못된 메모리를 참조하는 것과 같은 행위를 하게 되면 마이크로프로세서에서는 예외가 발생하게 되며,이때 이 예외를 어떻게 처리하는지는 OS가 해주어야 하는 몫이다.Windows에서는 이러한 예외가 발생하면 그 예외를 발생시킨 스레드가 그에 대한 에러 처리를 할 수 있도록 해주고 있으며,이를 구조화된 예외처리라고 부른다. |
#include <windows.h> ULONG G_nValid; EXCEPTION_DISPOSITION __cdecl except_handler( //예외 처리 struct _EXCEPTION_RECORD *ExceptionRecord, void *EstablisherFrame, struct _CONTEXT *ContextRecord, void *DisatcherContext ) { ContextRecord->Eax = (ULONG)&G_nValid; return ExceptionContinueExecution; } int main(int argc,char *argv[]) { ULONG nHandler = (ULONG)except_handler; PCHAR pTest = (PCHAR)0; __asm //SEH 설치 { push nHandler //에러 핸들링을 수행할 함수 주소 push FS:[0] mov FS:[0],ESP } __asm { mov eax,0 mov [eax],'a' //SEH 발생 잘못된 메모리 참조 } __asm //SEH 제거 { mov eax,[ESP] mov FS:[0],EAX add esp,8 } return 0; } |
Typedef struct EXCEPTIONREGISTRATIONRECORD { DWORD prev_structure; //이전에 설치된 Exception handler DWORD ExceptionHandler; //해당 에러 핸들러 } |
Typedef enum _EXCEPTION_DISPOSITION{ ExceptionContinueExcution, //0 ExceptionContinueSearch, //1 ExceptionNestedException,//2 ExceptoinCollidedUnwind//3 } |
.386 include c:\masm32\include\windows.inc includelib c:\masm32\lib\user32.lib .data PUSH offset @DetectHardwareBPX ;Exception handler ; Fire SEH MOV EBX,DWORD PTR[OrgEbp] ;@exit 함수 주소 ; Check DRx registers CMP DWORD PTR DS:[EAX+4h],0 end start |
보호되어야 할 코드 XOR EAX,EAX XCHG DWORD PTR DS:[EAX],EAX |
Garbage코드 삽입 PUSH EAX MOV EBX,3 POP EAX SUB EBX,3 XOR EAX,EAX MOV EAX,0DEADh SHR EAX,4 XCHG DWORD PTR DS:[EAX],EAX |
#include <windows.h> int main(int argc,char *argv[]) |
Copyright(c) 1998-2008 A3 Security ,LTD
[출처] Anti-Reversing Code [4/4]|작성자 카이
Disclaimer
.386 include \masm32\include\windows.inc includelib \masm32\lib\user32.lib .data CALL IsDebuggerPresent CMP EAX,1 PUSH 40h JMP @exit PUSH 30h @exit: PUSH 0 end start |
.386 은 어떠한 인스트럭션 를 가질 것인까에 대한 정의 정도가 되겠습니다 위와 같이 .386이 되어 있다면 386의 인스트럭션을 가질 것이며 .486 .586 등이 존재 합니다 이와 같은 정의는 각각의 사양에 따른 아키텍쳐가 조금씩 차이가 나기 때문입니다.
(가령 386에서는 32비트 레지스터와 32비트 주소 버스 및 외부 데이터 버스의 특징을 가지고 있으며 펜티엄 계열 에서는 실행 속도를 향상시킨 새로운 마이크로 구조 설계를 기반 하는 차이 입니다 이에 대한 설명은 대부분의 어셈블리어 책에 초반부에 설명이 되어 있습니다)
다음은 .model 지시자은 메모리 모델에 해당하는 지시자 입니다. 윈도우는 항상 flat 모델을 쓰고 있으므로 flat이라 지정해주며 뒤에 stdll은 콜링컨베션중 스탠다드 콜에 해당하는 정의 입니다. Flat의 간단한 정의는 no segment, 32bit address,protected mode only 라고 정의 되어 있습니다. Option에 대해서는 잘 모르겠군요. 다음으로는 include입니다 이건 조금이라도 프로그래밍을 하신분들이 라면 아실꺼라 생각하고 생략합니다.
.data 지시자는 데이터 세그먼트를 지정하는 지시자 입니다. DB(Define Byte) 라고 해서 원바이트 형으로 데이터를 정의 하겠다는 뜻입니다. C에서는 char 형에 해당합니다. .code 지시자는 이제부터 코드 세그먼트를 지시하고 있다는 뜻입니다. 그뒤 start 는 일종의 코드 엔트리를 가리키는 레이블입니다.
코드 중에 @exit: 라고 정의 된 부분이 있고 그것을 호출하는 부분을 쉽게 정의 하기 위한 형태라고 생각 하시면 되겠습니다. 다른 부분은 일반적인 어셈 코드와 다를 바가 없습니다.
이제는 PEB 구조체와 TEB 구조체의 값을 간단히 살펴보겠습니다. 주로 windbg를 이용하여 확인 하지만 간단히 값을 확인 할때는 likekd를 이용하면 편합니다. Livekd는 다음 사이트에서 다운로드 받을수 있습니다.
http://www.sysinternals.com/utilities/livekd.html 이 툴의 동작 방식은 커널 메모리 덤프를 실시간으로 떠서 동작합니다. 단지 메모리 덤프를 통하여 커널을 분석할 때 유용한 툴입니다. 그전에 Debugging Tools for Widows가 먼저 설치 되어 있어야 합니다.참고로 dt명령어는 커널 구조체의 값을 확인할 때 쓰이는 명령어 입니다.
아래는 TEB 구조체 입니다.
아래는 PEB 구조체 입니다.
여기서는 0x030 오프셋 만큼의 위치에 PEB의 포인터가 하며 PEB의 0x002 오프셋 위치에 BeingDebugged 가 존재합니다. EDIT-PLUS 에서 설정한 컴파일 환경을 이용하여 위의 코드를 컴파일 하여 OLLY로 좀더 디테일 하게 알아 보겠습니다.
아래는 OLLY 로 열어본 IsDebugerPresent 입니다.
위의 코드에서 IsDebugerPresent()가 호출된뒤에 리턴값이 1이라면 디버거를 찾았다는 메시지를 띄워 준다는 걸 알 수 있습니다. 좀더 자세히 콜문의 어셈루틴을 알아 보겠습니다.
아래는 IsDebugerPresent call 세부 루틴 입니다.
위의 달랑 세줄에서 위에 설명한 것들에 대한 것을 실행합니다. 일반적으로 FS 세그먼트 영역은 데이터 세그먼트 일종이다. FS:[18]은 TEB의 위치를 가르키며 그 주소값을 EAX에 담고 Eax+0x30 은 PEB의 위치를 다시금 EAX 에 담고 다시 EAX+0X2 값 BeingDebugged 값을 담아서 리턴 하고 있다. Livekd를 이용하여 TEB의 주소와 올리에서 뿌려주는 주소값이 일치 함을 알수있다.
1.2 IsDebugerPresent 우회
이를 우회 하는 방법에는 여러 가지 방법이 존재합니다 우선 해당 플래그 값을 수정 하면 됩니다. 하지만 올리에서 해당 안티 디버깅 에 대한 플러그인 이 존재 합니다. 해당 플러그인은 다음 사이트에서 다운로드 받을수 있습니다.
http://www.openrce.org/downloads/details/111/IsDebuggerPresent
올리의 플러그인 폴더에 압축을 풀면 다음과 같은 플러그가 생기는 걸 확인 할 수 있습니다.
위 그림에서 Hide는 IsDebugPresent를 우회 한다는 말이고 Restore은 다시금 복원 한다는 의미 입니다. 각자 실행하여 정말로 돌아가는지 확인 해보시길 바랍니다.
[계속...]
참고사이트 및 문서
EDIT PLUS 를 이용한 MASM 환경 구축
http://mysilpir.net/entry/EditPlus-Assembly-%EC%84%A4%EC%A0%95-MASM
ANTI REVERSE
http://zesrever.xstone.org/
http://slaxcore.tistory.com
http://beist.org/research/public/artofunpacking/artofunpacking.pdf
http://openrce.org
정덕영님의 윈도우 구조와 원리
THX to zersrever,slaxcore,ashine,ap0x,정덕영FROM Hong10
Copyright(c) 1998-2008 A3 Security ,LTD
[출처] Anti-Reversing Code [2/4]|작성자 카이
Disclaimer
간단히 Enter 키를 누름으로 써 설치를 할 수 있다.
다음은 EDIT-PLUS에서 설정이다. 도구->기본설정->사용자도구 에서 그룹 과 도구 항목을 설정 할 수 있다.
아래는 이와 같은 설정을 나열 해 보여주고 있다.
Edit-Plus Assembly 설정 (MASM32)
1. Assemble 4. Debug |
http://www.editplus.com/dn.cgi?asm2.rar 여기서 다운 받은뒤 EDIT-PLUS 가 설치된 폴더에 복사 하면 된다 다음은 해당 구문강조에 대한 설정을 하는 장면이다.
해당 설정을 끝 마쳤다면 이제 실질적인 ASM 코드를 작성 하는 시간이다. 처음에는 간단한 문법 설명을 위하여 IsDebuggerPresent 라는 안티 디버깅 코드를 대상 으로 설명 하고 그외 안티 코드들을 설명할 때 덧붙이는 형식으로 진행한다. 이제 아래 그림처럼 새 파일을 하나 생성 하여 본다.
[계속...]
참고사이트 및 문서
EDIT PLUS 를 이용한 MASM 환경 구축
http://mysilpir.net/entry/EditPlus-Assembly-%EC%84%A4%EC%A0%95-MASM
ANTI REVERSE
http://zesrever.xstone.org/
http://slaxcore.tistory.com
http://beist.org/research/public/artofunpacking/artofunpacking.pdf
http://openrce.org
정덕영님의 윈도우 구조와 원리
THX to zersrever,slaxcore,ashine,ap0x,정덕영FROM Hong10
Copyright(c) 1998-2008 A3 Security ,LTD
Disclaimer [출처] Anti-Reversing Code [1/4]|작성자 카이
※ 현재 ㈜에이쓰리시큐리티에서 테스트 및 분석 중에 있으며, 이 문서는 계속 업데이트될 것입니다. 본 문서는 보안취약점으로 인한 피해를 최소화하는 데 도움이 되고자 작성되었으나, 본 문서에 포함된 대응방안의 유효성이나 기타 예상치 못한 시스템의 오작동 발생에 대하여서는 ㈜에이쓰리시큐리티에서는 일체의 책임을 지지 아니합니다
.386 include c:\masm32\include\windows.inc .data ASSUME FS:NOTHING PUSH 40h JMP @exit PUSH 30h @exit: PUSH 0 end start |
.386 include c:\masm32\include\windows.inc includelib c:\masm32\lib\user32.lib .data start: ASSUME FS:NOTHING MOV EAX,DWORD PTR FS:[18h] ;TEB PUSH 40h JMP @exit PUSH 30h @exit: PUSH 0 end start |
.386 include c:\masm32\include\windows.inc includelib c:\masm32\lib\user32.lib .data start: MOV [MinusOne],0FFFFFFFFh PUSH offset ntdll ;ntdll.dll PUSH offset zwqip ;NtQueryInformationProcess MOV [NtAddr],EAX MOV EAX,offset MinusOne PUSH 0 POP EAX TEST EAX,EAX PUSH 40h JMP @exit PUSH 30h @exit: PUSH 0 end start |
.386 include c:\masm32\include\windows.inc includelib c:\masm32\lib\user32.lib .data start: ASSUME FS:NOTHING ; Exception PUSH 30h PUSH 0 ; SEH handleing PUSH 40h PUSH 0 end start |
Copyright(c) 1998-2008 A3 Security ,LTD
Disclaimer
※ 현재 ㈜에이쓰리시큐리티에서 테스트 및 분석 중에 있으며, 이 문서는 계속 업데이트될 것입니다. 본 문서는 보안취약점으로 인한 피해를 최소화하는 데 도움이 되고자 작성되었으나, 본 문서에 포함된 대응방안의 유효성이나 기타 예상치 못한 시스템의 오작동 발생에 대하여서는 ㈜에이쓰리시큐리티에서는 일체의 책임을 지지 아니합니다.
[ASM] intel 8086 어셈블리 명령어 & 지시어 | RE | 2007/03/22 21:02 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
출처 - http://club.paran.com/multiloader 저자 - deVbug
옮긴이의 실력부족으로 그림은 옮기지 못하였습니다. ( 그대로 복사하니 안되더군요 )
deVbug의 멀티로더 만들기
1장 2번째 시간
사전지식
이번 시간엔 리버스 엔지니어링과 어셈블리어, 컴퓨터 구조에 대해서 공부해볼 것이다.
리버스 엔지니어링
여러분들은 크랙에 대해서 아는가?
크랙crack.
부수다 라고 해석할 수 있다. 부수다?
소프트웨어의 크랙은 본래의 내용을 부숴서 자기 마음대로 수정하는 것이라 할 수
있겠다. 그리고 크래킹의 세계에서 패치는 결국 크랙과 같은 의미로 쓰이고 있다.
크랙을 하기위해선 결국 궁극적으로 리버싱을 해야한다. 리버싱?
리버스reverse
뒤집힌 것이겠지. 정확한 뜻은 사전을 찾아보라.
리버싱은 리버스 엔지니어링을 하는 것을 일컫는다.
리버스 엔지니어링reverse engineering
역공학이라 해석한다. 완성되어진 실행파일 등을 분석해서 내부가 어떤식으로
수행되는지 분석하는 것을 역공학, 리버스 엔지니어링이라 할 수 있다.
그렇게 분석하여 우리가 원하는 대로 수정하는 작업이 크랙이자 패치가 되는 것이다.
여러분들은 No-CD 크랙(패치)이나 키젠(키제너레이터key generator) 등을
들어보았는가? 본인은 많이도 들어봤다.
이런 프로그램들이 바로 이 리버스 엔지니어링을 거쳐 완성된 것들이다.
내부를 분석해야 우리가 원하는 데로 하도록 할 수 있을 것 아닌가.
2진수, 10진수, 16진수 그리고 기계어와 어셈블리어
파일은 두가지로 나눌 수 있다.
Text 파일과 Binary 파일. 텍스트 파일은 텍스트 에디터로 열 때 제대로 볼 수 있는
파일이다. 바이너리 파일은 텍스트 에디터로 열어보면 완전히 깨져서 나오게 된다.
그림 파일을 하나 골라 열어보라. 볼 만할 것이다.
사실 텍스트 파일도 바이너리 파일이라 할 수 있다.
파일의 내용은 결국 숫자의 조합이거든. 파일은 sequence of bytes이다.
bytes는 byte들이고 byte는 8bit이다. 8bit는 뭘까?
01000001과 같은 것이다. 그리고 저 값은 A이다.
bit는 이것 아니면 저것을 의미하는 컴퓨터에서 쓰이는 단위이다.
bit는 컴퓨터에서 쓰이는 최소 단위이다. 0아니면 1, yes or no!
메모장에서 A만 입력한 뒤 저장해보자. 이를 헥사 에디터로 열어보자.
41
이라고 적혀있을 것이다. 아래와 같이.
41은 16진수이다. 이것을 2진수로 바꾸면, 01000001이다. 그리고 이것은 A이다.
텍스트 파일도, 바이너리 파일도 결국 내부에는 2진수로 저장되는 것이다.
단지 이 2진수가 특정 서식에 맞춰져 있는 파일이면 그 규칙에 따라 실행이
되고, 텍스트 파일은 그대로 읽어올 뿐이다.
아스키ASCII에 대해 들어보았는가?
ASCII(American Standard Code for information interchange)
해석하자면 정보 교환을 위한 미국 표준 코드이다.
아스키코드는 기본적으로 7바이트로 이뤄져있다.
A는 아스키코드에 1000001으로 지정되어있다. 이는 규칙이다.
10진수로 바꾸면 65가 되고 16진수로 바꾸면 41이 된다.
B는 당연히 1000010이겠지?
이렇게 제어코드부터 알파벳, 숫자에 몇몇 특수문자까지 미리 지정해두었다.
무슨 소릴하려고 이런 쓸데없는 소릴 한참이나 하고 있을까?
2진수의 집합인 파일이 저런 규약으로 정해진 문자들을 의미하는 것일 수도 있고
명령어를 의미하는 것일 수도 있다는 것이다.
16진수 90은 어셈블리어 명령어 nop이다.
16진수 EB는 어셈블리어 명령어 JMP이다.
이 둘은 나중에 매우 많이 쓰일테니 꼭 외워두라.
이런 명령어가 실은 CPU에 내장되어있는 명령어 세트의 한 명령을 의미한다.
어셈블리어는 결국 CPU에 내장되어있는 명령어와 1대 1 대응되는 것이다.
이는 CPU가 다르면 어셈블리어도 다르다는 의미이다.
이렇게 CPU에 내장되어 있는 명령어가 바로 기계어이고, 보통 2진수나 16진수로
표현된다. 그리고 이것을 사람이 좀 더 보기 좋게 해석하는 데에 쓰이는 언어가
어셈블리어이다.
초창기 프로그래밍 언어가 전무한 시절, 사람들은 기계어로 프로그램을 짰다.
지난 시간에 텍스트 에디터나 헥사 에디터로 열어본 사람은 알 것이다.
기계어는 결코 할만한게 아니라는 것을.
도저히 머리 빠질 지경이 되자 사람들은 좀 더 편하게 하기 위해 고민하게 됐다.
이렇게 등장한 것이 어셈블리어이고, 덕분에 어셈블리어는 기계와 매우 가깝다.
즉 기계에 종속적이다. 기계가 바뀌면 언어도 바뀐다.
이런 언어가 저급언어이다.
아무튼 여러분들은 어셈블리어를 알아야한다. 그것도 Intel계열로.
그러면 길이 열린다.
모든 파일은 숫자의 조합이라 했다. 그리고 그 숫자는 특정한 의미를 갖고.
그리고 특정한 서식에 의해 만들어진 파일(실행 파일)을 디스어셈블러에 넣고
돌리면 어셈블리어로 된 소스가 나온다고 지난 시간에 이야기했다.
이게 있어야 우리 맘대로 주무를 수 있다고도 이야기 했다.
여러분들은 또한 2진수, 8진수, 10진수, 16진수를 알아두라.
이정도의 연산(서로 바꾸는 등의)을 약간 할 줄은 알아야한다.
특히 2진수와 16진수, 알아보기 편하게 10진수도 함께 알아두면 좋다.
컴퓨터는 2진수를 쓴다고 했다. 왜 2진수를 쓰는지는 알고 있으리라 믿고 싶지만
대충 적어보자면, 기계는 전기로 작동한다. 전기는 전류가 흐른다, 흐르지 않는
다 이렇게 두가지 상태 밖에 표현을 못 한다.
즉 yes or no, 이것 혹은 저것, 0 아니면 1, 이렇게 흑백 두가지 밖에 표현하지
못 하는 것이다. 이렇기 때문에 2진수가 쓰이고 binary라고 불리고 digital이라고
불린다.
그런데 헥사 에디터를 보면, 0과 1로 이뤄진게 아니라 0부터 F까지 쓰이는 16진수
를 쓰고 있다. 이상한가?
사실 2진수로 표현하도록 할 수도 있지만 알아보기가 쉽지 않다. 거기다가 분량도
만만치 않게 나온다.
2진수 : 010001000011101011010000
10진수: 4471504
16진수: 443AD0
같은 내용이라도 분량이 줄어들어 있는게 보이는가?
게다가 2진수에서 16진수로는 쉽게 바꾸지만 10진수로는 바꾸기가 어렵다.
보기에도 2진수보다도 좋고 쓰기에도 좋으며 효율성도 좋다면 당연히 16진수를
쓰겠지?
아래 파트에서는 내용을 자세히 할지, 멀티로더 만드는 데만 필요한 것을
적을지 많이 고민을 했다.
준회원용 공개 강좌에서는 훑어보기만 하기로 했다.
부족하다 느껴지면 다른 책자나 인터넷 강좌, 다른 분들의 리버스 엔지니어링
강좌 등을 살펴 보기 바란다.
컴퓨터의 구조
컴퓨터는 입력장치, 출력장치, 연산장치 등으로 구성되어있다.
이중에 우리가 알아야할 것은 연산장치이다. 그 중에서도 중심에 있는 CPU에
대해서 살펴보자.
중앙처리장치CPU
Central Processing Unit
중앙처리장치는 컴퓨터의 두뇌에 해당하는 곳으로 입력장치로부터 받은 정보를
가공해서 출력장치로 보내주는 역할을 한다.
정확하게는 모든 연산과 제어를 하는 곳이다.
우리는 이 CPU에 명령을 보내고 CPU는 그걸 받아서 명령에 맞는 작업을 수행한다.
명령을 그럼 어떻게 보낼까?
그 때 쓰이는 것이 바로 프로그램이다.
프로그램은 CPU로 보낼 명령들의 집합체이다.
이런 명령은 내부에서 덧셈 연산기 하나만으로 뺄셈, 곱셈, 나눗셈 모두를 수행
한다. 신기한가? 나도 신기하다.
그리고 저 4칙 연산만으로 모든 작업을 수행한다.
그리고 모두들 산수는 배웠을 것이다. 덕분에 아래의 글을 이해할 것이다.
무언가를 더하는데, 당연히 더할 그 무언가가 필요하다는 것은 다들 알 것이다.
+ 만으로는 아무것도 못 한다.
2 + 3이어야 덧셈이고, 결과는 5라는 것을 알 수 있다.
이런 +와 같은 것을 연산을 의미하는 기호라는 뜻으로 연산자라 하고, 나머지
양쪽의 2와 3 같은 것은 연산을 당하는 쪽이라 하여 피연산자(operand)라고 부른다.
즉 모든 연산에는 피연산자가 하나 이상은 존재해야하는 것이다.
문제는 CPU 역시 연산을 하려면 피연산자를 입력받아야 하는 것인데, 우리들은
머리에 그 값들을 저장하지만 CPU는 어디에 저장을 할까?
컴퓨터에대해 약간 아는 사람들은 이렇게 답할지도 모른다.
램, 메모리.. 심지어 하드디스크.
하드디스크라고 생각한 사람은 반성하라.
CPU는 빛의 속도로 움직인다고 생각하자.
하드디스크는 거북이이다. 램은 토끼 정도?
당신이 옆에서 기어가는 거북이를 보면 무슨 생각이 드는가?
당연히 답답할 것이다.
그럼 우리보다도 빠른, 매우 빠른 속도로 연산을 하는 CPU가 하드디스크나 램을
보면 어떤 생각이 들까? 오만상 쥐어 패고 싶을 것이다.
값을 달라고 아무리 떠들어도 떠뜸떠뜸 이야기를 해대면 미친다.
즉 CPU는 그런 장치 말고 자신만의 초고속 저장장치가 필요하다.
그 장치가 바로 레지스터이다.
(캐시라고 생각했는가? 캐시는 아니다.)
레지스터는 연산에 필요한 값들이나 결과값, 플래그 값이나 오버플로 값 등을 잠깐
저장해두고 쓰는 곳이다.
필요한 값을 즉시 즉시 저장해서 쓰고 날리고 쓰고 날리고 하는 곳이다.
게다가 수학적인 연산도 가능한 곳이다.
그리고 용도에 따라서 여러개로 분류해서 CPU에 내장해두었다.
여러분들은 이것에 대해서 알아야한다.
하지만 여러분들은 멀티로더만 만들면 될 것 아닌가? 자세한 것은 넘어가고 종류만
보도록 하자.
범용 레지스터 : EAX, EBX, ECX, EDX
스택 레지스터 : ESP, EBP
포인터 레지스터 : ESI, EDI, EIP
플래그 레지스터
세그먼트 레지스터: CS, DS, SS, ES...
범용 레지스터가 많이 쓰이고 중요한 역할을 가지고 있다.
범용 레지스터
General Purpose Registers
16bits 시절에는 범용 레지스터가 2바이트였으나 32bits로 넘어오면서 4바이트가
됐고 이름도 AX, BX, CX, DX에서 Extended가 붙었다.
그 구조는 아래와 같다.
참고로 네모 한 칸에 4bits 이다. 즉 두 칸에 1byte이다.
* EAX 레지스터(Accumulator)
주로 곱셈, 나눗셈 자료의 입출력에 쓰이는 레지스터다. 다른 레지스터에 비해
자료의 입출력 속도가 빠르다.
* EBX 레지스터(Base)
주로 포인터의 역할을 지정한다. 포인터란 어떤 장소를 가리키는 것이다. 유식
하게 하자면 "간접 주소 지정"이라 하겠다.
자료의 입출력이 가능하다.
* ECX 레지스터(Count)
주로 반복문에서 카운터로 많이 사용되는 레지스터이다.
역시 자료의 입출력이 가능하다.
* EDX 레지스터(Data)
주로 곱셈, 나눗셈과 자료 입력에 많이 쓰이는 레지스터이다.
ABCD가 그냥 알파벳순으로 쓰고자 붙였다고 생각했는 사람 있는가?
모르면 다 그런다.
스택 레지스터
Stack Registers
* ESP 레지스터(Stack Pointer)
스택에 쌓아둔 자료의 마지막 위치를 기억하는 레지스터로 이 값은 PUSH와
POP 명령으로 바뀔 수 있다.
자료구조(특히 스택)에 대해서 공부한 사람은 쉽게 이해할 것이다.
* EBP 레지스터(Base Pointer)
간접 주소를 지정할 때 사용하는 레지스터로 보통은 다른 레지스터와 함께
사용된다.
포인터 레지스터
Pointer Registers
특정 주소를 가리킬 때 사용하는 레지스터로 많이 사용되지는 않는다.
* ESI 레지스터(Source Index)
주로 간접 주소지정에 사용되며 자료를 읽어올 때 오프셋 주소를 기리키게
된다.
* EDI 레지스터(Destination Index)
마찬가지로 주로 간접 주소지정에 사용되며 자료를 저장할 때 오프셋 주소를
가리키게 된다.
* EIP 레지스터(Instruction Pointer)
이 레지스터는 어떤 명령을 수행하고 다음으로 수행할 명령어가 있는 위치를
가리키는 레지스터이다.
플래그 레지스터
Flag Registers
플래그 레지스터는 명령어 실행 중 결과를 저장하는 레지스터로 임의로 변경
할 수 없다.
명령의 결과와 분기명령의 조건, 자리 올림의 표시 등을 가지고 있기에 플래그
레지스터는 상당히 중요한 레지스터라 할 수 있다.
* 플래그 레지스터의 모습
0 - Carry : 연산결과 자리 올림, 내림에 사용하는 플래그
1 - 1
2 - Parity : 연산결과 하위 8비트에서 1로 되어 있는 비트의 수가 짝수
이면 세트
3 - 0
4 - Auxiliary carry, 보조 캐리 플래그
5 - 0
6 - Zero : 연산결과 0이면 세트
7 - Sign : 부호 있는 연산에서 결과가 음수일 때 세트
8 - Trap : 하나의 명령이 실행될 때마다 INT 01h를 발생할 지 결정
9 - Interrupt : 인터럽트를 걸리게 할 것인지 결정. 세트되면 인터럽트 허용
A - Direction : 문자열 명령어에 사용되는 플래그, 세트되면 SI, DI 레지
스터 값이 증가. 문자열 비교나 이동할 때 왼쪽이나 오른
쪽으로의 방향을 결정
B - Overflow : 연산결과 값이 레지스터에 표현할 수 없을 만큼 클 경우 세트
CD - IOPL : 입출력 조작과 관련
E - NT : 386에서 추가된 Nested Task Flag
F - 0
10 - Resume : 386에서 추가된 Resume Flag
11 - VM : 386에서 추가된 Virtual Mode Flag
세그먼트 레지스터
Segment Registers
세그먼트를 관리하는 레지스터로 데이터 세그먼트, 스택 세그먼트 등을 쉽게 관리
하게 해주는 레지스터이다.
* CS 레지스터(Code Segment)
현재 프로그램의 명령어가 시작되는 곳을 가리키는 레지스터
* DS 레지스터(Data Segment)
데이터 세그먼트를 가리키는 레지스터. 즉, 자료가 저장되어 있는 곳 중 시작 위치
를 가리킨다.
* SS 레지스터(Stack Segment)
스택 세그먼트의 위치를 가리키는 레지스터이다.
* ES 레지스터(Extra Segment)
세그먼트의 시작 위치를 가리키며 문자열 명령어에 사용되기도 한다.
쓰다보니 세그먼트가 등장했다. 세그먼트와 오프셋에 대해서는 별로 중요치 않으니
그냥 넘어가자.
어셈블리어
기본 명령어
본 강좌에서는 2 address 방식의 명령어를 중심으로 설명한다.
어셈블리어의 명령어는 다음과 같은 구조를 이루고 있다.
[opcode] [destination operand], [source operand]
[명령어] [대상 오퍼랜드], [소스 오퍼랜드]
* PUSH: SP 레지스터를 조작하는 명령어 중의 하나로 스택에 데이터를 저장한다.
push
push eax ; eax 레지스터의 값을 스택에 집어 넣는다.
push 20 ; 20을 스택에 집어 넣는다.
push 40203F ; 메모리 주소 40203F의 값을 스택에 집어 넣는다.
* POP: 역시 SP 레지스터를 조작하는 명령어로 스택의 데이터를 꺼낸다.
pop
pop eax ; 맨 마지막에 스택에 집어넣은 값을 꺼내 eax 레지스터에 저장.
* MOV: 메모리나 레지스터 등의 값을 옮길 때 쓰인다.
move
mov eax,ebx ; ebx의 값을 eax로 옮긴다.
mov eax,20 ; 20을 eax에 옮긴다.
mov eax,dword ptr [40203F] ;메모리 주소 40203F의 값을 eax에 옮긴다.
* LEA: 대상 오퍼랜드의 값을 소스 오퍼랜드의 값으로 만든다.
load effective address
lea eax,ebx ; eax의 값을 ebx의 값으로 만든다.
* INC: 레지스터의 값을 1 증가 시킨다.
increase
inc eax ; eax의 값을 1 증가 시킨다.
* DEC: 레지스터의 값을 1 감소 시킨다.
decrease
dec eax ; eax의 값을 1 감소 시킨다.
* ADD: 레지스터의 값이나 메모리의 값을 더할 때 쓰인다.
add
add eax,ebx ; eax = eax + ebx
add eax,20 ; eax = eax + 20
add eax,dword ptr [40203F] ; eax = eax + 40203F의 값
* SUB: 레지스터의 값이나 메모리의 값을 뺄 때 쓰인다.
subtract
sub eax,ebx ; eax = eax - ebx
sub eax,20 ; eax = eax - 20
sub eax,dword ptr [40203F] ; eax = eax - 40203F의 값
* NOP: 아무 것도 하지 않는다.
no operation
* CALL: 프로시져(procedure)를 호출할 때 쓰인다.
call
call dword ptr [40203F] ; 메모리 주소 40203F을 호출한다.
* RET, RETN: 호출한 지점으로 돌아간다.
return
* CMP: 레지스터 끼리 혹은 레지스터와 값을 비교한다.
compare
cmp eax,ebx ; eax와 ebx를 비교한다.
cmp eax,20 ; eax와 20을 비교한다.
cmp eax,dword ptr [40203F] ; eax와 메모리 40203F의 값을 비교한다.
* JMP: 특정 위치로 무조건 점프한다.
unconditional jump
jmp dword ptr [40203F] ; 메모리 주소 40203F로 점프한다.
조건부 점프: CMP나 TEST와 같은 연산 결과에 따라 점프를 결정
* JE: CMP나 TEST 의 결과가 같다면 점프
jump if equal
* JNE: CMP나 TEST 의 결과가 같지 않다면 점프
jump if not equal
* JZ: 왼쪽 인자의 값이 0 이라면 점프
jump if zero
* JNZ: 왼쪽 인자의 값이 0 이 아니라면 점프
jump if not zero
* JL: 왼쪽 인자의 값이 오른쪽 인자의 값보다 작으면 점프
jump if less; signed
* JNL: 왼쪽 인자의 값이 오른쪽 인자의 값보다 작지 않으면 (크거나 같으면) 점프
jump if not less; signed
* JB: 왼쪽 인자의 값이 오른쪽 인자의 값보다 작으면 점프
jump if below; unsigned
* JNB: 왼쪽 인자의 값이 오른쪽 인자의 값보다 작지 않으면 (크거나 같으면) 점프
jump if not below; unsigned
* JG: 왼쪽 인자의 값이 오른쪽 인자의 값보다 크면 점프
jump if greater
* JNG: 왼쪽 인자의 값이 오른쪽 인자의 값보다 크지 않으면 (작거나 같으면) 점프
jump if not greater
* JLE: 왼쪽 인자의 값이 오른쪽 인자의 값보다 작거나 같으면 점프
jump if less or equal; signed
* JGE: 왼쪽 인자의 값이 오른쪽 인자의 값보다 크거나 같으면 점프
jump if greater or equal
논리 연산
논리 연산에도 여러가지가 있지만, 자주 쓰이는 and, or, not, xor, text만
다루도록 하겠다.
* AND: 대응되는 각 비트가 모두 1이면 1, 나머지는 모두 0
다음 표를 알아두라.
A B &
-----
0 0 0
0 1 0
1 0 0
1 1 1
mov eax,10
and eax,8
eax의 값은,
1010 & 1000 = 1000
8이다.
* OR: 대응되는 각 비트 중 하나라도 1이면 1, 모두 0이면 0
A B |
-----
0 0 0
0 1 1
1 0 1
1 1 1
* XOR: 대응되는 각 비트가 서로 다르면 1, 서로 같으면 0
A B ^
-----
0 0 0
0 1 1
1 0 1
1 1 0
* NOT: 각 비트를 반전시킨다.
A !
---
0 1
1 0
mov eax,8
not eax
eax의 값은,
1000을 반전시킨 0111이므로 7이다.
* TEST: 오퍼랜드에는 영향을 끼치지 않고 플래그만 세트시킨다.
여러분들은 어떤 멀티로더를 만들고 싶은가?
다음 시간엔 프로그래밍과 디버깅에 대해서 할 것이다.
역시 중요하다.. OTL
참고 사이트
dual5651님의 홈페이지
참고 서적
CRACK, 파워북 2000, Sky Hacker, Debugging Shock 공저(절판)
Computer Organization & Architecture : designing for performance - 6th ed, Pearson Education Inc 2003, William Stallings 저
글 옮긴이의 하고 싶은말..
조금 읽으면서.. -
참고 사이트가 듀얼님.. ^^ 너무 유명해 ..ㅋ
스택에서 데이터를 꺼내는데 쓰인다.
ex:) Pop eax :스택에 가장 상위에 있는 값을 꺼내애서 eax에 저장한다.
주의점 : Push 의 역순으로 값은 스택에서 Pop 된다.
Mov : 메모리나 레지스터의 값을 옮길 때[로 만들 때]쓰인다.
ex:) Mov eax,ebx :ebx 레지스터의 값을 eax로 옮긴다[로 만든다].
ex:) Mov eax,20 :즉석값인 20을 eax레지스터 에 옮긴다[로 만든다].
ex:) Mov eax,dword ptr[401F47] :메모리 오프셋 401F47 의 값을 eax에 옮긴다[로 만든다]
Lea : 오퍼렌드1의 값을 오퍼렌드2의 값으로 만들어준다.
ex:) Lea eax,ebx : eax레지스터의 값을 ebx의 값으로 만든다.
Inc : 레지스터의 값을 1증가 시킨다.
ex:) Inc eax : Eax 레지스터의 값을 1증가 시킨다.
Dec : 레지스터의 값을 1 감소 시킨다.
ex:) Dec eax : Eax 레지스터의 값을 1 감소 시킨다.
Add : 레지스터나 메모리의 값을 덧셈할떄 쓰임.
ex:) Add eax,ebx :Eax 레지스터의 값에 ebx 값을 더한다.
ex:) Add eax,50 :Eax 레지스터에 즉석값인 50을 더한다.
ex:) Add eax,dword ptr[401F47] : Eax 레지스터에 메모리 오프셋 401F47의 값을 더한다.
Sub : 레지스터나 메모리의 값을 뻇셈할떄 쓰임.
ex:) Sub eax,ebx : Eax 레지스터에서 ebx 레지스터의 값을 뺸다.
ex:) Sub eax,50
Eax : 레지스터에서 즉석값 50을 뺸다.
ex:) Sub eax,dword ptr[401F47] :Eax 레지스터에서 메모리 오프셋 401F47의 값을 뺸다.
Nop : 아무동작도 하지 않는다. : 90
Call : 프로시저를 호출할떄 쓰인다.
ex:) Call dword ptr[401F47] : 메모리 오프셋 401F47을 콜한다.
Ret : 콜한 지점으로 돌아간다.
Cmp : 레지스터와 레지스터혹은 레지스터 값을 비교하기위하여 쓰인다.
ex:) Cmp eax,ebx :Eax 레지스터와 Ebx 레지스터의 값을 비교한다.
ex:) Cmp eax,50 :Eax 레지스터와 즉석값 50을 비교한다.
ex:) Cmp eax,dword ptr[401F47]
:Eax 레지스터와 메모리 오프셋 401F47의 값을 비교한다.
Jmp : 특정한 메모리 오프셋으로 이동할떄 쓰인다.
ex:) Jmp dword ptr[401F47] :메모리 오프셋 401F47 로 점프한다.
조건부 점프: Cmp나 Test 같은 명령어의 결과에 따라점프한다.
Je : Cmp나 Test 의 결과가 같다면 점프
Jne : Cmp나 Text 의 결과가 같지 않다면 점프
Jz : 왼쪽 인자의 값이 0 이라면 점프
Jnz : 왼쪽 인자의 값이 0 이 아니라면 점프
Jl : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작으면 점프(부호있는)
Jnl : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작지 않으면(크거나 같으면) 점프 (부호있는)
Jb : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작으면 점프(부호없는)
Jnb : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작지 않으면(크거나 같으면) 점프 (부호없는)
Jg : 왼쪽 인자의 값이 오른쪽 인자의 값보다 크면 점프
Jng : 왼쪽 인자의 값이 오른쪽 인자의 값보다 크지 않으면 (작거나 같으면) 점프
Jle : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작거나 같으면점프 (부호있는)
Jge : 왼쪽 인자의 값이 오른쪽 인자의 값보다 크거나 같으면 점프
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
약 이정도의 명령어들이 가장 많이 나오는 것들임으로
최소한 위에 나온것들은 외워 두도록 하자.
3. 논리연산
이글에서는 5가지 논리연산에 대해서 쓸것이다.
논리연산자는 두 오퍼렌드의 값의 비트들을 대응시켜 명령에 따른 적절한 값을 구하여 첫번쨰 오퍼렌드의 값을 바꾸어 주는것이다.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
AND 연산
대응되는 비트가 둘다 1이면 결과는 1이고 그외의 결과들은 모두 0 이 된다.
ex:) MOV EAX,8
AND EAX,10 :위를 계산하기 위해 우선 두 오퍼렌드의 값을 2진수로 바꾸어 주면 8은 1000 이 되고 10은 1010 이 되고 AND 연산은 둘다 1이여야 1이 됨으로 결과는 1000 이 됩니다.
OR 연산
대응되는 비트중 하나가 1 또는 둘다 1이면 결과는 1이고 그외는 모두 0이 된다.
ex:) MOV EAX,8
OR EAX,10
:위를 계산하기 위해 두 오퍼렌드의 값을 2진수로 바꾸어 주면 8은 1000이 되고 10은 1010이 되고 OR 연산은 한쪽 또는 양쪽둘다 1이면 1이고그외는 모두 0 임으로 결과는 1010이 된다.
XOR 연산
대응되는 비트 중에서 한비트가 1이고 다른 비트가 0이면 1이 되고 두개의 비트가 1이면 0 이 되고 두개다 0 이어도 0이 된다.
ex:) MOV EAX,8
XOR EAX,10
:위를 계산하기 위해 두 오퍼렌드의 값을 2진수로 바꾸어 주면 8은 1000이 되고 10은 1010이 되고 XOR 연산은 한쪽만 1이어야 1임으로 결과는 10이 된다.
NOT 연산
NOT 연산은 오퍼렌드의 값을 반대로 하여 준다.
ex:) MOV EAX,10
NOT EAX
:위를 계산하기 위해 오퍼렌드의 값을 2진수로 바꾸어 주면 10은 1010이 되고 NOT 연산은 1 과 0을 반대로 하여 줌으로 결과는 0101 이 된다.
*Test 연산은 오퍼렌드에 영향을 주지 않으며 플래그만 세트 시키어 준다.
2.레지스터
범용 레지스터
(1) Eax 레지스터
누산기인 Eax 레지스터는 입출력과 거의 모든 산술연산에 사용된다. 곱셋과 나눗셈, 변환 명령어등은 반드시 Eax 레지스터를 필요하게 된다.
Eax 레지스터는 32bit의 레지스터이고 16bit 의 레지스터로 ax가 있다.
(ax는 왼쪽의 ah와 오른쪽의 al로 이루어져 있다)
(2) Ebx 레지스터
Ebx는 주소지정을 확대하기 위한 인덱스로서 사용될수 있는 유일한 범용 레지스터 이며, 다른 일반적인 계산 용도로도 쓰인다.
Ebx는 32bit 레지스터이고 16bit로 eb가 있다.
(eb는 왼쪽의 bh와 오른쪽의 bl로 이루어져 있다)
(3) Ecx 레지스터
Ecx는 루프의 반복 횟수나 좌우방향의 시프트 비트 수를 기억한다. 그외의 계산에도 사용된다.
Ecx는 32bit 레지스터이고 16bit로 cx가 있다.
(cx는 왼쪽의 ch와 오른쪽의 cl로 이루어져 있다.)
(4) Edx 레지스터
Edx는 몇몇 입출력 동작에서 사용 된다.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
플래그 레지스터
(1) OF [Over Flow]
산술연산후 상위 비트의 오버플로를 나타냄
(2) DF [Direction]
스트링 데이터를 이동하거나 비교할떄 왼쪽 또는 오른쪽으로의 방향을 결정한다.
(4) SF [Sign]
산술결과의 부호를 나타낸다.[0=양수,1=음수]
(5) ZF [zero]
산술연산 또는 비교동작의 결과를 나타낸다.
[0=결과가 0이 아님,1=결과가 0임]
(6) CF [Carry]
산술연산후 상위 비트로부터의 캐리 그리고 시프트 또는 회전동작의 결과 마지막 비트
내용을 저장한다.
(7) TF [trap]
프로세서가 단일 스텝 모드(single-step mode)를 동작할수 있도록 해준다.