반응형
문서 작성일 : 2008년 12월 19일
최종 수정일 : 2008년 12월 19일

Hong10님께서 작성하신 "Anti Reversing Code" 큰 주제를 바탕으로 4개로 나눠 작성하였습니다.



5.3 Hardware Break Point
이왕 인터럽터 나 SEH에 대해서 나온 만큼 브레이크 포인트에 대한 안티 리버싱 기술에 대하여 살펴 보겠습니다. 사실 위에서 언급한 Debugger interrupts만으로도 소프트 브레이크 포인터에 대한 탐지가 가능합니다. 물론 언급한 내용외에 코드를 체크하거나 체크섬을 검사하여 탐지를 할수 있습니다. 이번 장에서는 Hardware Break Point를 어떻게 탐지하며 우회를 할 것 인가에 대하여 다루겠습니다. 시작 하기에 앞서 먼저 디버그 레지스터리에 대한 이해가 필요합니다.

디버그 레지스터는 총 7개가 구성이 됩니다. (DR0~DR7) 또한 DR4와 DR5는 사용되 지 않고 있으며 DR0~DR3 까지는 브레이크 포인트가 걸린 주소값을 담는 용도 입니다. 그래서 하드웨어 브레이크 포인트 가 4개 밖에 존재 하지 않습니다.
이러한 하드웨어적인 방식은 DR0~DR3중 하나에 브레이크 포인트로 사용될 주소로 설정하여 준 후 DR7 레지스터에 브레이크가 발생하게 될 조건을 명시하게 됩니다. 좀더 중요한 DR7 레지스터리에 알아봅시다. 아래 출처에서 설명 되어 있어서 긁어 오듯이 설명 하겠습니다.
 

출처 http://slaxcore.tistory.com/entry/하드웨어-브레이크-포인트-탐지

DR7은 Debug Control Register 입니다. 브레이크 포인터 타입 및 활성화 여부 브레이크 포인트 길이 등이 저장 됩니다. 아래는 위 그림에서 RW/0~RW/3 의 설정값.
 



다음 그림은 LEN0~LEN3에 설정되는 브레이크 포인터 Data에 대한 길이 값이 저장됩니다.
 


또한 (L0~L3,G0~G3)은 새로운 브레이크 포인터에 대한 설정값등이 설정됩니다. 앞서 SEH에 대하여 간략하게 설명드렸습니다. 좀더 깊게 이해 하기 위하여 정덕영님의 Windows 구조와 원리 p162에 나와 있는 글로써 인용을 하겠습니다.
구조화 된 예외 처리(Structed Exception Handling)
 프로세스가 메모리와 각종 자원에 대한 구별을 가진다면 스레드는 목적한 코드를 일정시간 동안 수행하는 실행 단위로 구별할 수 있으며,각각의 스레드가 가지는 독립적인 요소중 하나는 에러 핸들러이다.
각각의 스레드는 자신이 목적하는 코드를 수행하게 되며, 만약 이코드가 수행하던 중 예외적인 상황이 발생하게 되면 운영체제와 컴파일러는 이 예외적 상황을 처리할 수 있는 기회를 스레드에게 제공 하고 있다.C나 C++와 같은 컴파일러에서는 우리에게 _try,_except,catch,thro와 같은 키워드를 제공함으로써 예외적 상황에 대해 프로그래머가 처리할 수 있는 기회를 제공해 주고 있다. 하지만 여기서 간과해서는 안되는 일은 이는 컴파일만으로는 되지 않는다는 점이다.
즉, 어떠한 스레드에서 잘못된 메모리를 참조하는 것과 같은 행위를 하게 되면 마이크로프로세서에서는 예외가 발생하게 되며,이때 이 예외를 어떻게 처리하는지는 OS가 해주어야 하는 몫이다.Windows에서는 이러한 예외가 발생하면 그 예외를 발생시킨 스레드가 그에 대한 에러 처리를 할 수 있도록 해주고 있으며,이를 구조화된 예외처리라고 부른다.

간단한 SEH 처리 과정을 코드와 더불어 설명하겠습니다.
SimpleSEH.cpp
 #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;

위의 코드는 사용자가 만든 SEH핸들러에 대한 처리를 정의하고 새로운 SHE를 발생시키기 위하여 0의 주소값을 가리키게 하여 SHE를 발생 시키는 코드입니다. SEH설치 부분을 먼저 살펴 보겠습니다.
 



현재 ESP 값이 기존의 FS:[0]값으로 가리키게 됩니다.이과정은 우리가 직접 넣은 push 명령으로 넣은 데이터들은 실제 Windows에서 EXCEPTOIN_REGISTRATION_RECORD라는 구조체로 정의하고 있는 데이터입니다. 구조체의 정의는 이렇습니다.
 Typedef struct
EXCEPTIONREGISTRATIONRECORD
{
DWORD prev_structure;  //이전에 설치된 Exception handler
DWORD ExceptionHandler; //해당 에러 핸들러
}

이런식으로 싱글리스트 형태로 이전의 Exception handler가 다음의 Exception handler를 가리키게 됩니다. 다음으로는 에러 처리에 해당하는 코드를 살펴보겠습니다. 코드를 보면 에러가 발생한 시점의 Eax 값을 전역변수 G_nValid에 담아서 ExceptionContinueExcecution을 리턴하고 있습니다. 이값은 0값으로 VC헤더 파일인 EXCPT.H에 정의 되어 있습니다.

 Typedef enum _EXCEPTION_DISPOSITION{
ExceptionContinueExcution, //0
ExceptionContinueSearch, //1
ExceptionNestedException,//2
ExceptoinCollidedUnwind//3
}


이러한 값은 악성 코드들에 많이 이용 됩니다. 보통 악성코드들은 앞서 살펴본 디버거 레지스터리 값을 바꿀 때 사용되어 지는 ContextRecord 을 이용하여 디버거 레지스트리의 값을 바꾸어 여러가지에 응용하며 또한 ExceptionContinuExecution(0) 값을 리턴함으로써 에러가 발생할 당시의 Register을 바꾸어 줄 수 있는데 보통  EIP 값을 수정하여 에러가 발생한 시점에 악성코드를 실행한 뒤(exception handler) EIP를 수정하여 보통의 OEP를 가장하는 기법을 이용합니다.
(실제로 디버거를 이용하여 디버깅을 할 때 OEP) 다음은 구조체 CONTEXT의 일부인 디버거 레지스터리(Context Record 구조체중 디버거 레지스터리)를 정의 하고 있는 부분입니다.
 



Hardware Break Point 탐지에 필요한 개념을 이해 하였으면 아래의 코드로 어떻게 탐지를 할수 있는지 애기를 해보겠습니다.

 .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

      include c:\masm32\include\windows.inc
      include c:\masm32\include\user32.inc
      include c:\masm32\include\kernel32.inc

      includelib c:\masm32\lib\user32.lib
      includelib c:\masm32\lib\kernel32.lib

    .data
       KoreaSecurity db "Korea Security",0h
       Protect db "보호하고 싶은가?",0h
       DbgNotFoundTitle db "Debugger status:",0h
       DbgFoundTitle db "Debugger status:",0h
       DbgNotFoundText db "Debugger hardware bpx not found!",0h
       DbgFoundText db "Debugger hardware bpx found!",0h
    .data?
OrgEbp   dd ?
OrgEsp   dd ?
SaveEip  dd ?
    .code
start:
; Setup SEH
MOV EAX,offset @Exit
MOV DWORD PTR[OrgEbp],EAX ;EAX에 @Exit함수주소를 담고 있음
MOV DWORD PTR[SaveEip],EBP; 현재 EBP를 SaveEip라고 담고 있음.
ASSUME FS : NOTHING

PUSH offset @DetectHardwareBPX ;Exception handler
PUSH FS:[0]
MOV DWORD PTR[OrgEsp],ESP ;현재 스택을 OrgEsp에 담고 있음.
MOV  FS:[0], ESP  ; 위에서 설명한 과정

; Fire SEH
XOR EAX,EAX ;EAX값을 0 초기화
XCHG DWORD PTR DS:[EAX],EAX ;0이라는 주소값 참조 exception발생
CALL @Protected;Hardware Break Point 로부터 보호 하고 싶은 영역
@Exit:
POP FS:[0]
ADD ESP,4
PUSH 0
CALL ExitProcess
@Protected:
  PUSH 30h
  PUSH offset KoreaSecurity
  PUSH offset Protect
  PUSH 0
  CALL MessageBox
@DetectHardwareBPX:
PUSH EBP;핸들러가 발생할 때 의 처리
MOV EBP,ESP
MOV EAX,DWORD PTR SS:[EBP+10h] ;Context Record값 세번째인자
; Restore ESP, EBP, EIP

MOV EBX,DWORD PTR[OrgEbp] ;@exit 함수 주소
MOV DWORD PTR DS:[EAX+0B8h],EBX;Context Record의 Ebp값
MOV EBX,DWORD PTR[OrgEsp];fs[0]과 esp가 바뀌기전의 esp값
MOV DWORD PTR DS:[EAX+0C4h],EBX;Context Record의 esp값
MOV EBX,DWORD PTR[SaveEip];
MOV DWORD PTR DS:[EAX+0B4h],EBX;context Record의 eip값

; Check DRx registers

CMP DWORD PTR DS:[EAX+4h],0
JNE @hardware_bpx_found
CMP DWORD PTR DS:[EAX+8h],0
JNE @hardware_bpx_found
CMP DWORD PTR DS:[EAX+0Ch],0
JNE @hardware_bpx_found
CMP DWORD PTR DS:[EAX+10h],0
JNE @hardware_bpx_found
PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox
  @hbpx_exit:
MOV EAX,0 ;위에서 설명한 ExceptionContinuExecution(0) 값
LEAVE
RET
  @hardware_bpx_found:
PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox
JMP @hbpx_exit

end start


다소 이때까지 본 코드보다는 길어 보이지만 복잡하지는 않다.앞전에 설명한대로 SHE를 설치하고 SEH처리에서는 디버거 레지스터리를 검사하며 Context Record값중 ebp,esp,eip를 변경하여 다시금 변경된 레지스티리 값을 복구하는 과정이다.
이를 올리로 본 화면은 이렇다. 아래 그림에서 보다시피 보호 하고 싶은 영역에 하드웨어 브레이크 포인트를 설정하였다. 확인하는 방법은 올리에서 Debug  Hardware Breakpoints를 선택하면 된다.


5.4 Hardware Break Point 우회
앞선 설명을 다 이해 했다면 우회하는 방법은 간단합니다.디버깅 체크루틴에서 강제적으로 디버거 레지스터리 값을 0으로 만들어 주거나 혹은 Exception을 유발 시키는 코드에 대해서 NOP을 처리해 해당 루틴이 실행하지 않도록 합니다.혹은 올리 플러그인중 PhantOm 이라는 것을 이용해 Drx 체크옵션을 주면 우회(PhantOm 플러그인을 이용한 우회)를 합니다.
 


다음은 Protect 영역에 Hardware break Point 부분입니다.



F9를 눌러 run을 하여 보자. Hardware break Point 발견을 한 부분 입니다.
 



6.1 Garbage Code & JunkCode
지금부터 설명한 안티 리버싱 방법은 앞선 기술과 달리 리버서들을 심리적으로 짜증나고 지치게 만드는 기법입니다. 단지 보호 될 코드를 분석할 때 시간을 끄는 용도로 사용됩니다. 두 개념은 다소 다른 의미를 지니고 있습니다. 먼저 Garbage코드는 코드중간에 의미없는 코드들을 집어 넣어 리버서들의 집중력(?) 과 혼란을 가중 시켜 버리며 Junkcode는 가령 디버거는 코드를 디어셈 할 때 정의된 OPCODE에 의해 디어셈을 화면에 DISPLAY합니다. 그
것을 착안하여 실행하지 않도록 하는 OPCODE를 중간에 넣으므로써 전혀 엉뚱한 디어셈 코드를 출력함으로써 리버서를 방해 합니다. 앞서 작성한 SHE를 유발 시키는 코드를 보호될 코드 영역으로 정하고 Garbage 코드를 삽입 해 올리로 살펴보겠습니다.

 보호되어야 할 코드
XOR EAX,EAX
XCHG DWORD PTR DS:[EAX],EAX

위와 같은 코드가 보호 되어야 할 것이라면 중간에 Garbage코드(프로그램에 상관없이 의미 없는 코드)를 삽입 합니다.
 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

간단하게 적용한 모습입니다. 물론 저정도 코드는 중간에 삽입된게 의미가 없다는걸 한눈에 알아 볼수 있지만 이러한 코드들이 무더기로 중간중간에 삽입이 되어 있다면 골치가 아플 것입니다.

다음은 ollydbg에서 Garbage코드가 삽입된 화면을 보여주고 있습니다.

 
다음은 Junk Code를 삽입 하여 보겠습니다. 여기에 해당하는 내용은 제스리버님의 홈페이지의 테스트를 참고로 하였습니다. 코드는 아래와 같습니다.

 #include <windows.h>

int main(int argc,char *argv[])
{
 __asm
 {
  jmp here+1;
  here:
   __emit 0xe9//__emit은 뒤에 바이트를 코드에 포함
  mov eax,1
 }
 return 0;
}



코드를 보면 중간에 박아서 디버거가 제대로된 코드를 뿌려주지 못하도록 하고 있습니다.

다음은 ollydbg로 junk code 삽입된것을 확인한 화면입니다.

 

MOV EAX,1 디어셈 코드는 온데 간데 보이질 않고 덩그러니 JMP코드가 자리 잡고 있습니다. 그런데 JMP하는 주소값이 이상하네요.머 저정도야 금방 눈치채겠지만 그래도 모르는 상태에서 본다면 다소 의아해 할 것 입니다. 해당 옵코드 0xe9 를 0x90(NOP)으로 처리한다면 원래 OP CODE인 mov eax,1을 복원 할 수 있습니다. 다음은 해당 코드를 복원 한 모습입니다. NOP으로 바꾼뒤 올리에서 Ctrl+a 혹은 마우스 우클릭시 Analysis  Alnalse code 을 클릭하면 OPCODE를 재분석 하게 됩니다.

다음은 Junk Code 패치한 화면입니다. 




여기까지 "Anti-Reversing Code" 에 대한 설명을 마치겠습니다.^^)


참고사이트 및 문서
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 [4/4]|작성자 카이


반응형
반응형
문서 작성일 : 2008년 12월 19일
최종 수정일 : 2008년 12월 19일

Hong10님께서 작성하신 "Anti Reversing Code" 큰 주제를 바탕으로 4개로 나눠 작성하였습니다.



Anti-Reverse
개인적으로 많은 리버서들을 짜증나게 하는 것 이 이 안티리버싱 기법이라고 생각합니다. 언팩을 하든 프로그램을 분석하든 이 안티리버싱 코드가 있으면 어떻게든 리버서들을 방해하고 혼란을 가중 시키기 위하여 여러가지 방법을 동원하여 괴롭힙니다. 물론 방어자 입장에서는 자신의 코드가 쉽게 분석 당하지 않기 위하여 더없이 고마운 기법이긴 하지만 말입니다.

1.1 IsDebugerPresent()
대부분의 기초적인 디버거 탐지 기법에는 PEB의 BeingDebugged 값을 체크를 하는 기법을 가지고 있습니다. PEB(Process Environment Block)의 위치는 TEB(Thread Environment Block)에서 0x30 만큼 떨어진 곳에 존재 합니다.
IsDebugerPresent는 유저모드의 디버거가 프로세서를 디버깅하고 있는지 flag 값을 체크 하여 확인합니다. 여기서 간단히 PEB과 TEB에 대해서 알아 보겠습니다. PEB은 유저레벨 프로세서의 대한 추가적인 정보를 가지고 있는 구조체이고 TEB은 쓰레드에 대한 정보를 가지고 있다고 말할 수 있습니다

이 구조체를 어떻게 구동되며 핸들링 하는지는 애기가 길어 지므로 생략합니다. 여기서는 PEB의 구조체 값중 BeingDebugged값을 체크하여 디버깅 유무를 판별 한다고 생각 하시면 될꺼 같습니다. 그 외 에도 PEB 구조체 값을 이용하여 판별 할수 있습니다. 그건 추후에 나오는 기법에 대하여 언급 하겠습니다. 아래의 코드를 바탕으로 기본적이 MASM 문법과 IsDebugerPresent 의 기능을 살펴 보겠습니다.

IsDebugerPresent 소스를 EDIT-PLUS stx에 적용한 코드입니다.

 .386
.model flat, stdcall
option casemap :none   ; case sensitive

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
DbgNotFoundTitle db "Debugger status:",0h
DbgFoundTitle db "Debugger status:",0h
DbgNotFoundText db "Debugger not found!",0h
DbgFoundText db "Debugger found!",0h
.code
start:

CALL IsDebuggerPresent

CMP EAX,1
JE @DebuggerDetected

PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox

JMP @exit
@DebuggerDetected:

PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox

@exit:

PUSH 0
CALL ExitProcess

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


Disclaimer

[출처] Anti-Reversing Code [2/4]|작성자 카이


반응형
반응형
1. EDIT PLUS 를 이용한 MASM32 컴파일 환경 구축
MASM32 어셈블러를 이용한 Anit-Reversing Code 구현에 앞서 간단히 MASM32 이란 무엇이며 어떻게 환경을 구축하여 테스트를 하는지 에 대한 설명을 할 것이다. MASM32이란 마이크로소프트사의 어셈블러 툴이다 그외 여러가지 어셈블러들이 존재하지만 일반적으로 API를 쓰기 위한 어셈블러로 MASM을 이용하게 된다.


MASM은 어디서 구할수 있으며 환경설정은 어떻게 할것인가? 다음 사이트에서 MASM을 구할수 있다.
http://www.masm32.com/masmdl.htm

현재 최신 버전은 9버전으로 파일명은 m32v9r.zip 으로 존재한다. 참고로 필자의 Visual Studio 의 버전은 6을 쓰며 또한 필요한 툴은 EDIT PLUS 가 있어야 한다 각자의 툴은 알아서 구하길 바란다. 먼저 MSAM을 설치해 본다.

 



간단히 Enter 키를 누름으로 써 설치를 할 수 있다.
다음은 EDIT-PLUS에서 설정이다. 도구->기본설정->사용자도구 에서 그룹 과 도구 항목을 설정 할 수 있다.
 



아래는 이와 같은 설정을 나열 해 보여주고 있다.

Edit-Plus Assembly 설정 (MASM32)

 

1. Assemble
명령 : C:\masm32\bin\ml.exe /c /coff /Zi,인수 : $(FileName),디렉토리 : $(FileDir),출력 내용 캡쳐 : 체크

2. Link (Console)
명령 : C:\masm32\bin\link.exe /SUBSYSTEM:CONSOLE /DEBUG,인수 : $(FileNameNoExt).obj,디렉토리 : $(FileDir)
출력 내용 캡쳐 : 체크

3. Link (Windows)
명령 : C:\masm32\bin\link.exe /SUBSYSTEM:WINDOWS /DEBUG,인수 : $(FileNameNoExt).obj,디렉토리 :$(FileDir)
출력 내용 캡쳐 : 체크 />

4. Debug
명령: C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin\MSDEV.EXE,인수
: $(FileNameNoExt).exe
디렉토리
: $(FileDir)
5. Execute
명령 : $(FileNameNoExt).exe,디렉토리 : $(FileDir)


또한 EDIT-PLUS 에서는 MASM 문법에 대한 구문강조 파일이 존재한다. 해당 파일은 아래 사이트에서 받을수 있다.

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]|작성자 카이


반응형
반응형

문서 작성일 : 2008년 12월 19일
최종 수정일 : 2008년 12월 19일

Hong10님께서 작성하신 "Anti Reversing Code" 큰 주제를 바탕으로 4개로 나눠 작성하였습니다.


2.1 PEB.NtGlobalFlag
PEB은 BeingDebugged 플래그 외에도, PEB는 NtGlobalFlag라는 필드를 갖고 있는데 NtGlobalFlag는 PEB으로부터 0x68 위치에 존재합니다. LiveKD를 이용하여 PEB의 구조체 값을 확인 해보았습니다.
 


이 플러그인의 값은 디버깅 중이 아니라면 0x0 값이 담기지만 디버깅 중이라면 0x70 값이 담겨 집니다. 그와 같은 것을 이용하여 디버깅 탐지를 합니다.

다음은 NtGlobalFlag Code 부분입니다.

 .386
.model flat, stdcall
option casemap :none   ; case sensitive

include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib

.data
DbgNotFoundTitle db "Debugger status:",0h
DbgFoundTitle db "Debugger status:",0h
DbgNotFoundText db "Debugger not found!",0h
DbgFoundText db "Debugger found!",0h
.code
start:

ASSUME FS:NOTHING
MOV EAX,DWORD PTR FS:[30h]
ADD EAX,68h
MOV EAX,DWORD PTR DS:[EAX]
CMP EAX,70h
JE @DebuggerDetected

PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox

JMP @exit
@DebuggerDetected:

PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox

@exit:

PUSH 0
CALL ExitProcess

end start




여기서 ASSUME FS:NOTHING 이란 구문은 세그먼트 레지스트의 주소값을 재할당 하는 것이 아니라 이 디렉티브를 맞나면 실행 시에 어셈블러가 주소를 계산하는 방법을 변경한다. 즉 FS 세그먼트에 NOTHING이라는 속성을 붙이는 의미이다.
해당 내용을 더 깊에 알아 보았는데 다음과 같은 사이트에서 친철히 설명 해주고 있었다.
http://www.winasm.net/forum/index.php?showtopic=2082

간단히 축약해서 애기하자면(맞는 애기인지도 모르겠다,필자의 영어실력은 가히 밑바닥 수준이라서…) FS:[0] 은 Exception handler를 Default를 가지고 있다. MASM 컴파일러는 기본적으로 이 레지스터를 사용할 때 ERORR를 뱉기 때문에 위와 같이 ASSUM FS:NOTHING 을 써주면 그와 같은 ERORR Check를 Remove 해준다고 설명이 되어 있다.

다음으로FS:[30] 는 PEB의 위치에서 ADD EAX,68h 에는 NtGlobalFlag 가 존재한다. 해당 코드를 올리로 열어 보았다.
 

Olly 로 확인한 NtGlobalFlag 루틴

2.2 NtGlobalFlag 우회
해당 탐지를 우회는 간단하다. 직접 코드 패치를 하여 바꾸 거나 혹은 플래그 값을 수정하거나 이다. 하지만 이것도 역시 올리에서 플러그인 형태로 지원을 하고 있다. 다음 사이트에서 다운로드 하여 쓰시면 됩니다.
http://www.openrce.org/downloads/details/241/Olly_Advanced

해당 플러그 인을 실행하고 NtGlobalFlag 를 체크 뒤 확인 해주면 자동적으로 리턴시 플래그 값을 수정하여 디버거 탐지를 우회 하게 됩니다. 그 외 코드 패치 등 다양한 방법 이 존재 합니다. 툴로써 좀 더 편하게 하는게 좋겠죠 ??;
 
다음은 Olly Advanced NtGlobalFlag 입니다.



3.1 Heap.HeapFlag , Heap.ForceFlags
ProcessHeap 은 PEB 에서 0x18 만큼 떨어져 있고 이 플래그는 프로세서가 디버깅 될 때 heap 이 만들어 지고 이때 설정 되는 플래그들은 HeapFlag와 ForceFlas 입니다. 처음 프로그램이 힙 영역을 만들면 ForceFlags 에는 0x0 값이 HeapFlag 에는 0x2 값이 설정 됩니다. 하지만 디버깅 중이라면 NtGlobalFlag 에 따라 두개의 플래그 값이 변경 됩니다.
만약 디버깅이 탐지 되어 변경 되었다면 FoceFlags에는 0x40000060값이 할당 되며 HeapFlag 에는 0x50000062 값이 할당 됩니다.이때 변경된 값을 체크 하므로써 디버깅의 유무를 판별합니다.

다음은 PEB 의 ProcessHeap 구조체 offset 입니다.


여기서 ProcessHeap의 구조체 를 살펴보면 언급한 두개의 값들이 존재합니다.



다음은 Process_Heap Code 부분입니다.

 .386
.model flat, stdcall
option casemap :none   ; case sensitive

include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc

includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib

.data
DbgNotFoundTitle db "Debugger status:",0h
DbgFoundTitle db "Debugger status:",0h
DbgNotFoundText db "Debugger not found!",0h
DbgFoundText db "Debugger found!",0h
.code

start:

ASSUME FS:NOTHING
MOV EAX,DWORD PTR FS:[18h] ;TEB
MOV EAX,DWORD PTR [EAX+30h] ;PEB
MOV EAX,DWORD PTR[EAX+18h] ;Process_Heap
CMP DWORD PTR DS:[EAX+10h],0 ;Force_Flags
JNE @DebuggerDetected

MOV EAX,DWORD PTR FS:[18h] ;TEB
MOV EAX,DWORD PTR [EAX+30h] ;PEB
MOV EAX,DWORD PTR[EAX+18h] ;Process_Heap
CMP DWORD PTR DS:[EAX+0ch],2 ;Force_Flags
JNE @DebuggerDetected

PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox

JMP @exit
@DebuggerDetected:

PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox

@exit:

PUSH 0
CALL ExitProcess

end start


3.2 Heap.HeapFlag , Heap.ForceFlags 우회
역시 앞선 방법과 동일 하게 코드 패치 및 플래그 변조로 우회를 할수 있다. 또는 올리 플러그인 OllyAdvanced 를 이용하면 쉽게 플래그 값을 자동으로 수정 해준다.
 
다음은 Olly Advanced HeapFlag,ForceFlags 우회 부분입니다.




4.1 NtQueryInformationProcess
CheckRemoteDebuggerPresent API는 디버거가 프로세서를 attach 하는 것을 감지합니다. 이 API 는 내부적으로 NtQueryInformationProcess를 호출합니다.이 함수는 또한 내부적으로 커널 구조체인 EPROCESS의 DebugPort 플래그를 검사합니다. 가령 유저 모드 디버거가 프로세서를 attach 한 상태이면 DebugPort 플래그 값은 0이 아닌값으로 설정이 됩니다.
앞서 저희는 PEB에 대해 간략하게 알아 봤습니다. PEB은 유저모드 레벨 프로세스에 대한 추가 정보 이며 여기서 설명하는 EPROCESS는 커널에서 프로세스를 관리하기 위해 사용하는 구조체 정도가 되겠습니다. 또한 NtQueryInformationProcess 을 내부적으로 불러 낼때에는 인자 값중 ProcessInformation 값은 7이 됩니다.
또한 DebugPort의 오프셋은 0x120 위치에 존재하며 유저모드 에서 디버깅 중이라면 NtQueryInformationProcess 함수의 인자 값중 hProcess는 0xfffffff 값이 되며 그렇지 않을 경우에는 0이라는 값이 담기게 됩니다

다음은 NtQueryInformationProcess Code 부분입니다.

 .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

      include c:\masm32\include\windows.inc
      include c:\masm32\include\user32.inc
      include c:\masm32\include\kernel32.inc

      includelib c:\masm32\lib\user32.lib
      includelib c:\masm32\lib\kernel32.lib

    .data
       DbgNotFoundTitle db "Debugger status:",0h
       DbgFoundTitle db "Debugger status:",0h
       DbgNotFoundText db "Debugger not found!",0h
       DbgFoundText db "Debugger found!",0h
       ntdll db "ntdll.dll",0h
       zwqip db "NtQueryInformationProcess",0h
    .data?
       NtAddr dd ?
       MinusOne dd ?
    .code

start:

MOV [MinusOne],0FFFFFFFFh

PUSH offset ntdll ;ntdll.dll
CALL LoadLibrary

PUSH offset zwqip ;NtQueryInformationProcess
PUSH EAX
CALL GetProcAddress

MOV [NtAddr],EAX

MOV EAX,offset MinusOne
PUSH EAX
MOV EBX,ESP

PUSH 0
PUSH 4
PUSH EBX
PUSH 7
PUSH DWORD PTR[EAX]
CALL [NtAddr]

POP EAX

TEST EAX,EAX
JNE @DebuggerDetected

PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox

JMP @exit
  @DebuggerDetected:

PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox

  @exit:

PUSH 0
CALL ExitProcess

end start



위의 코드에서 ? 는 아마 변수를 선언할 때 값을 할당하지 않은 상태일꺼라 생각이 듭니다. 어째든 위의 코드에서 커널 API를 수행하기 위해서 LoadLibrary와 GetProcAddress를 이용하여 해당 함수 주소를 얻은뒤 앞서 설명한 인자값을 토대로 호출 하고 디버깅 중이라면 리턴되는 값을 체크하여 디버거 존재 유무를 판별 하고 있습니다.

다음 그림은 위의 코드를 Olly에서 확인한 장면(NtQueryInformationProcess in Olly)입니다.
 


그림에서 알수 있듯이 인자 값이 저런식으로 들어가 호출하게 되어야지 디버깅 체크를 할수 있습니다.


4.2 NtQueryInformationProces 우회
이 방법을 우회하는 방법 역시 여러가지가 존재 할수 있습니다. 다만 올리 플러그 인중 Olly Advanced 는 NtQueryInformationProcess 의 인자 값중 하나인 hProcess를 0으로 만듬으로써 해당 루틴을 우회하게 될꺼라고 생각했으나 잘 안되는군요. 일단은 그냥 주 루틴이 나오면 리턴되는 부분에 EAX값을 0을 설정하면 우회가 됩니다.
 



5.1 Debugger Interrupts

이 방법은 디버거가 하나의 소프트 브레이크 포인트를 수행하려고 할 때 INT3(0xcc)를 삽입 함으로 써 수행이된다. 인터럽터 가 수행이 된다는 말은 즉 예외처리가 일어 난다는 의미이다. 하지만 디버거가 디버깅 수행중 일 때 같은 OP 코드인 INT3을 만난 다면 예외 처리없이 수행을 하게된다.
이 Debugger Interrupts는 바로 이러한 것을 착안하여 안티 디버깅을 수행한다. 즉 Interrupt 수행중에 같은 인터럽터 코드를 만났을 때 exception을 하지 않는 경우를 디버깅 중이라고 판별하게 된다. 좀더 유연한 이해를 위해 아래 코드를 참고 한다.

다음은 Debugger Interrupts 부분입니다.

 .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

      include c:\masm32\include\windows.inc
      include c:\masm32\include\user32.inc
      include c:\masm32\include\kernel32.inc

      includelib c:\masm32\lib\user32.lib
      includelib c:\masm32\lib\kernel32.lib

    .data
msgTitle db "Execution status:",0h
msgText1 db "No debugger detected!",0h
msgText2 db "Debugger detected!",0h
    .code

start:

ASSUME FS:NOTHING
PUSH offset @Check
PUSH FS:[0]
MOV FS:[0],ESP

; Exception
INT 3h

PUSH 30h
PUSH offset msgTitle
PUSH offset msgText2
PUSH 0
CALL MessageBox

PUSH 0
CALL ExitProcess

; SEH handleing
@Check:
POP FS:[0]
ADD ESP,4

PUSH 40h
PUSH offset msgTitle
PUSH offset msgText1
PUSH 0
CALL MessageBox

PUSH 0
CALL ExitProcess

end start


위에 코드에서 @Check는 exception이 일어 날 때 등록되는 SEH 이다. INT 3이 수행될 때 익셉션이 일어 나지 않는다면 Debuger dectect 메시지가 출력 될것이다.다음은 올리(Debugger Interrupt in Olly)에서 살펴본 모습이다.

 


저기에서 INT3을 유심히 살펴 보면 저기가 분기문이라고 이해하시는 편이 더 수월 할수도 있습니다 INT3이 exception을 일으킨다면 0x0040103e로 향하게 될것이며 그렇지 않을경우 0x00401024코드로 향하게 될것입니다.


5.2 Debug Interrupts 우회
우회 방법은 간단합니다. 올리에서 디버깅 옵션에 보면 INT 3 을 만났을 때 무시하지 않고 SEH를 following 하라는 옵션을 선택하여 줍니다.




다음과 같이 우회를 하게 된다면 Exception Handle로 빠지는 루틴(Exception Handle 을 호출하는 루틴)을 보실수 있습니다.



[계속...]

참고사이트 및 문서
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
※ 현재 ㈜에이쓰리시큐리티에서 테스트 및 분석 중에 있으며, 이 문서는 계속 업데이트될 것입니다. 본 문서는 보안취약점으로 인한 피해를 최소화하는 데 도움이 되고자 작성되었으나, 본 문서에 포함된 대응방안의 유효성이나 기타 예상치 못한 시스템의 오작동 발생에 대하여서는 ㈜에이쓰리시큐리티에서는 일체의 책임을 지지 아니합니다.

 

반응형
반응형
[ASM] intel 8086 어셈블리 명령어 & 지시어 | RE 2007/03/22 21:02   
난동도리(biggy2000) http://memolog.blog.naver.com/biggy2000/588

8086 명령어


 

명  령  어

설                                               명

Data Transfer

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

스택으로부터 플래그 레지스터로 뽑음

Arithmetic

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의 더블 워드로 변환

Logic

NOT

Invert

오퍼랜드의 1의 보수, 즉 비트 반전

SHL/SAL

Shift logical / arithmetic Left

왼쪽으로 오퍼랜드만큼 자리 이동 (최하위 비트는 0)

SHR

Shift logical Right

오른쪽으로 오퍼랜드만큼 자리 이동 (최상위 비트 0)

SAR

Shift arithmetic Right

오른쪽 자리이동, 최상위 비트는 유지

ROL

Rotate Left

왼쪽으로 오퍼랜드만큼 회전 이동

ROR

Rotate Right

오른쪽으로 오퍼랜드만큼 회전 이동

RCL

Rotate through Carry Left

캐리를 포함하여 왼쪽으로 오퍼랜드만큼 회전 이동

RCR

Rotate through Carry Right

캐리를 포함하여 오른쪽으로 오퍼랜드만큼 회전 이동

AND

And

논리 AND

TEST

And function to Flags, no result

첫 번째 오퍼랜드와 두 번째 오퍼랜드를 AND하여 그 결과로 플래그 세트

OR

Or

논리 OR

XOR

Exclusive Or

배타 논리 합 (OR)

String Manipulation

REP

Repeat

REP 뒤에 오는 스트링 명령을 CX가 0이 될 때까지 반복

MOVS

Move String

DS:SI가 지시한 메모리 데이터를 ES:DI가지시한 메모리로 전송

CMPS

Compare String

DS:SI와 ES:DI의 내용을 비교하고 결과에 따라 플래그 설정

SCAS

Scan String

AL 또는 AX와 ES:DI가 지시한 메모리 내용 비교하고 결과에 따라 플래그 설정

LODS

Load String

SI 내용을 AL 또는 AX로 로드

STOS

Store String

AL 또는 AX를 ES:DI가 지시하는 메모리에 저장

Control Transfer

CALL

Call

프로시저 호출

JMP

Unconditional Jump

무조건 분기

RET

Return from CALL

CALL로 스택에 PUSH된 주소로 복귀

JE/JZ

Jump on Equal / Zero

결과가 0이면 분기

JL/JNGE

Jump on Less / not Greater or Equal

결과가 작으면 분기 (부호화된 수)

JB/JNAE

Jump on Below / not Above or Equal

결과가 작으면 분기 (부호화 안 된 수)

JBE/JNA

Jump on Below or Equal / not Above

결과가 작거나 같으면 분기 (부호화 안 된 수)

JP/JPE

Jump on Parity / Parity Even

패리티 플레그가 1이면 분기

JO

Jump on Overflow

오버플로가 발생하면 분기

JS

Jump on Sign

부호 플레그가 1이면 분기

JNE/JNZ

Jump on not Equal / not Zero

결과가 0이 아니면 분기

JNL/JGE

Jump on not Less / Greater or Equal

결과가 크거나 같으면 분기 (부호화된 수)

JNLE/JG

Jump on not Less or Equal / Greater

결과가 크면 분기 (부호화된 수)

JNB/JAE

Jump on not Below / Above or Equal

결과가 크거나 같으면 분기 (부호화 안 된 수)

JNBE/JA

Jump on not Below or Equal / Above

결과가 크면 분기 (부호화 안 된 수)

JNP/JPO

Jump on not Parity / Parity odd

패리티 플레그가 0이면 분기

JNO

Jump on not Overflow

오버플로우가 아닌 경우 분기

JNS

Jump on not Sign

부호 플레그가 0이면 분기

LOOP

Loop CX times

CX를 1감소하면서 0이 될 때까지 지정된 라벨로 분기

LOOPZ/LOOPE

Loop while Zero / Equal

제로 플레그가 1이고 CX&#8800;0이면 지정된 라벨로 분기

LOOPNZ/LOOPNE

Loop while not Zero / not Equal

제로 플레그가 0이고 CX&#8800;0이면 지정된 라벨로 분기

JCXZ

Jump on CX Zero

CX가 0이면 분기

INT

Interrupt

인터럽트 실행

INTO

Interrupt on Overflow

오버플로우가 발생하면 인터럽트 실행

IRET

Interrupt Return

인터럽트 복귀 (리턴)

Processor Control

CLC

Clear Carry

캐리 플레그 클리어

CMC

Complement Carry

캐리 플레그를 반전

CLD

Clear Direction

디렉션 플레그를 클리어

CLI

Clear Interrupt

인터럽트 플레그를 클리어

HLT

Halt

정지

LOCK

Bus Lock prefix


STC

Set Carry

캐리 플레그 셋

NOP

No operation


STD

Set Direction

디렉션 플레그 셋

STI

Set Interrupt

인터럽트 인에이블 플레그 셋

WAIT

Wait

프로세서를 일지 정지 상태로 한다

ESC

Escape to External device

이스케이프 명령

 

8086 지시어

 

지시어

내                                  용

형                                  식

SEGMENT
-
END

어셈블리 프로그램은 한 개 이상의 세그먼트들로 구성된다. SEGMENT 지시어는 하나의 세그먼트를 정의한다.

segname SEGMENT ; 세그먼트 시작
&#8942; ; 세그먼트 내용
segname ENDS ; 세그먼트 끝

PROC
-
ENDP

매크로 어셈블리에서는 프로그램의 실행 부분을 모듈로 작성할 수 있다. 이 모듈을 프로시저(Procedure)라 부르며, PROC 지시어가 이를 정의한다.

procname PROC ; 프로시저의 시작
&#8942; ; 프로시저의 내용
procname ENDP ; 프로시저의 끝

ASSUME

어셈블러에게 세그먼트 레지스터와 사용자가 작성한 세그먼트의 이름을 연결시킨다.

ASSUME SS:stack_segname,
DS:data_segname,
CS:code_segname,
ES:extra_segname

END

전제 프로그램의 끝을 나타냄

END

데이터 정의 지시어 : 프로그램에서 데이터를 저장할 기억 장소를 정의, 초기값 부여

DB

Define Byte

name DB 초기값

DW

Define Word

name DW 초기값

DD

Define Double Word

name DD 초기값

DQ

Define Quad Word

name DQ 초기값

DT

Define Ten Bytes

name DT 초기값

EQU

변수 이름에 데이터값이나 문자열 정의

name EQU 데이터값/문자열

=

EQU와 달리 정의된 값을 변경 가능


EVEN

어셈블리시 이 지시어가 사용되는 곳의 주소가 짝수로 되도록 함


PAGE

어셈블리 리스트의 형식을 결정

PAGE [length][,width]

TITLE

어셈블리 리스트의 각 페이지에 제목 출력

TITLE text


반응형
반응형

출처 - 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 저

글 옮긴이의 하고 싶은말..

조금 읽으면서..   -
참고 사이트가 듀얼님.. ^^ 너무 유명해 ..ㅋ

출처 : Tong - 통토로동통똥똥님의 reverse tech통

반응형
반응형

1.어셈블리어
Push  : sp
레지스터를 조작하는 명령어중의 하나이다.
      
스택에 데이터를 저장하는데 쓰인다
.
ex:) Push eax :
스택에 Eax의 값을 스택에 저장한다
.
ex:) Push 20 :
즉석값인 20을 스택에 저장한다
.
ex:) Push 401F47 :
메모리 오프셋 401F47의 값을 스택에 저장한다
.

Pop  :
이또한 sp 레지스터를 조작하는 명령어중 하나이다.

스택에서 데이터를 꺼내는데 쓰인다.
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)를 동작할수 있도록 해준다.

출처 : Tong - 통토로동통똥똥님의 reverse tech통

반응형
반응형