Programming/Assembly2012. 1. 26. 18:58


"인텔 기반 컴퓨터를 위한 어셈블리 언어 5판" 은 어제부터 시작한 어셈블리 교재입니다.

공부하면서 풀은 연습문제입니다.


1번 - 텍스트 색상 그리기

TITLE 5.8.1 example

; 5.8.1
; 텍스트 색상 그리기

 
INCLUDE Irvine32.inc
.data
printMsg BYTE "Assembly language - NullBr4in - ajlab.tistory.com", 0

.code
main PROC
mov eax, yellow
call SetTextColor
mov edx, OFFSET printMsg
call WriteString
call Crlf

mov eax, green
call SetTextColor

call WriteString
call Crlf

mov eax, cyan
call SetTextColor

call WriteString
call Crlf

mov eax, lightRed
call SetTextColor

call WriteString
call Crlf

mov eax, lightgray
call SetTextColor

exit
main ENDP
END main



2번 - 피보나치 수의 파일
 
 

 
Posted by NullBr4in
Programming/Assembly2012. 1. 26. 18:03


어셈블리어 명령어 정리는 시간 날때마다 한번씩 내용을 추가하겠습니다 ( 2012-01-26 )


 어셈블리어 명령어에 대해 정리할때 아래와와 같은 표기법을 이용해서 정리하겠습니다. 
 
  [그림 1] 인텔 IA-32 메뉴얼에서 채택한 피연산자에 대한 간단한 표기법



 MOV 
 - MOV 명령어는 소스 피연산자로부터 목적지 피연산자로 데이터를 복사한다.
 - MOV 복사하고자 하는 목적지, 복사할 대상이 있는 곳
 - MOV destination, source
 - MOV 명령어는 dest = source 와 같다 

 - MOV 명령어의 규칙
  + 두 피연사자는 같은 크기이어야 한다.
  + 두 피연산자는 모두 메모리 피연산자일 수는 없다.
  + CS, EIP, IP는 목적지 피연산자일 수 없다.
  + 즉시값은 세그먼트 레지스터로 이동될 수 없다.
 
 - 세그먼트 레지스터를 제외한 일반적으로 사용하는 MOV의 형태들이다.
  + MOV reg, reg
  + MOV mem, reg
  + MOV reg, mem
  + MOV mem, imm
  + MOV reg, imm

 - MOV 명령어를 사용할 때 한 메모리에서 다른 메모리 위치로 이동시키는데 사용하지 못한다.
   그러므로 다음의 방법을 사용한다.
  + var1, var2 는 WORD 형태를 가지고 있습니다.
    mov ax, var1
    mov var2, ax

 
Posted by NullBr4in
Programming/Assembly2012. 1. 23. 17:11


5.4.2 장에서 스택을 이용한 문자열을 역순으로 배치하는 예제 입니다.

고급언어를 만지다가 저급언어를 만지게 되니  저급언어가 방법도 여러가지에 효율적이라고 생각되네요.



 TITLE Reversing a String (RevStr.asm)

INCLUDE Irvine32.inc
.data
aName BYTE "Abraham Lincoln", 0 ;  역순으로 배치할 문자열을 선언합니다
nameSize = ($ - aName) - 1 ; aName에 대해 문자열의 길이를 구하는 부분인데, 여기서 $ 는 
                                                                ; 현재 명령어의 위치 주소 입니다. 현재 주소에서 aName의 주소를
                                                                ; 빼게 되면 문자열의 길이만 남게 되는데, 여기서 널문자를 제외하기 위해 - 1 을
                                                                ; 해줍니다. 

.code
main PROC

; 스택으로 문자를 push 하는 부분 
mov ecx, nameSize                            ; 반복문에서 사용할 count 레지스터인 ecx에 문자열의 길이를 넣어줍니다.
mov esi, 0                                          ; esi에는 0으로 초기화 시켜줍니다. esi는 간접 피연산자를 이용한 간접 주소 지정
                                                                ; 을 통해 문자열을 하나하나 가리키게 됩니다. 

L1:                                                            ; L1 루프의 시작
movzx eax, aName[esi]                     ; aName+esi 위치의 문자열을 eax로 이동시켜줍니다. aName+0, aName+1 이런식
push eax                                           ; eax를 스택에 추가하고 - L,i,n,c,o,l,n,,, 이런식으로 스택에 쌓이게 됩니다.
inc esi                                               ; esi를 1증가시킵니다 - 다음 반복에서 다음 문자를 가리키기 위함입니다.
loop L1                                              ; L1의 처음부분으로 가서 반복합니다.

; 아래는 스택에서 문자를 pop 하는 부분.

mov ecx, nameSize                             ; 반복이 끝나면 0이 된 ecx에 다시 문자열의 길이를 넣어줍니다.
mov esi, 0                                           ; esi 또한 0으로 초기화 합니다

L2:
        pop eax                                              ; pop을 하여 스택의 맨 나중에 들어간 'n'을 꺼내옵니다.
mov aName[esi], al                            ; 꺼내온 값을 [aName+esi] 에 넣습니다.
inc esi                                               ; esi를 1 증가시킵니다.
loop L2                                              ; 루프의 처음으로 돌아갑니다

; 역 배치가 끝나고 출력 하는 부분.

mov edx, OFFSET aName                     ; aName의 offset을 edx에 저장합니다.                  
call WriteString                                      ; WriteString 함수는 edx에 저장된 주소의 위치에 있는 문자열을 출력합니다.
call Crlf                                               ; 개행
exit                                                    ; 종료
main ENDP
END main


Posted by NullBr4in
Programming/Assembly2012. 1. 23. 16:30


약 170억번 반복되는 반복문을 통하여 컴퓨터 성능을 시험해보는 예제 입니다.

반복문 시작 전 후로 현재 시간을 구하여 빼는 원리입니다.



TITLE Link Library Test #3 (TestLib3.asm)

; Calculate the elapsed time of executing a nested loop

INCLUDE Irvine32.inc

OUTER_LOOP_COUNT = 3 ; adjust for processor speed
.data
startTime DWORD ?
msg1 BYTE "Please wait...", 0dh, 0ah, 0 ; 0dh,0ah 는 개행문자입니다.
msg2 BYTE "Elapsed milliseconds: ",0
.code
main PROC
mov edx, OFFSET msg1 ; "Please wait..."이라는 문자열을 edx 에 넣습니다.
call WriteString ; 문자열 출력 - edx에 있는 주소의 문자열을 출력합니다.
; Save the starting time.
call GetMSeconds ; GetMSeconds - 자정 이후를 msec 단위로 계산하여 eax로 리턴
mov StartTime, eax ; eax 에 리턴된 값을 StartTime에 넣는다.
mov ecx, OUTER_LOOP_COUNT ; ecx - loop를 반복할 횟수를 넣어줌.

; Perform a busy loop.
L1:
call innerLoop ; innerLoop PROC 을 세번 호출한다.
loop L1

; Display the elapsed time.
call GetMSeconds ; 위의 루프가 끝나면 다시 한번 현재 시간을 구합니다
sub eax, startTIme ; 현재 시간에서 루프 시작하기 전 시간을 빼줍니다
mov edx, OFFSET msg2 ; "Elapsed milliseconds: "의 오프셋을 edx에 저장
call WriteString ; edx에 저장된 문자열 출력
call WriteDec ; eax에 있는 루프 도는데 걸린 시간 값을 출력
call Crlf ; 개행문자
exit ; main PROC 종료
main ENDP

innerLoop PROC ; innerLoop Proc 정의
push ecx ; 이전 루프에서 ecx가 사용되고 이번에도 사용되므로 스택에 저장함
mov ecx, 0FFFFFFFFh ; 반복 횟수를 FFFFFFFFh 로 지정
L1: ; L1 루프
mov eax, eax ; eax를 eax로 이동시키는 아무 의미 없는 코드 삽입
loop L1 ; L1 처음으로 . 
pop ecx ; 루프가 끝나면 이전 ecx를 스택에서 꺼내서 복원시킵니다
ret ; innerLoop Proc 을 리턴합니다.
innerLoop ENDP

END main

Posted by NullBr4in
Programming/Assembly2012. 1. 23. 16:13


이 예제도 5.3.3 에 있는 두번째 테스트 프로그램입니다.

처음에는 10개의 부호 없는 정수를 무작위로 생성하고 그 다음에는 -50부터 +49 범위에 있는 10개의 부호 있는 정수를 생성합니다.

어셈블리어 - 난수 생성하기

 



  TITLE Link Library Test #2 (TestLib2.asm)

; Testing the Irvine32 Library procedures.

INCLUDE Irvine32.inc

TAB = 9 ; 탭을 출력하기 위한 Tab의 ASCII 값

.code
main PROC
call Randomize ; 뒤에서 사용할 Random 함수를 위해 Seed 설정. ( 이 함수를 호출하지 않으면 같은 값이 계속 나옴)
call Rand1                 ; Rand1 프로시저 호출
call Rand2                 ; Rand2 프로시저 호출
exit                          ; 종료
main ENDP

Rand1 PROC                     ; Rand1 프로시저 정의
; Generate ten pseudo-random integers. 10개의 부호 없는 정수 생성
mov ecx, 10 ; loop 10 times // ecx에 10을 넣어서 10번 반복되게 한다.

L1:                                   ; L1 루프 시작 부분 
call Random32 ;  Random32 함수 호출  - 무작위 int 생성 리턴값은 eax로 들어갑니다.
call WriteDec ;  WriteDec - eax에 있는 값을 출력해줍니다.
mov al, TAB              ; al에 Tab의 ASCII 값을 넣어줍니다.
call WriteChar        ; Tab의 ASCII 값을 출력합니다.
loop L1                     ; L1의 끝 - ecx가 0 이 아니라면 L1 의 처음 부분으로 돌아갑니다.

call Crlf                    ; 개행 
ret                            ; 리턴
Rand1 ENDP

Rand2 PROC                      ; Rand2 프로시저 정의
; Generate ten pseudo-random integers between -50 and + 49 - -50 ~ +45 에 해당하는 부호 있는 정수 생성
mov ecx,10         ; ecx에 10을 넣어서 반복문이 반복될 횟수 설정

L1:                                ; L1 시작 부분
mov eax, 100 ; 0-99
call RandomRange ; 무작위 int 형 생성 - 범위 안에서
sub eax, 50 ; 0 - 99 사이에서 생성 된 값에 50을 빼줍니다 
call WriteInt ; 연산 된 값을 출력해줍니다.
mov al, TAB ; Tab의 ASCII 값을 al에 넣어줍니다.
call WriteChar ; Tab 출력
loop L1                      ; L1의 끝 - ecx가 0이 아니라면 L1의 처음 부분으로 돌아갑니다.

call Crlf                     ; 개행
ret                            ; 리턴
Rand2 ENDP
END main 

Posted by NullBr4in
Programming/Assembly2012. 1. 23. 16:02


책에서 보다가 괜찮은 샘플소스가 있어서 첨부해봅니다.

전경색과 배경색을 바꾸고

배열을 16진수로 출력하고

부호 있는 정수에 대해 입력을 받아서

입력한 정수를 10진수, 16진수, 2진수로 출력하는 샘플 코드 입니다.




TITLE Library Test #1: Integer I/O (TestLib1.asm)

;Tests the Clrscr, Crlf, DumpMem, ReadInt,
;SetTextColor, WaitMsg, WriteBin, WriteHex,
; and WriteString procedures.

Include Irvine32.inc
.data
arrayD DWORD 1000h, 2000h, 3000h                ; DWORD 형 크기가 3인 배열 arrayD 생성
prompt1 BYTE "Enter a 32-bit signed integer:",0 ; 문자열 생성
dwordVal DWORD ?                                      ; DWORD 형 변수 dwordVal 생성

.code
main PROC
; Set text color to yellow text on blue background:
mov eax, yellow + (blue * 16)              ; 배경색을 blue로, 글씨색을 yellow로 설정하겠다는 것을 eax에 입력
call SetTextColor                               ; eax값을 인자로 가져가는 SetTextColor 함수 호출로 설정
call Clrscr ; 화면 초기화

; Display the array using DumpMem.
mov esi, OFFSET arrayD ; arrayD의 오프셋을 esi에 저장
mov ecx, LENGTHOF arrayD ; arrayD의 길이를 ecx에 저장
mov ebx, TYPE arrayD ; arrayD의 타입, 즉 DWORD를 ebx에 저장(4가 저장됩니다.)
call DumpMem ; memory 출력. 위에서 입력한 esi,ecx,ebx를 기준으로 출력합니다.
                                                                ; 함수들에 대해 자세한 내용은 따로 포스팅 하겠습니다. 
call Crlf ; 개행

; Ask the user to input a signed decimal integer.
mov edx, OFFSET prompt1                  ; "Enter a 32-bit signed integer: " 문자열의 오프셋을 edx에 저장
call WriteString                                   ; edx에 있는 문자열 출력 
call ReadInt ; integer 입력 받기
mov dwordVal, eax ; ReadInt는 eax로 받은 값이 리턴되기 때문에 받은 값을 dwordVal 변수에 저장

; Display the integer in decimal, hexadecimal, and binary.
call Crlf ; new line
call WriteInt ; 부호 있는 10진수로 출력
call Crlf
call WriteHex ; 부호 있는 16진수로 출력
call Crlf
call WriteBin ; 부호 있는 2진수로 출력
call Crlf
call WaitMsg ; "Press any key..." 문자열 출력 후 키 입력 대기

; Return console window to default colors.
mov eax, lightGray + (black * 16)         ; 작업이 끝났으면 실행창의 환경설정을 다시 돌려줍니다.
call SetTextColor                               ; 바로 위에서 적용한 검은색 바탕에 밝은 회색으로 설정
call Clrscr ; 화면 초기화
exit
main ENDP
END main

Posted by NullBr4in
Programming/Assembly2012. 1. 23. 14:02


"인텔 기반 컴퓨터를 위한 어셈블리 언어 5판" 은 어제부터 시작한 어셈블리 교재입니다.

공부하면서 풀은 연습문제입니다.

1~4는 별로 필요가 없어보여서... 5~8번 문제만 풀었습니다.


5번 - 직접 오프셋 주소 지정

TITLE 4.7.5 eaxmple

;4.7.5-1
;5.1 직접 오프셋 주소지정


INCLUDE Irvine32.inc
.data
Uarray WORD 1000h, 2000h, 3000h, 4000h
Sarray SWORD -1, -2, -3, -4

.code
main PROC
movzx eax, Uarray
movzx ebx, (Uarray+2)
movzx ecx, (Uarray+4)
movzx edx, (Uarray+6)
call DumpRegs
exit
main ENDP
END main
  

5번 - 직접 오프셋 주소 지정 - 2
TITLE 4.7.5 eaxmple

; 4.6.5-2
;5.2 직접 오프셋 주소지정

INCLUDE Irvine32.inc
.data
Uarray WORD 1000h, 2000h, 3000h, 4000h
Sarray SWORD -1, -2, -3, -4

.code
main PROC
movsx eax, Sarray
movsx ebx, (Sarray+2)
movsx ecx, (Sarray+4)
movsx edx, (Sarray+6)
call DumpRegs
exit
main ENDP
END main 
 
6번 - 피보나치 수
TITLE 4.7.6 eaxmple

; 4.6.6
; Fibonacci
; 피보나치 수 12항 까지

INCLUDE Irvine32.inc
.data
first BYTE 1
second BYTE 0
sum BYTE ?

.code
main PROC

movsx ebx, first
movsx edx, second
mov ecx, 12
L1:
mov eax, ebx
add eax, edx
call DumpRegs

mov bl, dl
mov dl, al

loop L1
exit
main ENDP
END main
  
7번 - 산술식
TITLE 4.7.7 eaxmple

; 4.6.7
; 산술식
; EAX = -val2 + 7 - val3 + val1

INCLUDE Irvine32.inc
.data
val1 SDWORD 8
val2 SDWORD -15
val3 SDWORD 20

.code
main PROC
mov eax, val2 ; EAX = FFFFFFF1h
neg eax ; EAX = Fh
add eax, 7 ; EAX = 16h

mov ebx, val3 ; EAX = 16h, EBX = 14h
neg ebx ; EAX = 16h, EBX = FFFFFFECh

add eax, ebx ; EAX = 2h

add eax, val1 ; EAX = Ah

call DumpRegs
exit
main ENDP
END main
 


8번 - 문자열의 역방향 복사
TITLE 4.7.8 eaxmple

; 4.6.8
; 8. 문자열의 역방향 복사



INCLUDE Irvine32.inc
.data
source BYTE "This is the source string", 0
target BYTE SIZEOF source DUP('#')
.code
main PROC
xor esi,esi ; esi 초기화
mov esi, OFFSET source ; source 주소를 esi에 넣는다.
add esi, SIZEOF source ; esi가 source의 오프셋에 source 길이를 더한다.
dec esi ; 그럼 1 byte가 넘어가게 되는데 이걸 다시 빼준다.
xor edi, edi ; edi 초기화
mov edi, OFFSET target ; target 주소를 edi에 넣는다.
mov ecx, LENGTHOF source ; ecx(loop count)에 source의 길이를 넣는다.

L1: ; loop L1
mov al, [esi] ; [esi], 즉 source 주소지에 있는 값을 al 로 복사한다.
mov [edi], al ; al, 의 값을 [edi], target 주소이제 복사한다.
inc edi ; edi 주소를 1 증가시킨다.
dec esi ; esi 주소를 1 감소시킨다.
loop L1 ; L1 으로 돌아가라
mov esi, OFFSET target ; target 주소를 esi에 넣어라
mov ebx, 1 ; ebx에 1을 넣어라. DumpMem 에서 사용할 값 - byte format
mov ecx, SIZEOF target ; target의 크기(SIZE * LENGTH)를 ecx 에 넣어라.
call DumpMem ; DumpMem 호출.

exit
main ENDP
END main
 







이 중에서 8번은 약간 헤깔립니다..
뭔가 비효율적으로 코딩한거 같은데...
흠...........
 
Posted by NullBr4in
Programming/Assembly2012. 1. 23. 01:58


리버싱을 공부하면서 계속 어셈블리어에 대한 기초가 약하다고 느꼈었는데 

이번에는 시간 내서 기초를 잡고 가기로 마음 먹었습니다.

먼저 설치 전에 Irvine library 를 설치해주세요

이건 첨부파일에 첨부해놨습니다.



같이 첨부된 AsmHighlighter1_5.vsix 파일은 Visual Studio 에서 asm 코딩 할 때 보기 좋게 색을 넣어줍니다.



Visual Studio 2010 에 MASM 설정하는 것으로 시작해 보겠습니다. 
 



1. 먼저 첨부 파일을 받아서 압축을 풀어줍니다. 저는 C:\Irvine 폴더에 압축을 풀었습니다.


2. Visual Studio 2010 을 키신후, File -> New -> Project 를 하여 새로운 Project 를 추가합니다.


3. 'Win32 Console Application" 을 선택해주시고 이름은 아무거나 넣어주시고 OK를 눌러주세요. 저는 "asmtest"라고 넣었습니다.


4. 처음에 뜨는 창에서 "Next"를 눌러줍니다.


5. "Empty project" 에 체크하여 빈 프로젝트를 생성합니다.


6. 프로젝트가 생성되었으면, 생성 된 프로젝트에서 마우스 오른쪽 버튼을 누르시고 "Build Customizations"를 선택해 줍니다.


7. 그림에서 보이는 부분처럼 masm 에 체크를 해줍니다.


8. 이제 소스 파일을 생성해봅시다. Source File 폴더에서 마우스 우측 버튼 누른 다음 Add->New Items 를 선택해줍니다.


9. 위와 같은 창이 뜨는데, 여기서 "Utility" 에 있는 "Text File"을 선택해주고 이름을 넣습니다. 여기서 주의할 점은 확장명은 ".asm"


10. 소스 추가가 끝나셨으면 다시 프로젝트를 누르시고 우측 버튼을 눌러서 "Properties"를 눌러줍니다.


11. "Configuration Properties"->"Linker"->"General" 순서대로 들어가시면 "Additional Library Directories"가 있습니다.
     여기에 아까 설치한 Irvine library의 경로를 넣어주시면 됩니다.


12. "Configuration Properties" -> "Linker" -> "Input" 으로 들어가신다음에 "Additional Dependencies" 에 
     "irvine32.lib" 를 맨 앞에 추가해주세요. 추가하시고 뒤에 ' ; ' <- 을 꼭 넣어주셔야 합니다.


13. " Configuration Properties" -> "Microsoft Macro Assembler" -> "General" 에 들어가보시면 "Include Paths"라고 있습니다.
     여기에도 Irvine library의 설치 경로를 넣어주시면 됩니다.


14. 드디어 설정이 끝났습니다! 책에 있는 예제를 넣고 컴파일 해봅니다.


15. 정상적으로 작동되는 것을 확인합니다!! 

수고하셨습니다! 
Posted by NullBr4in
프로젝트/창과 방패2012. 1. 22. 17:37


성현이가 만든 키로거를 간단하게 훑어보고 올린 제목의 "간단한 키로거"라는 거에서 

proja.exe에서 hook.dll 을 로드하여 SetWindowsEx 함수를 이용하여 전역 후킹하는 것으로 예상 함.
(타겟 프로그램이 따로 없는듯 하기 때문.)

proja.exe 파일과 hook.dll 파일, 두개의 파일로 이루어진 키로거


- proja.exe

분석 중, 0040BAE0h 에 위치부터 성현이가 만든 키로거의 핵심 루틴으로 보이는 것을 발견.

함수명을 확인한 결과, 

.text:0040BB13 mov     esi, esp
.text:0040BB15 push    offset aHook_dll                ; "hook.dll"
.text:0040BB1A call    ds:LoadLibraryA

위와 같은 부분을 찾았으며, "hook.dll" 을 로드하는것을 볼 수 있다.
그리고 해당 루틴안에는 아마도 분기문으로 로드가 실패했을때는
 "hook.dll을 로드할 수 없습니다."라는 MessageBox 출력 후 프로세스 종료

만약 성공시에는 계속 진행되는데,

.text:0040BB67 push    offset aGetmsgproc              ; "GetMsgProc"
.text:0040BB6C mov     edx, dword_4237AC
.text:0040BB72 push    edx
.text:0040BB73 call    ds:GetProcAddress

을 보면 "hook.dll"에서 export 한 함수인 GetMsgProc함수 주소를 가져오는 것을 볼 수 있다. 

.text:0040BB83 cmp     dword ptr [ebp-4], 0
.text:0040BB87 jnz     short loc_40BBCF
.text:0040BB89 mov     esi, esp
.text:0040BB8B push    0
.text:0040BB8D push    offset unk_420060
.text:0040BB92 push    offset unk_421008
.text:0040BB97 mov     eax, [ebp+8]
.text:0040BB9A push    eax
.text:0040BB9B call    ds:MessageBoxA
.text:0040BBA1 cmp     esi, esp
.text:0040BBA3 call    __chkesp
.text:0040BBA8 mov     esi, esp
.text:0040BBAA mov     ecx, dword_4237AC
.text:0040BBB0 push    ecx
.text:0040BBB1 call    ds:FreeLibrary
.text:0040BBB7 cmp     esi, esp
.text:0040BBB9 call    __chkesp
.text:0040BBBE mov     esi, esp
.text:0040BBC0 push    1
.text:0040BBC2 call    ds:ExitProcess

그 이후 역시 위와 같이 분기문을 통해 함수 주소를 가져오는것이 실패하면 메시지박스 출력 후 
프로세스를 종료하는 것을 볼 수 있다.

만약 가져오는데 성공 했다면 아래와 같이 성현이가 사용한 후킹 기법의 핵심이라 볼 수 있는
SetWindowsHookEx 함수를 이용해 후커를 설치하는 부분을 볼 수 있다.

.text:0040BBCF mov     esi, esp
.text:0040BBD1 push    0
.text:0040BBD3 mov     edx, dword_4237AC
.text:0040BBD9 push    edx
.text:0040BBDA mov     eax, [ebp-4]
.text:0040BBDD push    eax
.text:0040BBDE push    3
.text:0040BBE0 call    ds:SetWindowsHookExA

SEtWindowsHookEx 를 사용하여 지역 후킹 또는 전역 후킹을 하는데,
디버거로 분석하기전 실행을 통해 봤을때는 전역 후킹으로 보였다.
타겟 프로그램이 따로 없어보였기 때문.

직접 이 부분을 디버거로 호출 부분의 스택 부분을 본다면,

000000000018F86C  0038100500000003
000000000018F874  0000000000380000   "hook.dll"
000000000018F87C  0040100500000001
000000000018F884  CCCCCCCC00000000

이처럼 나와 있다.(64비트라 좀...)

이것과 같이 볼 것은 SetWindowsHookEx 함수 정보인데,

HHOOK WINAPI SetWindowsHookEx( __in int idHook,
__in HOOKPROC lpfn,
__in HINSTANCE hMod,
__in DWORD dwThreadId);
위와 같다.

그럼 스택을 이용해서 성현이가 작성한 코드에서 호출 한 것을 봐보면,

SetWindowsHookEx(3, GetMsgProc의 주소, 1, 0); 와 같은 방법으로 호출 한 것을 알 수 있다.

먼저 첫번째 인자는
WH_GETMESSAGE
                3

Installs a hook procedure that monitors messages posted to a message queue. For more information, see the GetMsgProc hook procedure.

즉 메시지 큐로 보내는 메시지 전체를 후커로 보낸다는 의미이고, 뒤에 설명을 보면 GetMsgProc 훅 프로시저에서 사용하는 걸 유추해 볼 수 있다.

그다음 두번째 인자는
후커, 즉 훅 프로시저의 포인터이고, 이건 위에서 얻은 GetProcAddress 함수의 리턴 값을 넣으면 될 것 같다.
세번째는 LoadLibrary 함수를 통해 얻은 "hook.dll" 핸들 값을 넣어주면 되고,
네번째는 후킹할 ThreadId 를 넣어주면 되는데, 특정 ThreadId 를 넣을 시 지역 후킹이 되고, 
0을 넣을 경우 전역 후킹이 되는데, 여기서 성현이는 전역 후킹을 사용했다.

계속해서 코드를 봤더니 역시 분기문을 통해 실패할 경우 메시지창을 띄우고 종료, 아닐 경우 다음으로 계속 진행되는데
여기서는 더이상 쓸모 있는 부분이 아니라 생략한다.



-hook.dll
자 이제 hook.dll 을 분석해보자.
일단 proja.exe 를 분석해서 알아낸 결과 hook.dll 에서 export 하는 함수인 GetMsgProc 함수를 찾아서 접근 해보았다.

GetMsgProc의 분기문 전까지 내용은 아래와 같다.

push    ebp
mov ebp, esp
sub esp, 48h
push ebx
push esi
push edi
lea edi, [ebp+var_48]
mov ecx, 12h
mov eax, 0CCCCCCCCh
rep stosd
mov eax, [ebp+arg_8]
cmp dword ptr [eax+4], 100h
jnz short loc_100010AB

마지막에서 두번째 줄인, cmp dword ptr [eax+4], 100h 부분은 후킹한 메시지의 wParam 값을 확인 후 분기하는거 같은데,
일단 키로거이기 때문에 100h은 WM_KEYDOWN 이나 KEY와 관련된 값이란걸 유추 할 수 있다.
확인 하기 위해 msdn 에 검색해보면
#define WM_KEYDOWN                      0x0100
위와 같이 WM_KEYDOWN 이 0x0100 으로 가져온 메시지에서 wParam이 WM_KEYDOWN 메시지인지 확인 한 후 분기하는걸 알 수 있다.

mov     esi, esp
push 0 ; hTemplateFile
push 80h ; dwFlagsAndAttributes
push 4 ; dwCreationDisposition
push 0 ; lpSecurityAttributes
push 0 ; dwShareMode
push 40000000h ; dwDesiredAccess
push offset FileName ; "c:\\test.txt"
call ds:CreateFileA
WM_KEYDOWN 일 경우엔, 처음에 CreateFile 함수를 통해 c:\test.txt의 파일 포인터를 가져오는 것을 알 수 있다.
이것으로 키값을 proja.exe의 윈도우에 출력이 아니라 해당 파일에 출력하는 것이라 유추 해 볼 수 있다.

다음으로 

push    2               ; dwMoveMethod
push 0 ; lpDistanceToMoveHigh
push 0 ; lDistanceToMove
mov ecx, [ebp+hObject]
push ecx ; hFile
call ds:SetFilePointer

SetFilePointer 함수를 사용하는데, msdn에 찾아보았다.
DWORD SetFilePointer(   HANDLE hFile,   
                        LONG lDistanceToMove,   
                        PLONG lpDistanceToMoveHigh,   
                        DWORD dwMoveMethod); 
This function moves the file pointer of an open file. A RAPI version of this function exists, called CeSetFilePointer (RAPI).

설명을 읽어보면 파일포인터를 이동하는 함수란걸 알 수 있다.
아마도 이걸 사용한거면 새로 입력 받는 키 값은 파일에서 맨 마지막에 붙이는 것 같은데,, 한번 봐보자,

첫번째 인자값엔 c:\test.txt 파일의 핸들을 넣었고,
두번째 인자값엔 옮길 위치를 넣는데, 0으로 지정한 것으로 보아 뒤에 나오는 네번째 인수에서 결정 할 것 같다.
세번째 인자는 Not supported; must be NULL or point to a value of zero. ,, 파일의 크기가 2기가 이상일 경우 사용하는것인데 여기선 0으로, 즉 사용 안함
네번째 인자는 [in] Starting point for the file pointer move. The following table shows possible values for this parameter.,, 파일 포인트 이동 시작 지점을 정해주는 것이다.
0x0 FILE_BEGIN - Indicates that the starting point is zero or the beginning of the file.
0x1 FILE_CURRENT - Indicates that the starting point is the current value of the file pointer.
0x2 FILE_END - Indicates that the starting point is the current EOF position.
이렇게 세가지가 있으며 0x2로 되어 있으니 파일 포인트 시작지점을 맨마지막으로 설정한다.

push 0 ; lpOverlapped
lea edx, [ebp+NumberOfBytesWritten]
push edx ; lpNumberOfBytesWritten
push 1 ; nNumberOfBytesToWrite
mov eax, [ebp+arg_8]
add eax, 8
push eax ; lpBuffer
mov ecx, [ebp+hObject]
push ecx ; hFile
call ds:WriteFile

그다음 나오는게 위의 WriteFile 함수 호출부분인데, 이건 받은 메시지에서 눌린 키값을 파일에 써주는 부분이다.

mov edx, [ebp+hObject]
push edx ; hObject
call ds:CloseHandle

그다음 나오는게 CloseHandle 호출이며 이 함수는 파일 핸들을 닫아주는 것으로 보인다.

이로써 분석은 끝나고,,
막는 방법은 여러가지를 생각 할 수 있다.
솔직히 쉽게는 해당 프로세스의 패턴을 프로그램에 등록해서 막을 수는 있고 가장 쉬운 방법이지만
프로그램을 재컴파일하여 만약 패턴부분이 바뀐다면 사용 할 수 없는 방법이다. (지속적인 업데이트 필요)

하지만 이번 프로젝트에서는 저런 방식(키로거의 패턴을 뽑아내서 해당 프로그램 차단 방식)을 하려는게 아니니까,,

원래는 현재 작업 중인 프로젝트가 있는데 막바지라 진행 중이던 프로젝트를 끝마치고 방어 프로그램을 제작해서 올리려고 했는데
모처럼 창과방패 프로젝트에서 처음 나온 결과물이니 최대한 빨리 답변 해주고 싶어서 이렇게 일단 보고서라도 만들었다.
현재 진행중인 프로젝트는 1~2틀 내로 끝날 것 같다.
진행중인 프로젝트가 끝나면 성현이의 키로거 전용 탐지기가 아니라, Message 를 이용한 hooking 을 봉쇄하는 프로그램을 만들어서 업로드 하겠다.

-------------------------------------------------------------------------------------

해당 글은 분석 했을 당시 동아리 게시판에 올렸던 글입니다.

저때 진행 중이었던 작업이 d3d hooking 분석 이였고, 지금은 그 작업을 끝내고 방패프로그램을 만드는 중입니다~ 

'프로젝트 > 창과 방패' 카테고리의 다른 글

첫번째 방패 - 첫번째 키로거  (19) 2012.01.22
창과 방패 프로젝트란..  (0) 2012.01.21
Posted by NullBr4in
프로젝트/D3D Hooking2012. 1. 22. 16:56


창과 방패 및 d3d hooking 분석 작업을 하면서 hooking 의 방어 방법에 많은 생각을 하게 되었습니다.

먼저 방어 방법에 앞서 hooking 에는 무슨 종류가 있는지, 또 어떤 방법으로 후킹 하는지에 대해 알아보고

막을 수 있는 방법에 대해 연구해보도록 하겠습니다.

먼저 웹서핑을 통해 정리가 깔끔하게 되어있는 후킹의 분류에 대해 퍼왔습니다.



API Hooking은 크게 유저모드와 커널모드 두가지로 나뉜다.

 

 

User-Mode Hooking
 

  • IAT(Import Address Table) Hooking: IAT 에 적혀있는 API 의 주소를 자신의 함수주소로 바꾸고 자신의 함수 끝에 다시 원래 API 주소로 돌려주는 방식. 가장 일반적으로 바이러스에서 사용하는 기법.

  • Inline Function Hooking (Detour Hooking): 사용할 API 의 첫 5바이트를 자신의 함수주소로 Jmp 하는 코드로 바꾸고 자신의 코드에서 다시 원래 API 의 바뀐 코드를 수정해주고 API 시작위치로 돌려주는 방식. IAT 후킹보다 지능적이여서 찾아내기가 쉽지 않다. 요새 많이 등장한다.

 

Kernel-Mode Hooking (루트킷)
 

  • SSDT(System Service Descriptor Table Modification): SSDT 가 가리키는 주소를 후킹 함수의 주소로 바꾸고 그 함수 호출후 다시 원래 커널 API 의 주소로 돌려주는 기법. 50% 이상의 루트킷이 사용하는 기법. 이런 기법은 프로세스, 파일의 은폐에 많이 사용됨.

  • DKOM(Direct Kernel Object Modification): 커널 Object 를 직접 조작해서 실행되는 프로세스, 스레드, 서비스, 포트, 드라이버 및 핸들의 Entry 를 실행리스트(PsActiveProcessHead, PsActiveModuleHead....)에서 감추는 기법.

  • SYSENTER: 유저모드에서 시스템 호출로 넘어갈때 INT 2E(for Windows 2000)/ SYSENTER 를 사용하게 되는데 호출후 시스템 서비스의 핸들러는 IA32_SYSENTER_EIP 라는 레지스터리에 저장된다. 커널 드라이버를 설치하여 해당 값을 수정하여 루트킷을 호출하고 다시 원래 값으로 돌려주는 기법.

  • Filter Device Drivers: 시큐리티 제품의 하단에 filter device driver 로 등록하는 기법이다. 부트 타임에 로드됨으로써 다른 어떤 안티바이러스 제품보다 먼저 실행된다.

  • Runtime Detour Patching: 커널 메모리를 직접 조작함으로써 그 메모리의 포인터가 루트킷을 가르키게 함으로써 커널 함수들을 후킹하는 기법. 예를 들면 Exception 을 일으키고 Exception Handle 을 컨트롤하는 IDT 레지스터를 자신을 가리키는 주소로 써줌으로써 후킹목적을 달성한다.

  • IRP table Modification: 디바이스 드라이버가 네트웍 패킷을 처리하거나 파일을 쓸때 사용하는 I/O Request Packets을 제어하는 Dispatch Routine 은 DEVICE_OBJECT 구조체에 저장된다. 바이러스에서 사용하는 루트킷은 IoGetDeviceObjectPointer 란 API를 사용하여 DEVICE_OBJECT 구조체에서 DRIVER_OBJECT 의 위치를 선정해줄수 있다. 즉 다른 Original Driver Call 이 일어나기 전에 자신의 루트킷을 먼저 실행하여 Call 결과를 조
    작한다.
     
출처 : http://cafe.naver.com/cheatenginekorea.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=25962

'프로젝트 > D3D Hooking' 카테고리의 다른 글

Direct3D Hooking 분석 해봤습니다.  (13) 2012.01.20
Posted by NullBr4in