1.정적분석

 

1. PEid 를 통해 무엇으로 만들어졌는지 확인

2. bintext를통해 메모장 정리

이런 식으로 정리되는데, 이것만 봐서는 잘 모른다.

 

2.동적분석

 

3. 직접 실행해서 실행하기 전과 후를 비교.

 

processMonitor 실행

SystemExplorer로 무엇이 변했는지 확인

 

몇분 있다가 컴퓨터가 꺼지면서 디스크가 파괴된 것을 확인.

이런식으로 점점 상태가 이상해진다.
컴터가 꺼지면서 부팅이안된다.

이렇게 프로세스 모니터를 통해 계속 분석을 반복하는 것이다.

이런식으로 Write File을 통해 디스크 용량을 지속적으로 사용한다.

 

3. 상세 분석

디버거 툴을 이용해 안에 내용을 살펴본다.

 

winhex를 통해 파괴된 디스크확인

전부 HASTATI로 변환되어있다.

 

'악성코드 분석' 카테고리의 다른 글

동적분석 툴  (0) 2019.06.11
AntiReversing  (0) 2019.06.05
패스워드 알아내기  (0) 2019.06.04
quiz를 통해 익숙해지기  (0) 2019.05.22
어셈블리어 로 test.cpp 해석  (0) 2019.05.21

 

동적 분석을 위해 쓰는 툴

 

1. Process Explorer

2. System Exeplorer

 

3. Process Monitor

 

PID로 전부 삭제하고 악성코드 실행
다 비운후, 실행하면 악성코드에 대한 것만 뜬다. 위에 빨간표시를 클릭하면 캡쳐를 뜨는 것이다.

 

프로세스 시작부터 끝부분을 분석

Process create 어디 위치에 생성되는지 확인

 

 

System32\kernel.exe파일 생
실제 된 것을 확인.

또다른 시스템 익스플로러로 보면

이렇게 생성된 것을 볼 수 있다.

 

스냅샷

동적분석을 하기 전에 스냅샷을 통해 현재 상태를 찍고,  동적 분석 후 변화된 상태를 스냅샷 찍어 무엇이 추가되고 변했는지 확인할 수 있다.

 

악성코드 실행후 무엇이 변했는지 보여준다.

 

 

'악성코드 분석' 카테고리의 다른 글

악성코드 분석법.  (0) 2019.06.12
AntiReversing  (0) 2019.06.05
패스워드 알아내기  (0) 2019.06.04
quiz를 통해 익숙해지기  (0) 2019.05.22
어셈블리어 로 test.cpp 해석  (0) 2019.05.21

Ctrl + N

현재 실행파일에서 사용하겠다고 말한 함수들의 이름을 볼 수있다.

 IsDebugger

원하는 문자를 입력해서 찾을 수 있다.

1. IsDebuggerPresent 를 검색해서 찾는다.

엔터를 누르면 그 함수위치를 볼 수있다.

즉 , 안티리버싱에 관한 함수를 알아내서 그 함수를 알고, 이름을 통해 그 분기를 찾아 가는 것이다.

 

 

이러한 안티리버싱에 관련된 함수를 구글에 검색해서 무슨 함수인지 알아본다.

https://docs.microsoft.com/en-us/windows/desktop/api/debugapi/nf-debugapi-isdebuggerpresent

 

IsDebuggerPresent function (debugapi.h)

Determines whether the calling process is being debugged by a user-mode debugger.

docs.microsoft.com

 

소스로 짜기위해서는 

_WIN32_WINNT 매크로를 0x0400 이상을 정의해야한다.

 

StdAfx.h에 #define _WIN32_WINNT 0x0501 을 정의

 

 

소스.

 

F5는디버거로 실행
Ctrl+ F5는 디버거없이 실행
JE를 JMP 변경해 우회시킬 수 있다. TEST JE는 거짓이면 점프, 참이면 실행이다.

 

JMP로 변경하면 if문으로 우회를 해 No Debugger가 출력된다.

 

 

 

함수안에 코드 분석

MOV EAX,DWORD PTR FS:[18]

FS의 값에 18을 더한 값을 EAX에 넣는다.

MOV EAX,DWORD PTR DS:[EAX+30]

위의 값에 30을 넣은 값이 EAX에 저장

MOVZX EAX,BYTE PTR DS:[EAX+2]

이 값으로 디버깅을 구별. 1이면 디버깅이고, 0이면 디버깅이 아니라고 판별
이렇게 작성하면 isdebugger가 검색해도 안나오기때문에 분석을 하나하나 다해야하기 때문에 힘들다.

__asm은 어셈블리어를 사용하게 해주는 함수이다.

 


ntqueryinformationprocess 함수

 

함수에 대한 설명

https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-ntqueryinformationprocess

 

NtQueryInformationProcess function (winternl.h)

Retrieves information about the specified process.

docs.microsoft.com

ntqueryinformationprocess를 사용하려면 LoadLibrary  GetProcAddress함수를 사용해야한다.

 

GetProcAddress function (libloaderapi.h)

Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).

docs.microsoft.com

 

LoadLibraryA function (libloaderapi.h)

Loads the specified module into the address space of the calling process.

docs.microsoft.com

이 함수는 디버거 포트를 통해 판단한다.

두개의 프로세스는 디버거 포트를 통해 실행된다.

 

ntqueryinformationprocess 

위의 함수에서 사용하는 GetProcAddress와 LoadLibrary를 통해 확인할 수 있다.

nt함수는 위의 2개함수를 사용한다.

 

LoadLibrary함수가 먼저실행되고, GetPorcAddress가 실행된다.

 

 

ntqueryinformationprocess는 바로 호출이 되지 않기 때문에 zwQueryinformationprocess먼저 호출하고 호출된다.

이함수의 인자값은 5개

-1은 자기자신 7번째 정보를 조회 실제데이터에 4바이트의 데이터 사이즈 0이니 필요없다.

0번부터 세는 것이다. 즉 , 7번째는 ProcessDebugPort

그냥 실행 시

0이면  디버깅을 사용하지 않고있다는 뜻이다.
디버깅 시 결과 값 FFFFFFFF로 디버깅포트가되어 안티디버깅이 실행된다.
즉, if문을 우회하면 된다. 
JE를 JMP로 변경해 우회하면 No Debugger Detected가 뜬다.

 

 


 

CheckRemoteDebuggerPresent 함수

https://docs.microsoft.com/en-us/windows/desktop/api/debugapi/nf-debugapi-checkremotedebuggerpresent

 

CheckRemoteDebuggerPresent function (debugapi.h)

Determines whether the specified process is being debugged.

docs.microsoft.com

 

매개변수가 2개인 함수이다.

지정된 프로세스가 디버깅되고 있는지 여부를 결정하는 함수이다.

 

함수가 성공하면(디버깅중이면) 반환값이 0이아니고, 실패하면 반환값이 0이다.

 

똑같이 Ctrl+ N을 통해 위의 함수를 찾는다.

위의 주소를 break를 걸고 그 주소로 찾아간다.

 

매개변수가 2개라 2개를 넣는다.

 

값이 1 즉, 0이 아니면 디버깅중이라 판단.

 

JE를 JMP로 변경해 흐름분기를 우회한다.


 

SetLastError

GetLastError

OutPutDebugString 

표시를 위해 문자열을 디버거에 보내는 함수

https://docs.microsoft.com/en-us/windows/desktop/api/debugapi/nf-debugapi-outputdebugstringw

 

OutputDebugStringW function (debugapi.h)

Sends a string to the debugger for display.

docs.microsoft.com

 

Ctrl + N 으로 OutPutString을 찾아 온다.

 

29A라는 값을 LastErr에 넣는다. 
OutPutDebugStringW 디버거로 문자열을 출력하라는 의미이다.
이런식으로 random이란 값을 출

GetLastError 는 LastError의 값을 가져온다. EAX로 가져옴

 

디버거가 있으면 에러가 일어나지 않고, 디버거가 없으면 에러가난다. 이것으로 판별.

LastError -> 마지막 에러를 캐치함. 즉 , 에러가 있으면 디버거가 없다 판단.

에러가 없으면 디버거가 있다판단.

 

에러가 있으면 Last Error에 있는 값과 EAX의 있는 값이 다르므로 디버깅이 없다판단

즉 , 같으면 디버깅이 있다고 판단하므로 안티디버깅 활성화

 

따라서 JNZ를 JMP로 변경해 우회


findWindow

 

활성화 중인 창에서 OllyDbg를 찾는다.

 

Ctrl + n 을통해 들어간다. 이번에는 2군데라 2군데 전부 break Point 를 건다.

활성 중인 Title을 찾는다. 내부 클래스에 대한 값을 찾는다.

 

 

OLLYDBG라는 창을 찾으면(0이 아니면) EAX에 값이 담겨 점프가 되어 OllyDBG Detected가 뜨고

창을 찾지 못하면 EAX값은 0이되어 점프를 하지 않아, Not Detected가 뜬다.

따라서 JNZ를 NOP 으로 변경하면된다.

NOP으로 변경

 

 

 


AnitQuiz1

 

디버깅을 실행시켜 Strat를 누르면, 메모장이 뜨면 성공

Findwindow를 찾아 JE를 JMP로 변경해 OllyDbg를 우회
이모양이 뜨지만 실행은 안됌.
찾다보니 이함수가 notepad.exe 와 연관된 것을 확인 JE를 NOP으로 변경해 점프가 되지않고 그대로 실행되게 설정. 

winexec는 응용프로그램을 실행시키는 함수.

https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-winexec

 

WinExec function (winbase.h)

Runs the specified application.

docs.microsoft.com

잘 실행된다
프로그램을 강제종료하는 명령어 외워두면 좋다.

-1 나 자신 

1E -> 30번 째 ProcessDebugObjectHandle을 의미

 

 

 

주소의 값이 다시 실행되는 곳에 브레이크포인트를 거는 방법이다. Hardware Break Point라 한다.

 

'악성코드 분석' 카테고리의 다른 글

악성코드 분석법.  (0) 2019.06.12
동적분석 툴  (0) 2019.06.11
패스워드 알아내기  (0) 2019.06.04
quiz를 통해 익숙해지기  (0) 2019.05.22
어셈블리어 로 test.cpp 해석  (0) 2019.05.21

CrackMe_1.exe
0.15MB

 

위의 파일을 디버깅툴로 실행시켜  사용자에게 입력값을 받아 실행하는 형태이기 때문에

 

텍스트 스트링으로만 모아서 입력 받는 값의 시작부분과 틀린부분 사이를 break point를 걸고 실행.

 

저 부분만 해석하면 된다.

 

이부분을 실행시키면 아래와같이 입력받는 값이 뜬다.

 

 

여기부분에서 실행이 멈춘다. 즉, 여기서 아무런 값을 입력하고 다음 문장으로 넘어가면

 

이부분을  통해 ECX에 J값이 들어간다.

 

이 콜문장을통해 
내가 넣은값과 s68h90i50n을 비교해 맞으면 Good jab 을 출력하게 한다.

따라서 답은 s68h90i50n

 

 

 

 

CrackMe_2.exe
0.15MB

위와 같이

 

위의 값을 입력해보면

ucancrack이라는 문자열이나온다

 

정답:

ucancrack

 

소스로 변환해보면

이렇게 된다.

 

CrackMe03.exe
0.01MB
이값을 변경

위의 exe파일을 소스로 변환하면

 

'악성코드 분석' 카테고리의 다른 글

동적분석 툴  (0) 2019.06.11
AntiReversing  (0) 2019.06.05
quiz를 통해 익숙해지기  (0) 2019.05.22
어셈블리어 로 test.cpp 해석  (0) 2019.05.21
Debugger사용법, 간단한test.exe파일을 통해 분석  (0) 2019.05.17

winmain을 어셈블리어에서 구별하는법

GetModuleHandle 밑에 CALL이 winmain이다.

 

1. 소스

 

2. 어셈블리어

MessageBOX부터 위로 코딩을 하면 된다.

 

 

 

퀴즈3.exe 파일의 코드 찾아내기

정답:

 

 

퀴즈4.exe 파일의 코드 찾아내기

 

 

 

MOV DWORD PTR SS:[EBP-A],EDX   //EDX의값 즉 공간을 다쓰고 6바이트가 남아서 6바이트를 0으로채움

MOV WORD PTR SS:[EBP-6],DX   // char str[24] = "AegisOne Security"; 

LEA EAX,DWORD PTR SS:[EBP-1C]  // EBP-1C는 배열의 첫주소이므로 배열명이 된다 따라서 str

MOV DWORD PTR SS:[EBP-4],EAX  // char * p = str;

 

 

MOVSX EDX,BYTE PTR DS:[ECX+6]   // 현재 ECX는 p이므로 p+6의 값을 저장     =*(p+6);

MOV DWORD PTR SS:[EBP-20],EDX   // int  n = *(p+6);

 

 

정답 :

 

 

 

퀴즈5

 

함수 파라미터들이 고정인 함수들이다.

strcpy(1,2);

memcpy(1,2,3);

strcmp(1,2);

strlen(1);

malloc(1);

free(1);

sprintf를 나타내고 있다. 총 24바이트중 int형인 4바이트만 사용 했으므로, 배열에 남은 20바이트를 지정한다.

PUSH가 3개인 것으로 보아, 값이 3개임을 알 수 있다.

func(&x, "%d", n);           이 형태를 보아 sprintf임이 유추가된다.

 

sprintf의 기본 양식은

sprintf(string, "%d", n ); 이다.

MOV DWORD PTR SS:[EBP-18],5    // int n = 5;

 

LEA EDX,DWORD PTR SS:[EBP-14] //배열의 시작주소를 나타낸다.

 

 

 

정답

 

 

 

퀴즈06

값 변경하기

 

이 값을 10으로 변경
저부분의 값을 변경하면 된다.
Selection 1개만 변경시 사용 

Selection 1개만 수정했을 시 사용

All modifications 여러개 수정했을 시 사용

 

수정한 후 copy all -> seve로 exe파일을 저장하면 완전히 수정이 된다.

 

 

값이 변한 것을 확인

 

소스분석

함수 첫 부분

PUSH

PUSH

PUSH

CALL 구조를 보아 sprint임을 유추

 

 

따라서 00401006의 값을 변경하면 값이 변한다.

 

 

 

 

조건문

 

 

조건문(if)

 

CMP 피연산자1 , 피연산자2  //1~2를 하여 양수, 음수,0을 판단 

 

CMP 피연산자1 에서 피연산자2를 빼고 양수면 피연산자1이 크고 음수면 피연산자2,  0이면 같다.

 

JE 같으면  JMP 가실행 

JNZ 서로 다르면 JMP가실행 

 

 

JGE 왼쪽이 크거나 같으면  JMP가 실행

JLE 왼쪽이 작거나 같으면  JMP가 실행

 

if(n<= 10)

JG 왼쪽이 크면 실행

 

if(n <= 10)

JL 왼쪽이 작으면 실행

 

 

 

 

 

if문이 참이되어 실행되면 JMP명령을통해 else부분을 뛰어넘긴다.

 

JMP문을 통해 0040103B로 온다.

 

 

 

 

 

 

and 연산자

 

 

 

 

or연산자

 

or연산자이기 때문에 그대로 연산자가 된다.

and와 다르게 jge가 참이면 push0 부분 즉 , 종속문장을 실행한다.

 

 

문제 02-1

 

아래 어셈블리어의 맞는 코드 작성

 

 

 

정답:

 

 

 

 

 

퀴즈 02-2

 

 

분석

 

MOV EAX,DWORD PTR SS:[EBP+10]  //arg 3   == lpCmdLine

EBP + 8   //arg 1

EBP + C   //arg 2

EBP + 10   //arg 3

 

따라서 lpCmdLine을 불러오면 된다.

 

Arg를 PUSH하고 들어갔으므로, EBP + * 은 arg를 의미

 

 

 

 CDQ는 나눗셈을 의미한다.  IDIV  몫과 나눗셈을 한번에 구한다.
TEST를 통해 EAX를 사용하고, EDX의 값은 MOVSX를 통해 다른 데이터로 덮어졌으므로, 몫의 값만 사용된 것을 알 수 있다.

TEST  참거짓을 비교하는 비교 연산자.

위의 문장을 통해 IDIV ECX의 값이 ch /13이 됨을 알 수 있다.

 

if의 종속문장 
else의 종속문장

 

JE  조건식 점프로 뛰는 범위 안에 JNZ가 겹쳐있으므로 or임을 유추한다.

JNZ는 앞에 문장이 참일 시 건너뛰는 용도로 사용되기 때문에 반대로 생각해야된다.

 

 

위의 어셈블에 대한 소스 정답.

0x49 - 0x34 + 0x41 = 0x56     0x56 == 'V'  즉 , 3번째 문자열에 V가 오는 값을 아규먼트로 등록해주면 success가 뜬다.

 

 

 

 

Switch case 문 유형 1.

 

 

위의 소스 어셈블리어

소스분석

2문장이 switch(n)을 의미 

 

case 0 , case1 , default를 의미 
위의 문장 JE에서 시작부분이 00401021로 가라 나타냈기 때문에 case0에 해당하는 문장임을 알 수 있다.

 

JE부분이 00401037로 가라 나타냈기 때문에 case1부분이다. JMP는 소스에서 break를 의미하기 때문에 전부 같은 주소를 가리킨다.

같은주소 == switch문의 끝

JMP부분이 0040104D로 가라했기 때문에 default부분이다.

 

 

스위치문은 이런형태로 나온다.

 

 

Switch case 문 유형 2.

Switch문이 4개 이상일 경우

어셈블리어 소스

 

소스 분석

4개이상의 case가 나올 시 위와 같이 단축되어 나온다. JA는 JG와 동일
default 부분이다. 즉 case의 마지막번호랑 비교해서 크다면 default로 가게 한다.
case부분이다. ECX*4는 고정, 뒤에 주소는 변함. 

즉 상수가 0일 경우  0*4 + 401097     //16진수계산

case 0 : 401097    

case 1 : 40109B

case 2 : 40109F

case 3 : 4010A3

 

위의 주소에 위치하는 데이터값의 번호에 관련된 곳에 case 시작부분이 있다.

 

 

퀴즈 2-3

소스분석

 

EBP+10 이므로 아규먼트 3번째값 lpCmdLine임을 알 수 있고,  JNZ이므로 0이 아니면 점프한다는 의미이기에 if(*lpCmdLine==0)이 된다.

 

if의 참인 부분 JNZ의 주소가 00401029를 가르키므로 그 전에 주소는 if문의 값이 된다.

OR EAX,FFFFFFFF  //  MOV EAX , -1 을 의미 

JMP의 주소가 함수 마지막을 가르키므로 return이 된다.

 

lpCmdLine[0] - 0x30은 값이 정수로 나올수 밖에 없으므로 int형이다.

 

n과 5를 XOR연산

 

 

CMP JE CMP JE 순을보아 Switch문임을 유추

 

case2 와 case4의 내용  JMP는 break;

 

default의 내용 

 

 

소스로 변환한 정답 

 

소스를 수정해서 success를 띄우는법

n^5 -> 4가 되는 값은 n == 1이다.

문자 '0'은 16진수로 0x30  10진수로 48이다. 따라서, lpCmdLine[0]의 값이 1이면 success가 출력된다.

 

 

 

 

2-4 퀴즈

 

어셈블리어

 

 

분석

MOV DWORD PTR SS:[EBP-4],EAX 문장을 통해  x라는 변수에 func(lpCmdLine)을 넣는다.

 

밑에 문장을 보아 switch(x)을 의미

 

case3개를 가진 switch문

40109F주소의 헥사값을 보면 저기에 적힌 주소들이 case문의 내용이다.

 

push가 1개인 것으로 보아, 인자가 1개인 함수이다. 

이제 여기서 추리를 한다.   lpCmdLine이니까 arument에 값을 하나한씩 넣어보면서 규칙을 찾아낸다.

 

1234를 넣어본다.
4라는 값이 반환 되었다.

 

123456을 입력해보았다.
6이라는 값이 반환되었다.

위의 규칙을 통해 문자열의 길이를 파악한다는 것을 알 수있다. 따라서 strlen()함수이다.

 

소스 정답

 

따라서 success를 띄우기 위해서는 x의 값이 4이상 이면 된다.

 

for문

벗어나는 키 F4

 

위의 소스에 관련된 어셈블리어

분석

이 형태면 무조건 for문이라 생각한다.

초기식 -> JMP하는 내용이 증감식이다.  for(i=1; ; i++)

JMP로 도착한 부분부터 다음 JMP까지가 조건식이다. for(i=1; i <11(0B); i++)

다음 JMP하는 내용이 종속문장이다.   sum = sum + i;  

위로 다시 올라간다. for문계속실행하는 JMP

분석해보면 아래와 같은 for문이 나온다.

for(i=1; i<11; i++)   
sum+=i;  

 

 

 

for 2번째 유형

 

어셈블리어

분석

char str[]="string";

for문의 형태이다.

 

MOV 

ADD

MOV 를통해  len++임을 알고

*(str+len) == str[len]

MOVSX 에서 EBP-8 + ECX인데, MOV연산자이기 때문에 안에 값을 가져온다 따라서 *(str+len)이된다.

JE기 때문에  *(str+len) 그대로 연산식이 된다.

JE와 JMP 사이에 값이 없으므로 안에 종속문장은 없다.

 

 

 

 

while문

 

 

 

if문이랑 비슷한데, 젤 마지막 JMP가 JMP했을 때 위로 올라가면 while 아래로 내려가면 else로 생각

 

continue는 종속문장 안에서 나온다. 

break                                           둘다 jmp로 나오니 혼동x

 

분석

 

 

2-5 퀴즈

 

소스정답

 

2-6 퀴즈

 

정답:

 

 

 

 

2-7

어셈코드

 

 

분석.

for문에 관련된 문장
while의 내용
아래 마지막 sucess문장

 

정답

 

success 띄우기

atoi()함수가 하는일 즉, 문자열 "123" 을 정수형 123으로 변환하는 코드이다.

즉 , 이 소스는 리버싱을 어렵게하기위해 0x30을 -> 0x2F로 변환해서 나타냈다.

 

따라서 마지막 if문을 보면 300-84=216 b의 값이 216이나오면된다.

 

0x30 -> 0x2F  는 1을 뺀 값이다.

216 -> 자릿수에 1씩 빼면 105가나온다.

105 -> 0x31 0x30 0x35 

따라서 argument 값에 105를 입력하면 정답이다.

 

 

 

 

'악성코드 분석' 카테고리의 다른 글

AntiReversing  (0) 2019.06.05
패스워드 알아내기  (0) 2019.06.04
어셈블리어 로 test.cpp 해석  (0) 2019.05.21
Debugger사용법, 간단한test.exe파일을 통해 분석  (0) 2019.05.17
기초지식(스택)  (0) 2019.05.16

1. test.cpp의 코드 내용

 

2. test.cpp의 어셈블리어

 

 

3. test.cpp문자형

4. 위의 코드 어셈블리어

 

MOV EAX,DWORD PTR DS:[405030]   //데이터의 값을 레지스터인 EAX에 저장

 

리틀앤디안 식으로 저장

 

MOV DWORD PTR SS:[EBP-8],EAX     //EAX레지스터 값을 스택에 저장

DWORD PTR SS:[EBP-8]가 배열의 시작주소

 

MOV ECX,DWORD PTR DS:[405034]  //405034데이터의 값을 ECX에 저장

MOV DWORD PTR SS:[EBP-4],ECX     //ECX레지스터의 값을 스택에 저장

char str1[] = "Reverse";

MOV EDX,DWORD PTR DS:[405038]  //405038데이터의 값을 EDX에 저장

MOV DWORD PTR SS:[EBP-18],EDX   //EDX레지스터의 값을 스택에 저장

MOV EAX,DWORD PTR DS:[40503C]    //40503C데이터의 값을 EAX에 저장

MOV DWORD PTR SS:[EBP-14],EAX      //EAX의 값을 스택에 저장

MOV ECX,DWORD PTR DS:[405040]    //405040데이터의 값을 ECX에 저장

MOV DWORD PTR SS:[EBP-10],ECX   //ECX의 값을 스택에 저장

char str2[] = "Enginerring";

XOR EDX,EDX       

 //null문자 생성       char str2[16] = "Engineering" 즉,[16]이라는  공간을 지정한후 남은 바이트를 null==0으로채운다.

MOV DWORD PTR SS:[EBP-C],EDX   //null문자를 스택에 저장

char str2[16] = "Engineering";

 

 

5. test.cpp 포인터

6. 어셈블리어

 

 

 

PUSH ESI

PUSH EDI

ESI와 EDI를 백업하기 위해 저장

 

 

MOV ESI,405030  //출발지 주소를 저장 즉  Reverse Engineering값을 넣기 위해 저장

 

LEA EDI,DWORD PTR SS:[EBP-1C]     //EBP-1C의 결과를 EDI에 저장

0012FF80 - 1C의 결과를 EDI에저장

REP 는 ECX의 값만큼 반복한다.

REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]

ES:[EDI] == 스택 주소 

DS:[ESI] == 데이터 주소

ESI가 가진 4바이트 데이터를 EDI에 저장     // 이작업을 5번동안 한다.

 

여기까지가 char str[]="Reverse Engineering";

XOR EAX,EAX

MOV DWORD PTR SS:[EBP-8],EAX

이 두문장을 통해서   첨자가 있는 배열임을 알게된다. 

여기까지가 char str[24]="Reverse Engineering";
값이 들어가있는 스택 형태

LEA ECX,DWORD PTR SS:[EBP-1C]     //EBP -1C 의값을 ECX에 저장

배열의 시작주소가 저장된다.

MOV DWORD PTR SS:[EBP-4],ECX        //ECX의 주소를 EBP-4에 저장

char * p = str;

 

MOV EDX,DWORD PTR SS:[EBP-4]      //EBP-4의 값을 EDX에 저장

MOVSX EAX,BYTE PTR DS:[EDX+5]   

//EDX값에 5를 더한다. 그값을 EAX에 저장  MOV연산자에 [ ]가 있을 시 포인터 이다.

//0012FF64 + 5  == 0012FF69  ==EAX == 0012FF69

*(p+5)

MOV DWORD PTR SS:[EBP-20],EAX        //0012FF69에 있는 데이터 1바이트를 저장

int n = *(p+5);

 

 

7. 구조체 코드

 

 

8. 어셈블리어

SUB ESP,0C    

지역변수가 12바이트이다.

 

MOV DWORD PTR SS:[EBP-C],0A   

int n1 =10;

MOV DWORD PTR SS:[EBP-8],14

int n2 = 20;

 

같은 공간을 쓰는 것을 의미하므로, 아래 문장을 통해 구조체인 것을 알 수있다. 

MOV BYTE PTR SS:[EBP-4],61

char ch1 = 0x61;

MOV BYTE PTR SS:[EBP-3],41

char ch2 = 0x41;

 

 

Debugger의 종류

 

GUI 환경

- OllyDBG

 

- Immunity Debugger

 파이썬에 사용할 때 유용

 OllyDBG와 IDA-PRO의 장점을 다 가져와 만든 디버거

 

- ghidra

제일 최신의 디버거

다른 디버거에 비해 무겁다.

 

- IDA-PRO

파이썬에 사용할 때 유용

유료버전

다른 디버거에 비해 무겁다.

 

- x64 dbg

64비트에서 전문적으로 사용되는 디버거

다른 디버거에 비해 무겁다.

 

CLI환경

- gdb

명령 창이라 보기 힘든 것을 보완하기 위해 PEDA 플러그인을 사용한다. 

- windbg

 

 

 

Immunity Debugger 환경

 

 

 

단축키

Ctrl + G   //주소의 위치로 이동 가능.

F7    //내부 들어가서 실행

F8   //내부 코드 전부 실행하고 넘어가기

F4   //선택한 지점 전까지 실행.

F9  //프로그램 끝까지 실행 

Ctrl + F2  //재시작

F2     // Software Breakpoint      

Breakpoint를 설정해 놓으면, 그 지점까지 실행이 된다. 따라서 Breakpoint를 걸고 재시작하고 다시 시작하면 Breakpoint까지 실행이 된다. (일종의 보험)

 

명령 수정

데이터 부분은 Space 바를 통해 수정 가능     

레지스터 부분은 Enter키를 통해 수정 가능 

스택 부분 Ctrl + e 키를 통해 수정 가능

 

데이터 부분에서 CALL 같은 함수의 안을 보고 싶을 때 Enter키 사용

CALL 부분에서 Enter입력,
이와 같이 함수에 들어갈 수있다. 나올땐 -키

 

 

entry point     

가장 먼저 실행할 명령어

값이 변경될 수 있다.

 

startup() 함수가 main 함수를 호출한다. 즉, 함수의 시작은 main이라 할 수 없다.

하지만 개발자가 시작하는 부분은 main함수부터이기 때문에 시작이라 말할 수도 있다.

 

 

startup 함수를 분석하는 것은 시간낭비가 될 수도 있다.

main() 함수부터가 개발자가 만든 코드이기 때문에 main부터 분석하는 것이 효율적이다.

 

winmain()이라는 함수가 시작되는 부분이다. 

F7을 통해 내부 함수로 들어간다. (winmain에 대한 함수 명령어들이다.)

F8을통해 한줄한줄씩 실행하면서 알아본다.

 

 

이 exe파일의 실행코드부분이다.

 

 

visual 6.0

실행파일 Release 상용목적  Debug 개발목적 디버깅 시 Release는 어셈블리 형태로 나오고 Debug는 자기 실행코드로 나타낸다.

 

Maximize Speed와 Minimize size 최적화 때문에 해석하기 어렵다. 

Default, Disable은 쉽게 해석된다. (리버싱 초심자들이 사용하기 편함.)

 

위와 같이 설정한 후, 

F7로 빌드

 

폴더에 아래와 같이 생성되면, test.exe파일을 디버거를 통해 실행.

오른쪽 네모 박스를 통해 개발자 코드 시작 부분을 알 수 있다.

 

이 3개의 PUSH가 의미하는 것은 아래의 그림이다.

메인 함수의 argument가 3개여서 envp , argv , argc 순으로 3개의 PUSH가 일어나고, 마지막으로 main CALL을 통해 호출이 되는 것이다.

 

kernel32 dll안에 Getversion함수라는 것을 알려주는 것이다.

 

CALL test.00401000의 의미

 

push return address(004010 D7) //리턴 값을 PUSH 하고

mov eip, 00401000 //00401000이라는 주소를 eip에 넣는다.

 

 

PUSH EBP    다음 지역변수 설정이 나온다.

 

위의 명령이 실행되면서 스택에 저장되는 구조이다.

variable(지역변수)
EBP
return address

ARG1

(argc)

ARG2

(argv)

ARG3

(envp)

 

3번째 부분 SUB ESP, 8을 하는 이유는 코드에서 int형을 2번 잡았기 때문에 지역변수 크기 8byte를 빼는 것이다.

즉, 이 함수가 사용할 총사이즈이다. 8  //char만 선언 시 1byte지만 내부 안에서는 4바이트로본다.

 

EBP의 값은 끝나기 전까지 변하지 않는다. 

 

 

시작 과 끝부분

 

아래 세 가지 명령은 함수가 시작할 때 스택의 공간을 확보하기 위해 나오는 코드이다.

PSH EBP  

 

MOV EBP, ESP 

 

SUB ESP,8 //이 문장은 있을 수도  없을 수도 있다.

 

이제 스택이 개발자의 코드값을 넣을 준비가 된 것이다.

 

함수가 끝나는 부분도 역시 아래 명령 3가지가 나온다.  시작 부분과 반대

RETN 

POP EIP 만 수행 

 

RETN 0C

POP EIP

ADD ESP,0C    2가지 수행   

 

즉 현재 exe의 코드는 분석해보면 RETN명령 후, ADD ESP, 0C를 하는데 RETN 0C가 나오면 한 번에 처리한다.

 

 

 

MOV ESP, EBP

POP EBP를 했을 때  

RETN            //CALL로 넣었던 주소가 나온다.

 

 

 

아래 함수 까지가 함수다.   이 함수는 썼던 명령들을 다시 되돌리는 작업이다.

 

 

 

 

개발자 코드 부분

 

PTR : 포인터

[ ] : 주소를 의미 

SS : Segment Selecter  Stack을 의미

DS : 주소라는 것을 의미한다.

 

Segment Selecter

현재 FS만 사용되고 있다.                          FS란 프로세스와 스레드의 정보를 볼 수 있다.   

 

MOV DWORD PTR SS:[EBP-8] , 0A     

//int a = 10;      EBP의 -가 붙으면 지역변수이다.   이제부터 EBP -8은 a라는 이름을 붙인다.

 

MOV DWORD PTR SS:[EBP-4] , 0A   

//int b = 10; 이제부터 EBP -4은 b라는 이름을 붙인다.

 

MOV EAX,DWORD PTR SS:[EBP-8]

// a의 값을 EAX의 저장   a

ADD EAX,DWORD PTR SS:[EBP-4]

// a+b의 값을 EAX의 저장 a+b

MOV DWORD PTR SS:[EBP-8],EAX

// a = a+b

XOR EAX,EAX

// return 0을 의미

 

 

'악성코드 분석' 카테고리의 다른 글

패스워드 알아내기  (0) 2019.06.04
quiz를 통해 익숙해지기  (0) 2019.05.22
어셈블리어 로 test.cpp 해석  (0) 2019.05.21
기초지식(스택)  (0) 2019.05.16
기초지식 (레지스터와 어셈블리어)  (1) 2019.05.15

Stack

 

프로그램 실행 시 사용되는 메모리 영역 중 하나이다.

런타임 시 생성.

Virtual memory(가상 메모리) 영역 중 하나이다.

MMU -> 주소를 관리해주는데 이러한 공간을 가상 메모리라 한다.

Thread 단위로 생성

시작 위치는 Vista 이상 랜덤  Xp는 고정이다.

 

용도

 

임시 데이터 백업

지역변수 저장

함수 매개변수 전달

함수 호출 관련 정보

OS에서 필요할 때 사용

 

 

Virtual memory(가상 메모리)의 구조

STACK
HEAP
DATA
TEXT/CODE

스레드 특징

하나의 프로그램 내에서 다수의 실행 흐름

완벽한 독립이 아닌 일정 부분 공유

공유로 인해 콘텍스트 스위칭(문맥 교환)이 줄어듬.

 

HEAP , DATA, TEXT는 공유로 사용되고 , STACK은 독립적으로 사용된다.

 

 

스택은 크게 Full Stack과 Descending Stack으로 나뉜다.

 

esp의 값은 현재주소에 최신 데이터 값이다.
값이 들어올 수록 주소가 낮아진다.

즉, ADD는 아래로 내려가고, SUB는 올라간다.

 

 

PUSH       

 

스택의 data를 저장

 

ESP를 감소시키고 16비트나 32비트 Source operand를 스택에 복사함.

 

POP       

ESP가 가리키는 곳의 저장된 내용을 Destination operand에 저장 후 ESP 값 증가.

 

EIP값을 변경하는 명령어

 

프로그램의 흐름을 변경할 수 있는 명령어

JMP

JMP 주소

주소를 EIP에 저장. -> MOV EIP, 주소 

 

바로 주소에 있는 코드가 실행된다.

 

CALL

함수의 처음 부분에 나온다.

CALL 주소 

 

PUSH Return Address  //EIP 백업

주소를 EIP에 저장. -> MOV EIP, 주소

 

RET

Linux : RET (Windows: RETN) 둘 다 같은 의미

함수의 끝부분에 나온다.

 

RETN

  POP EIP

 

RETN n

  POP EIP

  ADD ESP, n

 

 

LEAVE

Stack Frame을 릴리즈 하는 데 사용하는 명령어

ESP와 EBP를 프로시저가 호출될 때 할당된 값으로 복구

 

아래 코드와 동일

MOV ESP, EBP

POP EBP 

리눅스 컴파일용 gcc에서 사용

 

 

Application Binary Interface

컴파일 코드와 컴파일된 코드, 컴파일된 코드와 OS 간의 인터페이스를 정의 

호환성 유지가 목적

 

char -> 1byte   c 같은 아스키코드에서는 1byte

      -> 2byte   java같은 유니코드에서는 2byte

Alignment 단위

4byte를 단위로 스택에 저장되기 때문에 9 ,10 ,11 ,12 전부 스택에 3개의 공간을 차지한다.

 

Calling Convention 

함수 호출 목적

Object File Format

.c ,. cpp 컴파일 시 나오는. obj를 의미

 

linker 

.dll .lib //winodws

.so //linux

.obj를 .dll .lib와 같이 함수가 저장되어있는 라이브러리에 연결시켜줘서 .exe를 생성하게 해 준다.

 

Name Decoration

실제 변수의 이름은 알 수 없다.

함수의 이름은 존재한다.

 

 

Endianness

Multi Bytes를 저장하는 순서를 정의하고 있음

최대 4byte 단위로 적용함

 

intel -> Little Endian

 

 

계산

1234 + 5678       

기본적으로 계산할 때 뒤에서부터 계산하는 식을 사용하는데, 그렇기 때문에 little endian의 속도가 더 빠르다.

 

문자열은 Big Endian으로 저장된다.

 

 

 

 

 

버그 바운티

기업의 서비스나 제품 등의 취약점을 찾으면 포상급을 지급하는 제도

 

 

 

리버싱 (Assembly 언어) 2일

Anti Reversing      3일

분석방법(정적, 동적, 상세) 분석

 

 

IA32 Basices  (Intel Architecture) 기반

32비트 어셈블리   

 

 

레지스터 

 

전부 10을 가져오지만 비트 크기가 전부 다르다.

 

EAX = 10;   <- int eax =10;        // 32bit 전체 

AX = 10;     <- short ax = 10;    // 16bit 

AL = 10;    <- char al = 10;      // 8 bit

 

범용 레지스터

- ESP를 제외하고 나머지 레지스터들은 아무 값이나 저장.

 

EAX   

- 종료되는 상황에 값이 있으면 return 값을 의미한다.

ex) MOV EAX 10   -> Return 10

 

EDX

 - MUL, DIV(곱셈, 나눗셈) 값 저장 

ECX

 - 평소에는 그냥 계산 값

 - rep 명령이 있으면 반복 횟수로 변경

 

ESI   

- 출발지 주소 저장

EDI

- 목적지 주소 저장

ESP

- 스택의 주소만 저장.

- 제일 마지막에 들어온 데이터의 주소를 저장.

- 즉 , 스택의 최신 데이터를 가리킨다.

EBP

- 스택 프레임의 처음(기준) 지점의 주소를 저장.

- main() 함수가 실행되면 가장 처음 부분의 주소

- main() 함수가 있고 function() 함수가 실행되면 function()의 처음 주소.

EIP

- 다음 실행할 명령의 주소

 

 

flag 

- 상태 코드

연산 결과가 0이면

zero flag=0

연산 결과가 0이 아니면

zero flag=1

 

Break Point

- 멈춰야 되는 주소

software와 hardware로 나뉨. 

 

software 

디버거가 break Point를 지원

실행하다가 원하는 주소에서 멈출 수 있음.

 

Hardware

 - 이 메모리의 데이터를 읽거나 쓰거나 실행하면 멈추는 것.

 - 시작 값과 결괏값을 알 때 일련의 연산과정을 확인할 수 있음 (암호학 쪽에서 사용)

 

 

 

어셈블리어

 

MOV

데이터 복사

ex)

eax = 10;   // MOV EAX, 10   

10의 값을 EAX로 복사한다. 

MOV 10, 20 //이러한 형태는 되지 않는다.

 

eXtention 확장이라는 의미

 

 

MOVZX

- 부호가 없는 정수에만 사용 //unsignend  부호 없는 정수 표현

ex)

 char 1byte  int 4byte

unsignend  int eax;

unsignend  char al =10;     

eax = al;   //작은 공간 -> 큰 공간으로 복사가 이뤄질 때 사용

MOVZX EAX , AX //부호 없는 정수 AX가 EAX에 들어갔다 해석.

부호없는 정수기 때문에 작은비트에서 큰비트로 복사 됐을 시, 부족한 비트는 0으로 채워짐.

 

MOVSX

- 부호가 있는 정수에만 사용

ex)

char 1byte  int 4byte

int eax;

char al =10;     

eax = al;   //작은 공간 -> 큰 공간으로 복사가 이뤄질 때 사용

 

MOVZX EAX , AX //부호 있는 정수 AX가 EAX에 들어갔다 해석.

부호가 있는 정수기 때문에 작은비트에서 큰비트로 복사 됐을 시, 부족한 비트는 1로 채워짐.

 

 

MOVS S(String)

- 문자열 복사

- 피연산자 개수가  0~2개이다.

- 다른 MOV 연산자들은 2개로 고정.

- 1,2,4 byte 씩 가능하다 64bit 시, 8byte도 가능.

 

- 문자열을 복사할 시, 알아서 주소를 변경해 복사를 해줌.

 

  방향 플래그(DF)가 1 이면 ESI와 EDI는 감소,

  방향 플래그(DF)가 0 이면 ESI와 EDI는 증가  //대부분이 증가한다.

 

 

MOVS 가 나올 때 무조건 크기를 명시해서 나옴. 아래와 같이 표시됨.

 

  BYTE == 1byte == B

  WORD == 2byte == W

  DWORD == 4byte == D

  QWORD == 8byte == Q  

 

MOVS     //피연산자가 없을 경우 , 크기에 따라 MOVSB  MOVSD MOVSW 이런 식으로 나타냄.

MOVSB // ESI의 1byte를  EDI로 옮긴다.

MOVSD // ESI의 4byte를  EDI로 옮긴다.

 

 

 

ESI  //출발지

EDI //도착지

MOVS EDI , ESI라는 연산 식이 고정이다. 따라서, 아래 연산 식이 전부 같은 의미.

MOVS EDI , ESI  

MOVS     //피연산자가 없을 경우 , 크기에 따라 MOVSB  MOVSD MOVSW 이런 식으로 나타냄.

MOVS EDI

MOVS ESI 

간단히 정리하면 , ESI의 값을 EDI로 복사할 거야 라는 의미이다.

 

ADD

더하기 연산자 

 

MOV EAX, 1

MOV EBX, 2

ADD EAX, EBX or ADD EAX, 2      

ADD 1, 2 //이런식의 문법은 오류다.

 

eax=1;

ebx=2;

eax+=ebx;

 

SUB

빼기 연산자

 

MOV EAX, 1

MOV EBX, 2

SUB EAX, EBX      

SUB 1, 2 //이런식의 문법은 오류다.

 

eax=1;

ebx=2;

eax-=ebx;

 

MUL

부호 없는 곱하기 연산자

- unsignend을 사용해야 보임.

- 결과는 EDX:EAX에 저장

 

IMUL

부호 있는 곱하기 연산자

 

- 피연산자의 수가 3개 까지 표현 가능

- 결과는 EDX:EAX에 저장

- 결괏값을 초기화할 때 양수면 0으로 초기화 음수면 1로 초기화

IMUL ECX -> //IMUL (EAX), ECX를 표현

IMUL EAX, EBX, ECX  //EBX * ECX의 값을 EAX의 저장

 

 

DIV

부호 없는 나누기 연산자

- unsignend을 사용해야 보임.

- 몫 : EAX

- 나머지 : EDX

 

IDIV

부호 있는 나누기 연산자

- 몫 : EAX

- 나머지 : EDX

- 나눗셈 수행 전 CBW, CWD, CDQ를 통해 부호 비트를 확장

- 음수면 1로 채우고 양수면 0으로 채운다.

 

 CBW 1byte 끼리 나누기

 CWD 2byte 끼리 나누기

 CDQ 2byte와 4byte를 나눌 때 큰 바이트를 따라감.

 

INC(++)

증가 연산자

값이 1씩 증가

 

DEC(--)

감소 연산자

값이 1씩 감소

 

AND 

둘다 참일 때만 참

 

MOV EAX, 59

MOV EBX, 15

AND EAX, EBX   //59와 15를 AND연산하여라.

 

eax = 59;

ebx = 15;

eax &= ebx;

 

OR

하나라도 참이면 참

 

MOV EAX, 59

MOV EBX, 15

OR EAX, EBX   //59와 15를 OR 연산하여라.

 

eax = 59;

ebx = 15;

eax |= ebx;

 

XOR

 같으면 0 다르면 1

 패리티 점검에 사용

 x^y=z ==  y^z=x

 

0을 선언할 때, 

MOV EBX, 0 이러한 식보다

XOR EBX, EBX   이러한 식이 더 유용하다. cpu가 논리연산을 선호하기 때문이다.

 

 

MOV EAX, 59

MOV EBX, 15

XOR EAX, EBX   //59와 15를 OR 연산하여라.

 

eax = 59;

ebx = 15;

eax ^= ebx;

 

SHL

연산자의 비트를 왼쪽으로 이동 곱셈

1의비트를 잃어버리면 곱셈의 규칙이 깨짐

 

MOV d1, 5

sh1 d1, 1

 

SHR

연산자의 비트를 오른쪽으로 이동 나눗셈 

1의비트를 잃어버리면 나눗셈의 규칙이 깨짐

 

MOV d1, 32

shr d1, 1

 

LEA

 

두번 째 Operand의 주소를 계산하고 첫번째 Operand에 저장

 

 

MOV 에서의 [ ]는 포인터 연산자이다.  즉, 

MOV eax, [edx+4] 는 edx+4의 주소를 eax의 저장한다라는 의미이다.

eax = *(edx+4);

LEA eax, [edx+4]   //edx+4의 결과를 eax의 저장

eax = edx+4;

 

 

SCAS (SCAn String)

EAX에 저장되어 있는 값과 ES:EDI가 가리키는 곳(주소) 에 저장되어 있는 값을 비교

SCASB  byte를 의미

 

 

 

STOS(STOre String)

EAX에 저장 되어 있는 값을 ES:EDI가 가리키는 곳에 값을 저장 

STOSB byte를 의미

 

 

 

 

CMP EAX, EBX  //EAX가 더크면 조건문 실행        if문 같은 제어문이 나올 때 나옴.

JG 0x1000

 

CMP, TEST, SCAS  //비교 연산자 대부분 JUMP와 연관되어 나옴.

JG 0x1000

JL

JE

JZ

JNZ

 

 

 

REP

ECX레지스터의 저장된 값 만큼  해당 명령어를 반복 실행, 접두사 형식으로 사용됨

 

'악성코드 분석' 카테고리의 다른 글

패스워드 알아내기  (0) 2019.06.04
quiz를 통해 익숙해지기  (0) 2019.05.22
어셈블리어 로 test.cpp 해석  (0) 2019.05.21
Debugger사용법, 간단한test.exe파일을 통해 분석  (0) 2019.05.17
기초지식(스택)  (0) 2019.05.16

+ Recent posts