SlideShare a Scribd company logo
REVERSING[6] Reverse Engineering
목차
• Code injection //리버싱 핵심 원리 3부 + 2부
• 원격 쓰레드를 이용
• 고급 언어로 프로그래밍 //27장
• 어셈블리어를 이용 //28장
• 인라인 패치 //20장
• 부록
• 참고 사이트
CODE INJECTION • 원격 쓰레드 이용
• 인라인 패치
코드 인젠션이란?
• 실행중인 대상 프로세스에 코드를 삽입한 후 실행하는 기법
• 일반적으로 CreateRemoteThread API를 이용하기 때문에Thread 인젝션이라고도 함
• 코드는 쓰레드 프로시저(쓰레드를 만들 때 인자로 넣는 함수, 쓰레드 메인)으로 넣고, 인자는 쓰
레드의 파라미터로 전달한다.
• 코드와 데이터를 각각 인젝션 해주는 것
<injector> <대상
프로그램>
필요한 문자열
함수 포인터
내가 만든 코드
이용
inject!
DLL INJECTION VS CODE INJECTION
• DLL injection은 DLL 자체를 프로세스에 올린다.
• DLL의 코드에서 사용하는 모든 데이터는 DLL의 데이터 영역에 있다.
• Ex) 문자열 등...
• -> 따로 데이터를 injection할 필요가 없다.
• Code injection은 코드 뿐만 아니라 코드에서 이용하는 데이터도 같이 인젝션 해 주어야 한다.
• 코드 인젝션을 사용하는 이유
• 메모리를 조금만 차지한다.
• DLL 인젝션보다 작은 공간을 차지한다.
• 흔적을 찾기 어렵다
• DLL 인젝션은 해당 프로세스 메모리에 흔적을 남기기 때문에 간단히 인젝션 여부를 알 수 있다.
• 코드 인젝션은 쉽게 흔적을 남기지 않는다. (알아내는 방법이 있긴 하다)
• 기타
• 코드 인젝션은 DLL 파일이 필요하지 않다. 인젝션하는 프로그램만 필요하다(injector)
• DLL injecton은 규모가 크고 복잡한 일을 수행할 때, Code injection은 규모가 작고 간단한 일을 수행할 때 사용한다.
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
• 쓰레드 인자로 넣을 구조체의 형식이다.
• 이 구조체 모양대로 내용을 써서 대상 프로그
램의 메모리에 써 둘 것이다.
• 쓰레드를 만들 때, 이 구조체를 저장해 둔 대상
프로그램의 가상메모리 주소를 인자로 준다.
• 대상 프로그램은 자신의 가상 메모리에 저장된 데이터를
이용하여 일을 할 수 있다.
• 함수 포인터를 이용하기 쉽도록 재정의하는 코드
이다.
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
• 위에 있는 두 줄은 함수 선언이다.
• 아래는 메인 함수이다.
• 인자로 PID를 입력 받는다.
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
• 인젝션할 코드이다.
• 인자로 받은 주소를 캐스팅한다.
• 미리 저장해 둔 함수 포인터(LoadLibraryA와 GetProcAddress)
와 문자열(user32.dll, MessageBoxA) 이용하여
user32.dll(MessageBoxA API가 저장된 DLL)을 열고,
MessageBoxA 함수 주소를 얻는다.
• 미리 저장해 둔 문자열을 이용하여 MessageBoxA함수에 인
자를 넣고 함수를 호출한다.
• kernel32.dll은 대부분의 프로그램이 이용하고, kenel32.dll과
같은 시스템 라이브러리는 모든 프로세스가 같은 가상메모
리에 매핑하기 때문에 injector에서 주소를 구해 넣어도 괜찮
았다.
• kernel32.dll을 이용하지 않는 dll도 있으니 code injection하기 전에
확인해야 한다.
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
• 본격적으로 코드를 인젝션하는 함수이다.
• 일단 대상 프로세스에 넣을 구조체(쓰레드 인자로 이용될)
에 내용을 적는다.
• 아래 구조체 모양을 참고해 보자.
• pFunc은 함수 포인터(주소)가 들어갈 공간이다.
• LoadLibraryA함수와 GetProcAddress함수의 주소를 구
해 넣었다.
• 이들은 MessageBoxA를 호출하기 위해서 구한 것이
다.
• szBuf는 문자열들이 들어갈 공간이다.
• 이들은 LoadLibraryA의 인자로 줄 DLL 이름과
GetProcAddress의 인자로 줄 함수 이름, 그리고
MessageBoxA를 호출할 때 인자로 줄 문자열들의 이
름이다.
• 다른 API를 이용하고 싶다면 그 API가 속한 DLL과 함수
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
• 프로세스를 열고 방금 내용을 채워 넣은 구조체를 그대로 써 준
다.
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
• 그 다음 인젝션할 코드(함수)의 크기를 구하고, 그 크기만큼 메모
리를 확보하여 내용을 쓴다.
• 인젝션할 코드는 이 함수 바로 다음에 올 것이다.
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
• 방금 넣은 코드의 주소는 pRemoteBuf[1]이고, 저장한 구조체의 주소는 pRemoteBuf[0]이
다.
• 이들을 인자로 넣어 CreateRemoteThread함수를 호출한다.
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
•주의!
• 컴파일은 반드시 Release mode로 한다.
• Debug모드로 하면 추가적으로 생성되
는 코드가 있다.(추가적으로 불리는 함
수가 있다)
• 따라서 Debug모드로 컴파일한 코드
를 인젝션하면 대상 프로그램에는 없
는 함수를 호출하기 때문에 에러가 나
고, 프로그램이 종료된다.
CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래
밍
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 어셈블리어를 이용하여 인젝션할 코드를
생성하면 조금 더 자유로운 형태로 이용
가능하다.
• 문자열(데이터)과 코드를 함께 넣을 수 있다.
• 올리디버거로 간편하게 만들 수 있다.
• 아무 EXE나 열고 빈 칸에 옆과 같은 내용을 쓰자
• 단, call 주소는 상황에 맞게 쓴다.
• 또는 인라인 어셈블리를 이용해도 됨
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 함수 프롤로그
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 인자로 받은 struct 주소를 가져온다.
• 이번에는 struct에 함수 주소(LoadLibraryA와
GetProcAddress)만 넣었다.
• 이 주소를 하드코딩 하지 않는 이유는
ASLR이라는 기법 때문이다.
• ASLR : Address Space Layout
Randomization.
• 시스템 DLL은 모든 프로그램이 같은
주소의 가상메모리에 올라온다. 하지만
재부팅하면 그 ‘같은 주소’가 될 주소가
바뀐다. 따라서 함수 주소는 매번 구해서
넣는다.
• ESI에는 LoadLibraryA, ESI+4에는
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• LoadLibraryA함수를 호출하는 코드이다.
• 사용할 dll 문자열은 user32.dll이다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• user32.dll 문자열을 스택에 저장한다.
• 리틀 엔디안으로
• 문자열 뒷부분부터 거꾸로
• 맨 마지막에 push esp를 해서 문자열의 맨
처음 부분이 저장된 주소를 스택에
넣는다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 순서와 리틀 엔디안에 주의한다.
• 문자열 맨 끝에 null (0)이 들어가는 것에
주의한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• ESI(인자로 받은 주소)에 저장된 구조체의 첫
번째 멤버인 LoadLibraryA 함수 주소를
이용하여 함수를 호출한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• GetProcAddress함수를 호출하는 코드이다.
• 사용할 함수 이름 문자열은 MessageBoxA이다.
• GetProcAddress함수는 WINAPI 함수 호출
규약을 이용한다.
• WINAPI 함수 규약은 stdcall 함수 호출
규약이다.
• -> 함수 인자를 역순으로 push한다
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 조금 전과 같은 방법으로, MessageBoxA
문자열을 스택에 저장하고, 주소를 넘긴다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• LoadLibraryA함수의 반환 값인 user32.dll의
핸들을 push한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• ESI+4에 저장된, 구조체의 두 번째 값인
GetProcAddress함수를 호출한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 이제 MessageBoxA를 호출할 것이다.
• 문자열은 2, 3번째 인자이다.
• 단순한 push방법을 이용하기엔 골치
아프다.
• push방법 말고 다른 방법을 이용해본다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 먼저 uType에 해당하는 인자를 push한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 만드는 중에는 일단 call 00400000(뒤에 아무
숫자, 짧지 않은 길이의)를 입력해 둔다.
• call을 하면 함수 call 바로 아래 주소(다음에
실행할 코드 주소)를 스택에 push한 뒤
점프한다.
• 사실 여기서 사용하는 주소는 상대
주소이다.
• 기계어에는 자신으로부터 얼마나 떨어진
곳으로 점프하라 라는 정보가 있다.
• 올리 디버거가 알아서 상대 주소로 계산해서
기계어를 만들어 준다.
• 어셈블리어로는 그냥 주소를 써 줘도
된다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 상대 주소
계산법
JMP 100
EIP : 50
(EIP는 다음에 실행할 코드 주소를
가리킨다.)
100
<어셈블리어
표현>
E9 B0 00 00 00
EIP : 50
(EIP는 다음에 실행할 코드 주소를
가리킨다.)
100
<기계어 표현>
시작주소 45 시작주소 45
• 00000050 + 000000Bo = 100
• 리틀엔디안으로 B0000000
• 주의 : 작은 바이트를 이용하는
short jmp라는 명령어도 있다.
일단은 그냥 점프를 이용하기
위해 4바이트의 주소를 넣자.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 상대 주소
계산법
EIP : 105
(EIP는 다음에 실행할 코드 주소를
가리킨다.)
JMP 45
<어셈블리어
표현>
<기계어 표현>
시작주소 100, 코드 크기 5바이트
시작주소 45
EIP : 105
(EIP는 다음에 실행할 코드 주소를
가리킨다.)
E9 40 FF FF FF
시작주소 100, 코드 크기 5바이트
시작주소 45
• 105 – c0 = 45
• -c0 = 4바이트로 FFFFFF40
• 00000105 + FFFFFF40 = 45
• 리틀 엔디안으로 40FFFFFF
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 수정하려는 줄을 클릭한 후 오른쪽키-binary-
edit으로 출력하고자 하는 문자열을 입력한다.
• 뒤에서 두 번째 인자인 메시지 박스 제목을
입력한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 맨 끝 null을 입력하는 것을 잊지 말자
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 위의 call 목적지가 문자열 바로 다음을
가리키도록 수정한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 똑같은 방식으로, 인쇄하고자 하는 문자열을
넣는다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• hWnd 인자로 0을 넣는다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• MessageBoxA함수를 호출한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 함수 에필로그이다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 파일을 저장하고 헥사 에디터로 연다.
• 기계어를 얻기 위함이다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 기계어를 복사해서 배열에 넣을 형태로 수정한다.
• 메모장으로 노가다하든 에디터를 쓰든...
• NotePad++같이 매크로를 이용할 수 있는 프로그램을 추천함
• 이것이 쉘코드 제작의 기본이다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 이번 구조체에는 함수 주소만을 넣는다.
• ThreadProc함수 대신 쉘코드를 넣는다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 메인 함수이다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 쓰레드 인자로 줄 구조체를 넣는
것은 똑같다.
• 문자열은 더이상 넣지
않는다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 함수 주소를 이용하여 사이즈를 구하던 것과는 달리, 단순히 쉘코드 배열의 사이즈를 구해 넣는다.
• 쉘코드를 프로세스에 쓰고 쓰레드를 생성한다.
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 성공!
CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이
용
• 쉘코드를 0xFF 식의 char 배열로 만들기보다는
위와 같이 스트링으로 쓰는게 더 정석적이다.
• 코드상에서 char 배열 초기화를 제외한
부분은 달라진 것이 없다.
• 취향 따라 쓴다..
TIP
• jmp를 다른 방식으로 표현한다면?
• jmp 401000를 아래와 같이 표현할 수 있다.
• push 401000
• ret
• Call을 다른 방식으로 표현한다면?
• push 다음 주소
• jmp 함수 주소
• 이렇게 같은 코드를 다른 방식으로 쓸 수도 있다.
인라인 패치
• 인라인 코드 패치, 인라인 패치
• 원하는 코드를 직접 수정하기 어려울 때 코드 케이브(Code cave)라고 하는 패치 코드를 삽입한 후 실행해 프
로그램을 패치시키는 기법
• 패킹 혹은 암호화 때문에 파일을 직접 수정하기 어려운 경우 많이 사용되는 기법
• 혹은 수정한 코드가 아래 코드를 침범하여 끼워 넣기 애매한 경우에도 이용할 수 있다.
• 매번 프로세스 메모리의 코드를 패치하기 때문에 인라인 코드 패치라고 부른다.
• 파일 자체에서 코드를 수정하는 것이 아니라 파일을 실행한 후 수정하는 것
• 복호화가 끝난 후 가는 OEP를 조작하여 끼워 넣은 코드로 이동하게 한다. 끼워 넣은 코드는 복호화 된 기존 코드에서 수정하고 싶은 부분을 수
정한다.
• 파일 수정을 이용한 Code injection, 그리고 이를 이용한 기존 코드 패치 방법이다.
인라인 패치
인라인 패치
일반적인 코드 패치 인라인 패치
대상 파일 파일 & 메모리
횟수 1번 파일에는 1번만
메모리는 실행될 때마다
방법 Direct
(원하는 위치에 직접 패치)
Indirect
(코드 케이브를 미리 설치한 후
메모리에서 원하는 영역이 복
호화 되었을 때 패치)
인라인 패치
• 패치할 프로그램이다.
• result가 3이 아닌 값을 출력하도록 해보자.
• 패치의 효과를 체험할 수 있도록 UPX 패킹을
적용하자.
인라인 패치
• UPX의 디코딩 루틴을 지나 OEP로 점프하는 코드를 찾았다.
• 이 주소는 앞으로 임의로 넣은 코드의 주소로 바꿀 것
• 임의로 넣은 코드는 디코딩 되어 메모리에 올라온 코드를 수정(패치)하는 일
을 한다.
인라인 패치
• 메인문이다.
• printf를 호출하기 전을 수정하고자 한다.
• 끼워 넣을 코드는 맨 밑 코드영역의 null padding 자리에 넣을 것이다.
• 드래그한 코드는 jmp 코드로 수정할 것이다.
• 정상 작동하기 위해서는 이 코드도 필요하므로 복사해 둔다
• 임의의 코드를 실행하면 드래그한 코드가 jmp 406AA0로 수정될 것(406AA0에는 printf 결과를 조작하기 위한 코드를 끼워
넣을 것)
인라인 패치
• 디코딩 루틴 이후 OEP로 점프하는 코드를 아래 추가한 코드로 점프하도록 변경
• 이 점프로 인해 실행되는 코드 : 기존 main 함수의 printf 전 코드를 jmp 코드로 변경하는 코드
• 바뀐 코드로 인해 아래쪽에 추가한 코드가 실행된다.
• jmp코드로 수정하기 위해 지워진 코드를 넣어 정상적으로 작동하도록 한다.
• 추가 하고싶은 코드를 넣어 임의로 변경한다 (이 경우, printf 인자를 조작하는 일)
• 다시 기존 코드로 점프하여 실행을 이어간다.
인라인 패치
• 성공!
• OEP를 수정할 때, 그 점프하는 코드마저 패킹이나 암호화가 적용
되어 있다면, 그 알고리즘을 알아내야 한다.
• 그래야 패킹/암호화를 풀었을 때 제대로 점프할 수 있다.
• 일부러 약간 돌아서 코드를 실행하게 했다.
• 여건이 된다면 다시 아래로 jmp하지 않고 바로 코드만 수정해도Ok.
부록 • 참고 사이트
참고 사이트
• JMP 상대주소
• https://ko.wikipedia.org/wiki/JMP_(x86_%EB%AA%85%EB%A0%B9%EC%96%B4)
• http://jyj850714.tistory.com/337
• 인라인 패치
• https://asecurity.so/2017/01/hooking-%ED%9B%84%ED%82%B9-code-path-inline-patch-%EC%BD%94%EB%93%9C-
%ED%8C%A8%EC%B9%98-%EC%9D%B8%EB%9D%BC%EC%9D%B8-%ED%8C%A8%EC%B9%98/

More Related Content

PPTX
Linux reversing study_basic_4
 
PPTX
System+os study 1
 
PPTX
System+os study 4
 
PPTX
Windows reversing study_basic_3
 
PPTX
Windows reversing study_basic_9
 
PPTX
System+os study 7
 
PPTX
Windows reversing study_basic_8
 
PPTX
Assembly 스터디 1
 
Linux reversing study_basic_4
 
System+os study 1
 
System+os study 4
 
Windows reversing study_basic_3
 
Windows reversing study_basic_9
 
System+os study 7
 
Windows reversing study_basic_8
 
Assembly 스터디 1
 

What's hot (20)

PPTX
Pwnable study basic_3
 
PPTX
Windows reversing study_basic_1
 
PPTX
Windows reversing study_basic_5
 
PPTX
Pwnable study basic_1
 
PPTX
Windows reversing study_basic_6
 
PPTX
Windows reversing study_basic_4
 
PPTX
System+os study 3
 
PPTX
Linux reversing study_basic_3
 
PPTX
Python
 
PPTX
Assembly 스터디 2
 
PPTX
Windows reversing study_basic_2
 
PPTX
Pwnable study basic_2
 
PPTX
System+os study 5
 
PPTX
Linux reversing study_basic_2
 
PPTX
Web hacking introduction
 
PPTX
Linux reversing study_basic_1
 
PDF
[Kerference] 시작! 리버싱 - 김종범(KERT)
PPTX
System+os study 6
 
PDF
2016317 파이썬기초_파이썬_다중설치부터_Jupyter를이용한프로그래밍_이태영
PDF
잘 알려지지 않은 숨은 진주, Winsock API - WSAPoll, Fast Loopback
Pwnable study basic_3
 
Windows reversing study_basic_1
 
Windows reversing study_basic_5
 
Pwnable study basic_1
 
Windows reversing study_basic_6
 
Windows reversing study_basic_4
 
System+os study 3
 
Linux reversing study_basic_3
 
Python
 
Assembly 스터디 2
 
Windows reversing study_basic_2
 
Pwnable study basic_2
 
System+os study 5
 
Linux reversing study_basic_2
 
Web hacking introduction
 
Linux reversing study_basic_1
 
[Kerference] 시작! 리버싱 - 김종범(KERT)
System+os study 6
 
2016317 파이썬기초_파이썬_다중설치부터_Jupyter를이용한프로그래밍_이태영
잘 알려지지 않은 숨은 진주, Winsock API - WSAPoll, Fast Loopback
Ad

Similar to Windows reversing study_basic_7 (20)

PDF
당신의 디버깅에 니코니코니
PDF
해커가 되고 싶은 자는 나에게... 정보보안 입문과 길 찾기
 
PDF
ant로 안드로이드 앱을 자동으로 빌드하자
PDF
병렬 프로그래밍
PDF
Ch09
PPT
2006 03 15_pe & api hook
PPTX
I os push: 세상에서 가장 간단한 날씨, 대기환경 엡
PDF
[2007 CodeEngn Conference 01] 김기오 - NASM 어셈블러 사용법과 Calling Convention
PDF
Go로 새 프로젝트 시작하기
PPTX
가상화된 코드를 분석해보자
PDF
6. code level reversing
PDF
[16]Obfuscation 101 : 난독화, 프로가드, R8, 트랜스포머 API
PPT
카사 공개세미나1회 W.E.L.C.
PPTX
아이폰에 포팅해보기
PDF
20150306 파이썬기초 IPython을이용한프로그래밍_이태영
PPTX
C#을 사용한 빠른 툴 개발
PPTX
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
PDF
ASP.NET MVC Framework 개발자를 위한 Razor Syntax.pdf
PDF
ModelSim 기초 매뉴얼
PDF
[데브루키] 게임 엔진 아키텍쳐_3장_게임을 위한 소프트웨어 엔지니어링 기초
당신의 디버깅에 니코니코니
해커가 되고 싶은 자는 나에게... 정보보안 입문과 길 찾기
 
ant로 안드로이드 앱을 자동으로 빌드하자
병렬 프로그래밍
Ch09
2006 03 15_pe & api hook
I os push: 세상에서 가장 간단한 날씨, 대기환경 엡
[2007 CodeEngn Conference 01] 김기오 - NASM 어셈블러 사용법과 Calling Convention
Go로 새 프로젝트 시작하기
가상화된 코드를 분석해보자
6. code level reversing
[16]Obfuscation 101 : 난독화, 프로가드, R8, 트랜스포머 API
카사 공개세미나1회 W.E.L.C.
아이폰에 포팅해보기
20150306 파이썬기초 IPython을이용한프로그래밍_이태영
C#을 사용한 빠른 툴 개발
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
ASP.NET MVC Framework 개발자를 위한 Razor Syntax.pdf
ModelSim 기초 매뉴얼
[데브루키] 게임 엔진 아키텍쳐_3장_게임을 위한 소프트웨어 엔지니어링 기초
Ad

Windows reversing study_basic_7

  • 2. 목차 • Code injection //리버싱 핵심 원리 3부 + 2부 • 원격 쓰레드를 이용 • 고급 언어로 프로그래밍 //27장 • 어셈블리어를 이용 //28장 • 인라인 패치 //20장 • 부록 • 참고 사이트
  • 3. CODE INJECTION • 원격 쓰레드 이용 • 인라인 패치
  • 4. 코드 인젠션이란? • 실행중인 대상 프로세스에 코드를 삽입한 후 실행하는 기법 • 일반적으로 CreateRemoteThread API를 이용하기 때문에Thread 인젝션이라고도 함 • 코드는 쓰레드 프로시저(쓰레드를 만들 때 인자로 넣는 함수, 쓰레드 메인)으로 넣고, 인자는 쓰 레드의 파라미터로 전달한다. • 코드와 데이터를 각각 인젝션 해주는 것 <injector> <대상 프로그램> 필요한 문자열 함수 포인터 내가 만든 코드 이용 inject!
  • 5. DLL INJECTION VS CODE INJECTION • DLL injection은 DLL 자체를 프로세스에 올린다. • DLL의 코드에서 사용하는 모든 데이터는 DLL의 데이터 영역에 있다. • Ex) 문자열 등... • -> 따로 데이터를 injection할 필요가 없다. • Code injection은 코드 뿐만 아니라 코드에서 이용하는 데이터도 같이 인젝션 해 주어야 한다. • 코드 인젝션을 사용하는 이유 • 메모리를 조금만 차지한다. • DLL 인젝션보다 작은 공간을 차지한다. • 흔적을 찾기 어렵다 • DLL 인젝션은 해당 프로세스 메모리에 흔적을 남기기 때문에 간단히 인젝션 여부를 알 수 있다. • 코드 인젝션은 쉽게 흔적을 남기지 않는다. (알아내는 방법이 있긴 하다) • 기타 • 코드 인젝션은 DLL 파일이 필요하지 않다. 인젝션하는 프로그램만 필요하다(injector) • DLL injecton은 규모가 크고 복잡한 일을 수행할 때, Code injection은 규모가 작고 간단한 일을 수행할 때 사용한다.
  • 6. CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍 • 쓰레드 인자로 넣을 구조체의 형식이다. • 이 구조체 모양대로 내용을 써서 대상 프로그 램의 메모리에 써 둘 것이다. • 쓰레드를 만들 때, 이 구조체를 저장해 둔 대상 프로그램의 가상메모리 주소를 인자로 준다. • 대상 프로그램은 자신의 가상 메모리에 저장된 데이터를 이용하여 일을 할 수 있다.
  • 7. • 함수 포인터를 이용하기 쉽도록 재정의하는 코드 이다. CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍
  • 8. • 위에 있는 두 줄은 함수 선언이다. • 아래는 메인 함수이다. • 인자로 PID를 입력 받는다. CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍
  • 9. • 인젝션할 코드이다. • 인자로 받은 주소를 캐스팅한다. • 미리 저장해 둔 함수 포인터(LoadLibraryA와 GetProcAddress) 와 문자열(user32.dll, MessageBoxA) 이용하여 user32.dll(MessageBoxA API가 저장된 DLL)을 열고, MessageBoxA 함수 주소를 얻는다. • 미리 저장해 둔 문자열을 이용하여 MessageBoxA함수에 인 자를 넣고 함수를 호출한다. • kernel32.dll은 대부분의 프로그램이 이용하고, kenel32.dll과 같은 시스템 라이브러리는 모든 프로세스가 같은 가상메모 리에 매핑하기 때문에 injector에서 주소를 구해 넣어도 괜찮 았다. • kernel32.dll을 이용하지 않는 dll도 있으니 code injection하기 전에 확인해야 한다. CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍
  • 10. • 본격적으로 코드를 인젝션하는 함수이다. • 일단 대상 프로세스에 넣을 구조체(쓰레드 인자로 이용될) 에 내용을 적는다. • 아래 구조체 모양을 참고해 보자. • pFunc은 함수 포인터(주소)가 들어갈 공간이다. • LoadLibraryA함수와 GetProcAddress함수의 주소를 구 해 넣었다. • 이들은 MessageBoxA를 호출하기 위해서 구한 것이 다. • szBuf는 문자열들이 들어갈 공간이다. • 이들은 LoadLibraryA의 인자로 줄 DLL 이름과 GetProcAddress의 인자로 줄 함수 이름, 그리고 MessageBoxA를 호출할 때 인자로 줄 문자열들의 이 름이다. • 다른 API를 이용하고 싶다면 그 API가 속한 DLL과 함수 CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍
  • 11. • 프로세스를 열고 방금 내용을 채워 넣은 구조체를 그대로 써 준 다. CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍
  • 12. • 그 다음 인젝션할 코드(함수)의 크기를 구하고, 그 크기만큼 메모 리를 확보하여 내용을 쓴다. • 인젝션할 코드는 이 함수 바로 다음에 올 것이다. CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍
  • 13. • 방금 넣은 코드의 주소는 pRemoteBuf[1]이고, 저장한 구조체의 주소는 pRemoteBuf[0]이 다. • 이들을 인자로 넣어 CreateRemoteThread함수를 호출한다. CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍
  • 14. •주의! • 컴파일은 반드시 Release mode로 한다. • Debug모드로 하면 추가적으로 생성되 는 코드가 있다.(추가적으로 불리는 함 수가 있다) • 따라서 Debug모드로 컴파일한 코드 를 인젝션하면 대상 프로그램에는 없 는 함수를 호출하기 때문에 에러가 나 고, 프로그램이 종료된다. CODE INJECTION – 원격 쓰레드를 이용 – 고급 언어로 프로그래 밍
  • 15. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 어셈블리어를 이용하여 인젝션할 코드를 생성하면 조금 더 자유로운 형태로 이용 가능하다. • 문자열(데이터)과 코드를 함께 넣을 수 있다. • 올리디버거로 간편하게 만들 수 있다. • 아무 EXE나 열고 빈 칸에 옆과 같은 내용을 쓰자 • 단, call 주소는 상황에 맞게 쓴다. • 또는 인라인 어셈블리를 이용해도 됨
  • 16. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 함수 프롤로그
  • 17. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 인자로 받은 struct 주소를 가져온다. • 이번에는 struct에 함수 주소(LoadLibraryA와 GetProcAddress)만 넣었다. • 이 주소를 하드코딩 하지 않는 이유는 ASLR이라는 기법 때문이다. • ASLR : Address Space Layout Randomization. • 시스템 DLL은 모든 프로그램이 같은 주소의 가상메모리에 올라온다. 하지만 재부팅하면 그 ‘같은 주소’가 될 주소가 바뀐다. 따라서 함수 주소는 매번 구해서 넣는다. • ESI에는 LoadLibraryA, ESI+4에는
  • 18. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • LoadLibraryA함수를 호출하는 코드이다. • 사용할 dll 문자열은 user32.dll이다.
  • 19. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • user32.dll 문자열을 스택에 저장한다. • 리틀 엔디안으로 • 문자열 뒷부분부터 거꾸로 • 맨 마지막에 push esp를 해서 문자열의 맨 처음 부분이 저장된 주소를 스택에 넣는다.
  • 20. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 순서와 리틀 엔디안에 주의한다. • 문자열 맨 끝에 null (0)이 들어가는 것에 주의한다.
  • 21. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • ESI(인자로 받은 주소)에 저장된 구조체의 첫 번째 멤버인 LoadLibraryA 함수 주소를 이용하여 함수를 호출한다.
  • 22. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • GetProcAddress함수를 호출하는 코드이다. • 사용할 함수 이름 문자열은 MessageBoxA이다. • GetProcAddress함수는 WINAPI 함수 호출 규약을 이용한다. • WINAPI 함수 규약은 stdcall 함수 호출 규약이다. • -> 함수 인자를 역순으로 push한다
  • 23. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 조금 전과 같은 방법으로, MessageBoxA 문자열을 스택에 저장하고, 주소를 넘긴다.
  • 24. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • LoadLibraryA함수의 반환 값인 user32.dll의 핸들을 push한다.
  • 25. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • ESI+4에 저장된, 구조체의 두 번째 값인 GetProcAddress함수를 호출한다.
  • 26. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 이제 MessageBoxA를 호출할 것이다. • 문자열은 2, 3번째 인자이다. • 단순한 push방법을 이용하기엔 골치 아프다. • push방법 말고 다른 방법을 이용해본다.
  • 27. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 먼저 uType에 해당하는 인자를 push한다.
  • 28. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 만드는 중에는 일단 call 00400000(뒤에 아무 숫자, 짧지 않은 길이의)를 입력해 둔다. • call을 하면 함수 call 바로 아래 주소(다음에 실행할 코드 주소)를 스택에 push한 뒤 점프한다. • 사실 여기서 사용하는 주소는 상대 주소이다. • 기계어에는 자신으로부터 얼마나 떨어진 곳으로 점프하라 라는 정보가 있다. • 올리 디버거가 알아서 상대 주소로 계산해서 기계어를 만들어 준다. • 어셈블리어로는 그냥 주소를 써 줘도 된다.
  • 29. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 상대 주소 계산법 JMP 100 EIP : 50 (EIP는 다음에 실행할 코드 주소를 가리킨다.) 100 <어셈블리어 표현> E9 B0 00 00 00 EIP : 50 (EIP는 다음에 실행할 코드 주소를 가리킨다.) 100 <기계어 표현> 시작주소 45 시작주소 45 • 00000050 + 000000Bo = 100 • 리틀엔디안으로 B0000000 • 주의 : 작은 바이트를 이용하는 short jmp라는 명령어도 있다. 일단은 그냥 점프를 이용하기 위해 4바이트의 주소를 넣자.
  • 30. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 상대 주소 계산법 EIP : 105 (EIP는 다음에 실행할 코드 주소를 가리킨다.) JMP 45 <어셈블리어 표현> <기계어 표현> 시작주소 100, 코드 크기 5바이트 시작주소 45 EIP : 105 (EIP는 다음에 실행할 코드 주소를 가리킨다.) E9 40 FF FF FF 시작주소 100, 코드 크기 5바이트 시작주소 45 • 105 – c0 = 45 • -c0 = 4바이트로 FFFFFF40 • 00000105 + FFFFFF40 = 45 • 리틀 엔디안으로 40FFFFFF
  • 31. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 수정하려는 줄을 클릭한 후 오른쪽키-binary- edit으로 출력하고자 하는 문자열을 입력한다. • 뒤에서 두 번째 인자인 메시지 박스 제목을 입력한다.
  • 32. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 맨 끝 null을 입력하는 것을 잊지 말자
  • 33. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 위의 call 목적지가 문자열 바로 다음을 가리키도록 수정한다.
  • 34. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 똑같은 방식으로, 인쇄하고자 하는 문자열을 넣는다.
  • 35. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • hWnd 인자로 0을 넣는다.
  • 36. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • MessageBoxA함수를 호출한다.
  • 37. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 함수 에필로그이다.
  • 38. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 파일을 저장하고 헥사 에디터로 연다. • 기계어를 얻기 위함이다.
  • 39. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 기계어를 복사해서 배열에 넣을 형태로 수정한다. • 메모장으로 노가다하든 에디터를 쓰든... • NotePad++같이 매크로를 이용할 수 있는 프로그램을 추천함 • 이것이 쉘코드 제작의 기본이다.
  • 40. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 이번 구조체에는 함수 주소만을 넣는다. • ThreadProc함수 대신 쉘코드를 넣는다.
  • 41. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 메인 함수이다.
  • 42. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 쓰레드 인자로 줄 구조체를 넣는 것은 똑같다. • 문자열은 더이상 넣지 않는다.
  • 43. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 함수 주소를 이용하여 사이즈를 구하던 것과는 달리, 단순히 쉘코드 배열의 사이즈를 구해 넣는다. • 쉘코드를 프로세스에 쓰고 쓰레드를 생성한다.
  • 44. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 성공!
  • 45. CODE INJECTION – 원격 쓰레드를 이용 – 어셈블리어를 이 용 • 쉘코드를 0xFF 식의 char 배열로 만들기보다는 위와 같이 스트링으로 쓰는게 더 정석적이다. • 코드상에서 char 배열 초기화를 제외한 부분은 달라진 것이 없다. • 취향 따라 쓴다..
  • 46. TIP • jmp를 다른 방식으로 표현한다면? • jmp 401000를 아래와 같이 표현할 수 있다. • push 401000 • ret • Call을 다른 방식으로 표현한다면? • push 다음 주소 • jmp 함수 주소 • 이렇게 같은 코드를 다른 방식으로 쓸 수도 있다.
  • 47. 인라인 패치 • 인라인 코드 패치, 인라인 패치 • 원하는 코드를 직접 수정하기 어려울 때 코드 케이브(Code cave)라고 하는 패치 코드를 삽입한 후 실행해 프 로그램을 패치시키는 기법 • 패킹 혹은 암호화 때문에 파일을 직접 수정하기 어려운 경우 많이 사용되는 기법 • 혹은 수정한 코드가 아래 코드를 침범하여 끼워 넣기 애매한 경우에도 이용할 수 있다. • 매번 프로세스 메모리의 코드를 패치하기 때문에 인라인 코드 패치라고 부른다. • 파일 자체에서 코드를 수정하는 것이 아니라 파일을 실행한 후 수정하는 것 • 복호화가 끝난 후 가는 OEP를 조작하여 끼워 넣은 코드로 이동하게 한다. 끼워 넣은 코드는 복호화 된 기존 코드에서 수정하고 싶은 부분을 수 정한다. • 파일 수정을 이용한 Code injection, 그리고 이를 이용한 기존 코드 패치 방법이다.
  • 49. 인라인 패치 일반적인 코드 패치 인라인 패치 대상 파일 파일 & 메모리 횟수 1번 파일에는 1번만 메모리는 실행될 때마다 방법 Direct (원하는 위치에 직접 패치) Indirect (코드 케이브를 미리 설치한 후 메모리에서 원하는 영역이 복 호화 되었을 때 패치)
  • 50. 인라인 패치 • 패치할 프로그램이다. • result가 3이 아닌 값을 출력하도록 해보자. • 패치의 효과를 체험할 수 있도록 UPX 패킹을 적용하자.
  • 51. 인라인 패치 • UPX의 디코딩 루틴을 지나 OEP로 점프하는 코드를 찾았다. • 이 주소는 앞으로 임의로 넣은 코드의 주소로 바꿀 것 • 임의로 넣은 코드는 디코딩 되어 메모리에 올라온 코드를 수정(패치)하는 일 을 한다.
  • 52. 인라인 패치 • 메인문이다. • printf를 호출하기 전을 수정하고자 한다. • 끼워 넣을 코드는 맨 밑 코드영역의 null padding 자리에 넣을 것이다. • 드래그한 코드는 jmp 코드로 수정할 것이다. • 정상 작동하기 위해서는 이 코드도 필요하므로 복사해 둔다 • 임의의 코드를 실행하면 드래그한 코드가 jmp 406AA0로 수정될 것(406AA0에는 printf 결과를 조작하기 위한 코드를 끼워 넣을 것)
  • 53. 인라인 패치 • 디코딩 루틴 이후 OEP로 점프하는 코드를 아래 추가한 코드로 점프하도록 변경 • 이 점프로 인해 실행되는 코드 : 기존 main 함수의 printf 전 코드를 jmp 코드로 변경하는 코드 • 바뀐 코드로 인해 아래쪽에 추가한 코드가 실행된다. • jmp코드로 수정하기 위해 지워진 코드를 넣어 정상적으로 작동하도록 한다. • 추가 하고싶은 코드를 넣어 임의로 변경한다 (이 경우, printf 인자를 조작하는 일) • 다시 기존 코드로 점프하여 실행을 이어간다.
  • 54. 인라인 패치 • 성공! • OEP를 수정할 때, 그 점프하는 코드마저 패킹이나 암호화가 적용 되어 있다면, 그 알고리즘을 알아내야 한다. • 그래야 패킹/암호화를 풀었을 때 제대로 점프할 수 있다. • 일부러 약간 돌아서 코드를 실행하게 했다. • 여건이 된다면 다시 아래로 jmp하지 않고 바로 코드만 수정해도Ok.
  • 55. 부록 • 참고 사이트
  • 56. 참고 사이트 • JMP 상대주소 • https://ko.wikipedia.org/wiki/JMP_(x86_%EB%AA%85%EB%A0%B9%EC%96%B4) • http://jyj850714.tistory.com/337 • 인라인 패치 • https://asecurity.so/2017/01/hooking-%ED%9B%84%ED%82%B9-code-path-inline-patch-%EC%BD%94%EB%93%9C- %ED%8C%A8%EC%B9%98-%EC%9D%B8%EB%9D%BC%EC%9D%B8-%ED%8C%A8%EC%B9%98/