앙 규문띠 

 

 

abex' crack me 2

오늘은 !!! 금요일이라 뭔가 바빴다...

 

그래도 리버싱 공부는 해야지 앞에 해답을 설명하고 뒤에 [나뭇잎 버전]으로 자세하게 공부해보겠다

 

abex' crack me 2... (언제 고수되죠?) 시작!

 

먼저 crack me 2.exe를 무작정 실행 해보았다.

 

abexcm2-voiees.exe
0.02MB

띠용!

 

Name이랑 Serial을 입력하란다. 그래서 입력해보았더니,

 

역시나 시리얼이 틀렸다고 나온다. 

아무생각없이 디버깅을 해야되겠지 ?

 

이 넓은 공간에서 무엇을 해야한단 말인가

 

일단 내가 생각했던건 먼저 메세지박스가 뜨는부분이 있을테고 거기에 위의 오류메세지인 Nope ~ 어쩌구가 있을것이라고 생각했다. -> 고렇다면... nope를 검색해보자!

 

나왔다! 따블클릭으로 들어가보자!

그랬더니 다음과 같이 해당 opcode로 이동되었다.

 

여기서 생각해봐야 할 부분이 오류를 출력하는 부분이 있다면 True를 출력하는 부분도 있을거 같다.

마우스 휠을 올려보자.

 

시리얼이 풀렸을 때의 문구가 나오는 부분을 찾게되었다!

여기서 주목해야할 부분은 위쪽 부분이다.

 

위의 부분에서 JE 조건 분기문이 생긴다 조건값에 따라서 점프하거나 바로밑으로 실행된다.

즉 조건 분기문의 위쪽부분에서 시리얼 넘버의 참거짓이 판별되는 무언가(?)가 있을것 같은 예감이..

 

그리하여 403329 call부분에 BP를 걸고 프로그램을 다시 실행시킨다. (call을 실행해보면 입력창이 뜬다.)

이후에 name과 serial을 입력한다.

 

그 다음[Check]를 누르고 스택값을 확인한다.

 

0018F42C 메...모

0018F42C의 주소로 이동하여 스택값을 확인해볼까?

 

내가 입력했던 abcd 패스워드가 그대로 스택으로 들어간걸 확인할 수 있다.

그 위에 시리얼 넘버로 추정되는 CFDDD9D1 가 문제의 해답인 것 같다.

다시 프로그램을 실행하고.... 해당 시리얼을 넣으면 !

 

키야 ~~~ 취한다!

crack me 2번 클리어!

 

나뭇잎 버전... 작성하자... 꼮..

[바로가기]

 

'Reversing' 카테고리의 다른 글

PE File(1)  (0) 2019.04.02
리버싱 5  (0) 2019.04.02
리버싱 3 Stackframe  (0) 2019.03.28
리버싱 2  (0) 2019.03.27
리버싱 1  (1) 2019.03.26

오늘은 스택 프레임에 대해서 알아보겠다!

 

스택 프레임

  스택 프레임이란 ESP가 아닌 EBP 레지스터를 사용해서 스택 내의 변수, 파라미터, 복귀 주소에 접근하는 기법!

(어제 스택의 구조 공부했다능 ㅎㅎ 스택의 ESP값은 유동적인 놈이기 때문에 EBP값을 이용해서 정확한 위치를 참고할 수 있게 된다!)

 

쉽게

- 스택 프레임 : 어떠한 함수가 호출 되었을 때 그 함수가 가지는 공간의 구조!

 

스택 프레임의 구조는 다음과 같습니더

 

PUSH	EBP			: 함수의 시작! (EBP값을 스택에 저장)
MOV		EBP, ESP	: ESP값을 EBP에 저장

...					: 함수본체, 여기서 ESP가 마구잡이로 변경되어도 EBP값에 영향을 미치지않음

MOV		ESP, EBP	: ESP값 복원(함수 시작했을 때 값으로 복원)
POP		EBP			: EBP값 복원
RET					: 함수 종료

간단히 요약

- 함수 호출 시 EBP값을 스택에 저장해놓고, 마구잡이로 쓰다가 함수 끝날 때에~쯤 되면 다시 복원시키고 함수를 종료함

 

자! 실습하자 StackFrame.exe 로 실습한다

 

StackFrame.exe
0.05MB

StackFrame의 코드는 다음과같다.

 

Stack Frame.cpp

#include "stdio.h"

long add(long a, long b)
{
	long x = a, y = b;
    return (x + y);
}

int main(int argc, char* argv[])
{
	long a = 1, b = 2;
    printf("%d\n", add(a,b));
    return 0;
 }

그냥 더하는 프로그램이다 ㅋㅋ

 

그럼 리버싱 핵심원리를 따라서 ㄱㄱ

 

x32dbg로 StackFrame.exe 파일을 열고 401000 주소로 간다.

 

이후에 401020에 [F2]를 걸고 실행해보면서 스택의 변화를 살펴보자!!!

 

401020은 main()입니다. 실행을 시키면 바로 스택 프레임을 생성합니다.

PUSH는 스택을 집어넣을 때 사용, EBP값을 스택에 집어넣는 행위 입니다. main()에서 EBP가 이전에 가지고 있던 값을 스택에 백업!

보세요 스택에 들어가부렸쥬?

그다음 줄을 실행합니다.

MOV 명령은 데이터를 옮기는 명령. ESP값을 EBP에 옮겨라! 이 명령 이후에는 EBP와 ESP값이 같아지는걸 확인할 수 있습니다.

ㄹㅇ 같아짐

그리고 이렇게 같아진 EBP값은 main()함수가 끝날 때까지 고정됩니다. (401020,401021의 실행을 통해 스택프레임 생성!)

 

* EBP와 ESP가 18FF40으로 같아졌고, 18FF40에는 main()함수가 시작할 때 EBP가 가지고 있던 초기값인 18FF88을 저장하게 됨

다음으로 main()안의 로컬 변수 (a,b)를 위한 공간을 만들예정 입니다

long a = 1, b = 2;

그 다음줄을 실행합니다

SUB는 알다시피 빼기 명령어 입니다 ESP에서 8을 빼라는 소리인데 a와 b가 long 타입이므로 각각 4바이트씩 크기를 가지기 때문에 8만큼의 공간을 확보합니다. 

18FF40 - 8 = 18FF38

다음 코드 입니다.

[EBP-4]에는 1을 넣고, [EBP-8]에는 2를 넣어라는 의미입니다. 각각 a,b를 의미하게 되겠습니다.

여기까지 실행한 후에 스택 상태를 확인!

18FF40 -4 = 18FF3C // 18FF40 - 8 = 18FF38

이제 덧셈을 해주는 add()함수로 가봅시다.

main()에서 add함수를 호출하는데 전형적인 함수호출의 모습이라고 합니다.

call 401000에서 401000이 바로 add()입니다. 위에서 봤다시피 add() 함수는 a,b를 파라미터로 받는데

[EBP-8], [EBP-4]의 b,a 역순으로 스택에 저장된다는 것이 중요. b가 먼저 스택에 드러가고 변수 a가 나중에 들어갑니다.

 

여기까지 실행한 후의 스택의 모습

main은 a,b add함수 호출은 b,a 재밌쥬?

이후에 call을통해 add()함수가 실행되고 다시 복귀주소를 통해 돌아와야 합니다.

바로밑의 401041이 바로 add()함수의 복귀 주소입니다. call 실행 후 스택의 모습은 다음과 같습니다.

401041로 복귀되는 모습

main() 함수가 스택프레임을 생성했듯이, add()함수도 스택 프레임을 따로 생성합니다.

코드는 main() 함수와 똑같습니다.

 

401000 ~ 40100F 까지의 그것은 main()과 같기때문에 따로 설명X

직접 실행해보면서 레지스터와 스택의 변화를 몸소 체험해보자!

 

이후에 마지막 ADD연산을 수행합니다. ADD 연산은 아래 코드에 해당하는 내용입니다.

return (x + y);

401012 번째 줄입니다.

변수 x의 값(EBP-8)을 EAX에 넣습니다. x=1

 

변수 y의 값(EBP-4)를 EAX에 더합니다.

 

1+2 =3 이므로 EAX의 값이 3이 되는것을 EAX 레지스터를 통해 확인해봅니다.

3이쥬 ? ㅎㅎ

이제 add() 함수의 스택 프레임을 해제합니다.

최초의 MOV EBP, ESP의 역으로써 원래대로 ESP를 복원하는 스택프레임 해제!

현재 EBP값을 ESP에 대입함

add() 함수가 시작되면서 백업했던 EBP값을 원래대로 복원 (401000과 대응되는 명령어)

add() EBP 복원=> add() 스택프레임 해제 완료

* 함수 호출전의 스택 상태로 완벽히 돌아오게 됨. (Stack BOF 기법에 취약할 수 있음)

이렇게 스택프레임 해제를 완료하면 add() 함수 호출전과 스택이 완전히 같다.

이제 main()함수로 돌아왔다. 다음 코드를 봅시다.

갑자기 ESP에 8을 더한다. 이유는 이러하다.

add() 함수에 넘겨준 파라미터 a,b의 공간이 이제 필요없기 때문에 회수해야한다.

=> add() 함수가 완전히 종료되었기 때문에 스택을 정리하는 과정ㅋ

정리 된거 보이쥬?

이제 printf 함수를 호출 해야겠죠?

printf("%d\n", add(a,b));

printf() 함수 호출 코드입니다.

401044 주소의 EAX에는 아까 add() 함수에서 저장된 EAX 값 3이 들어있습니다.

그렇네요 EAX=3

그리고 위의 40104A에서 call의 401067은 Visual C++에서 생성한 printf() 함수입니다.

(printf() 함수는 복잡하기 때문에 알아서 찾아보셈 ㅎㅎ 똑같이 파라미터 2개쓰며 크기는 8바이트를 사용!)

 

이후에 printf() 함수 호출 후 스택이 정리되었기 때문에 ADD로 함수 파라미터를 제거해줍니다 :3

 

그 다음으로 main() 함수의 리턴값(0)을 셋팅해줍니다.

return 0;

같은 값끼리 연산시켜 0을 셋팅하네요?

이제 main() 함수를 종료하면서 스택프레임을 해제합니다.

마찬가지로 초기의 행위의 역순으로 수행합니다.

위의 두 명령으로 인해서 main() 함수의 스택프레임마저 해제되었습니다. 

스택의 상태를 확인해보면 제일 처음의 스택모습과 완전히 동일한 모습입니다.

스택의 동작 이해 완료!

 

'Reversing' 카테고리의 다른 글

PE File(1)  (0) 2019.04.02
리버싱 5  (0) 2019.04.02
리버싱 4  (0) 2019.03.29
리버싱 2  (0) 2019.03.27
리버싱 1  (1) 2019.03.26

바이트 오더링(Byte Ordering)

- 바이트 오더링 : 데이터의 저장하는 방식을 말함.

CPU 벤더사에 따라 바이트 단위로 데이터를 받아들이고 메모리에 적재하는 방식이 다르다고 함.

방식에는 대표적으로 2가지가 있는데 '빅 엔디언' 방식과 '리틀 엔디언' 방식임


빅 엔디언 : 네트워크 프로토콜에 사용

리틀 엔디언 : 인텔 x86 CPU 사용(window계열 리버서들이 리틀 엔디언에 대해서 알아야하는 이유)


핵심 : 리틀 엔디언은 역순으로 적재함

[엔디언 위키 바로가기]


리틀 엔디언 확인


다음의 코드를 통해서 리틀 엔디언의 개념을 알아보자!



LittleEndian.exe


LittleEndian.exe를 x32dbg를 통해서 open 해서 디버깅을 시작할 예정이다.


어제 해봤던 것처럼 F9를 눌르면 EntryPoint에서 부터 디버깅을 시작하면 된다.


시작!!



지난 시간 복습겸 main함수를 찾아보도록 한다 F8을 꾹 눌르면



401000을 호출하는 부분에서 MessageBox가 뜨게된다. 그렇다면 call함수 내부로 들어가보자 [Ctrl+F2]로 프로그램을 다시시작하고... 

[Ctrl+G]를통해 401000주소로 이동한다.

그랬더니


[짝짝짝! 여기가 main()의 내부이다. 소스코드의 byte, word, dword 가 보인다]


Goto를 이용해 byte의 주소인 413780의 헥사 덤프로 진입해보자.



[변수 b,w,dw의 값들이 '리틀 엔디언' 방식으로 저장되어 있는거 보인다 보여!]



CPU 범용 레지스터(EAX,EBX,ECX,EDX,EBP,ESI,EDI,ESP)

- 이 부분은 각각 레지스터의 용도와 쓰임새 부분을 짚고 넘어가야 될 것 같아서 정리해서 남겨두었다!


[CPU 범용 레지스터 정리]


이 후에 다른 레지스터들에 대해서도 간략히 알아보자!


세그먼트 레지스터

- 'Segment'는 메모리의 영역에 주소,범위 혹은 접근 권한 등을 부여해서 메모리를 보호하는 기법이다. 


세그먼트는 페이징 기법과 함께 가상 메모리를 실제 물리 메모리로 변경할때 사용되는데 이 세그먼트 메모리는 SDT라는 곳에 기술 되어있고, 


SDT의 index를 가지고 있는것이 '세그먼트 레지스터' 라고 할 수 있겠따 (어렵당;)


2바이트(16bit) 크기의 6개의 레지스터로 구분되어 있다.(CS, SS, DS, ES, FS, GS)


CS 코드 세그먼트, SS 스택 세그먼트, DS 데이터 세그먼트 이정도만 숙지하고 넘어가자!


플래그 레지스터

- 플래그 레지스터의 이름은 EFLAGS이며 32비트의 크기입니다.


여기에서는 깊게 이해하지말고 3가지 플래그만 알아두고 넘어가도록 합시다



[ZF : Zero Flag, OF : Overflow Flag, CF : Carry Flag 각각의 플래그들은 참일때 1로 셋팅 됩니다!]





'Reversing' 카테고리의 다른 글

PE File(1)  (0) 2019.04.02
리버싱 5  (0) 2019.04.02
리버싱 4  (0) 2019.03.29
리버싱 3 Stackframe  (0) 2019.03.28
리버싱 1  (1) 2019.03.26

리버싱 입문 1


리버싱 입문자로써 가장 많이 추천하는 책인 '리버싱 핵심 원리'를 통해서 학습을 진행할 것이다!



리버싱계의 성경(?)이라고 하는데 넘 기대댄다

우선 이 책은 출판된지 굉장히 오래되었다. 그래서 ollydbg로 시작을 하는데 나는 x32dbg로 실습을 진행하겠다. (64비트가 대세니까 x64쓸거임 ㅎ)


우선 다음의 hello world!로 리버싱을 입문하였다.


HelloWorld.exe


다음과 같이 간단한 소스이다.


x32dbg에 넣은 후에 [F9]키를 한번 누르면 EntryPoint(를 찾아준다



Entry Point는 PE파일의 실행 시작 주소를 가르킨다 ASLR이라고 프로그램이 시작될때마다 주소가 바뀌게하는 안티 디버깅 기법도 알아두면 좋을거 같다.


아무튼 내가 오늘 해볼일은 디버거의 여러가지 단축키를 손에 익을(?)떄까지 사용해볼것이다.


F7, F8, F9, Ctrl+F2 등을 이용해서 OPcode를 실행시켜보면서 main함수를 찾아볼 예정이다. (단축키설명 생략)


call인자는 함수의 호출을 뜻한다. call 부분에서 F7을 누르면 해당 함수로 진입한다.


Ctrl+F9는 함수에 진입한 후 빠져나올 때 사용하는데 return값으로 커서가 이동하니 F7을 한번만 더눌르면 함수 탈출에 성공하고 다음 opcode로 넘어간다.

(모르면 단축키막 눌러보자!)


어쨌든 우리는 MessageBox API를 호출하는 부분이 main함수의 안쪽이기 때문에 MessageBox관련 내용이 나올때까지 디버깅을 실시한다.

(F7쓰지말고 F8만... 함수한번들어가면 나오기힘듬...)



그러다보면 <&GetCommandLineA>이런거도 보게되고 좀더 디버깅을 하다보면



다음과 같이 내가찾던 MessageBox API를 호출하는 코드를 확인할 수 있다.

그래서 해당함수 안으로 F7로 진입해보면

짜잔! main함수 안으로 진입하였다!! 밑의 push 두개는 각각 Title과 data를 스택에 주소를 할당하는 과정인것 같다.





그 이후로 여러가지 디버거의 명령어들과 단축키들에 대해서 실습해본다.

대부분 올리디버거랑 단축키는 비슷했다.


[Ctrl+G]


[F2 - BreakPoint]


[; - 주석] (주석이랑 레이블 동시에 안되더라; 올리랑 약간다른듯)

[: - 레이블]



주석과 레이블 확인 (Ctrl+Alt+C,L)


레이블은 basecamp를 차릴때 많이 사용하는 듯하다 저자 이승원님은 이렇게 basecamp를 차린다고 합니다.



다음으로 위와같이 한줄씩 디버깅하는게 아니라 함수가 호출된 부분을 검색할 수있는 쉬운 방법도 있다고 한다.

xdb같은경우에는 올리랑 조금 다르더라.

'우클릭 - 다음을 찾기 - 모든 모듈 - 모듈간 호출' 에서 message를 검색하고 떠블클릭하면 해당 opcode로 이동!

- 이렇게하면 좀더 빨리 main안으로 진입할 수 있었을텐데...



나머지 memory map이나 name같은 것들은 책을보면서 실습해보면 되겠다.


이제는 프로그램을 패치 시켜보겠다. Hello world를 Hello reversing으로... ㅋㅋ


첫번째 방법으로 문자열 자체를 패치시키면 된다.

(hello world가 들어간 12692A4를 헥사덤프에서 이동후에 데이터값을 변조)




오오오오!! ㅋㅋㅋㅋ (문자열 수정의 제약조건 - 기존 문자열 버퍼 크기 이상의 문자열을 입력하긴 어렵다)



두번째방법! 메모리 영역에 새로운 문자열을 생성하여 전달해본다.

- 현재는 Hello world를 F292A4주소에 전달하고, 메세지박스가 그걸 출력해준다.

그러면 이문자열의 주소를 변경해서 출력해주면 될것 같다. (전혀 다른 문자열주소를 함수에 넘겨줌)



헥사의 빈 Null padding공간에 데이터를 작성한 후




push에 수정된 메모리 주소를 넘겨준다




그랬더니...아니 이건






[헤헤]



오늘은 여기까지 ㅅㅅ


정리.

- xdbg로 실습.

- hex값 변조로 데이터 변조

- push 메모리 주소값 변조를 통해 내용변조

- 계속 디버깅해봐야 눈에 stub code가 보인다고함

- 리버싱 재밌다. 뒤에가면 어렵다는데 무섭당 -끝-








'Reversing' 카테고리의 다른 글

PE File(1)  (0) 2019.04.02
리버싱 5  (0) 2019.04.02
리버싱 4  (0) 2019.03.29
리버싱 3 Stackframe  (0) 2019.03.28
리버싱 2  (0) 2019.03.27

+ Recent posts