[풀이 바로가기]

[알고리즘 알아내기!]

'Abex' crackme' 카테고리의 다른 글

Abex' crackme #1  (0) 2019.03.27

앙 규문띻 

 

 

PE file

오늘은 PE file 구조에 대해서 공부해야겠다.

그전에 PE파일에 대해서 잘 정리해놓은 블랙펄 콘치님의 PE둥 PE둥 시리즈들을 먼저 보고오면 좋을듯하다. (짱짱;;)

 

[PE둥 PE둥 놀면서 훑어보는 PE(1)]

[PE둥 PE둥 놀면서 훑어보는 PE(2)]

[PE둥 PE둥 놀면서 훑어보는 PE(3)]

 

PE file 이란 ? 

- PE 포맷(Portable Executable)은 윈도우 운영 체제에서 사용되는 실행 파일, DLL, object 코드, FON 폰트 파일[1] 등을 위한 파일 형식이다 (위키)

 

즉 .exe 뿐만아니라 dll, sys같은 파일들도 전부 PE 파일이라고 명칭할 수 있다고 한다.

에그 복잡해;;;

이미지 - https://slimv.tistory.com/entry/PE-File-Format-0x01

 

먼저 헤더부터 살펴보자.

PE 파일의 헤더는 3가지로 구분됩니다.

1) DoS Header

2) NT Header

3) Section Header

 

DoS Header

DoS쓰지도않는데 DoS헤더가 왜있을까!

=> PE file format을 만들 당시 널리 사용되던 DOS파일에 대해 하위호환성을 고려해서 만들었음.

그 결과로 인해 PE 헤더의 제일 앞부분에 기존 DOS EXE Header를 확장시킨 IMAGE_DOS_HEADER 구조체가 존재.

 

그렇다면 구조체 내용을 보자!

typedef struct _IMAGE_DOS_HEADER {
     WORD  e_magic;      /* 00: MZ Header signature */
     WORD  e_cblp;       /* 02: Bytes on last page of file */
     WORD  e_cp;         /* 04: Pages in file */
     WORD  e_crlc;       /* 06: Relocations */
     WORD  e_cparhdr;    /* 08: Size of header in paragraphs */
     WORD  e_minalloc;   /* 0a: Minimum extra paragraphs needed */
     WORD  e_maxalloc;   /* 0c: Maximum extra paragraphs needed */
     WORD  e_ss;         /* 0e: Initial (relative) SS value */
     WORD  e_sp;         /* 10: Initial SP value */
     WORD  e_csum;       /* 12: Checksum */
     WORD  e_ip;         /* 14: Initial IP value */
     WORD  e_cs;         /* 16: Initial (relative) CS value */
     WORD  e_lfarlc;     /* 18: File address of relocation table */
     WORD  e_ovno;       /* 1a: Overlay number */
     WORD  e_res[4];     /* 1c: Reserved words */
     WORD  e_oemid;      /* 24: OEM identifier (for e_oeminfo) */
     WORD  e_oeminfo;    /* 26: OEM information; e_oemid specific */
     WORD  e_res2[10];   /* 28: Reserved words */
     DWORD e_lfanew;     /* 3c: Offset to extended header */
 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

크기는 총 40이다. 여기서 일단 살펴볼껀 2가지를 살펴보자. 

- e_magic

- e_lfanew

 

e_magic : DOS signature 4D5A를 나타낸다. ('MZ')

MZ 보이시죠 헤헿

PE의 스펙에 맞게 '4D5A'의 2바이트로 시작하며 'MZ'가 보입니다. 그 다음으로

e_lfanew : NT Header의 offset을 나타낸다.

빨간네모박스가 e_lfanew 값입니다. 000000D8 입니다. (리틀 엔디언)

실제 000000D8에 'PE'가 보이네요 [NT Header 시작부분]

이 값들은 PE header이기 때문에 변조하면 정상 실행되지 않습니다.

 

DoS Stub

다음으로 DoS Stub 입니다.

DoS Header와 NT Header 사이가 그렇다면 DoS Stub이 되겠죠 ??

이 부분입니다!

DOS Stub의 존재여부는 옵션이며 크기도 일정하지 않습니다. (없어도 파일실행과 상관없음!)

위그림에서 offset 40~4D(this 전) 까지는 16비트 어셈영역이셈 32비트 운영체제에서 실행안됌. (DOS환경에서 실행)

=> 즉 DoS환경과 window 환경 둘다 호환되는 실행파일을 만들 수 있다?!

 

NT Header

다음으로 NT Header의 구조체 Image_NT_HEADERS입니다.

 

typedef struct _IMAGE_NT_HEADERS {
  DWORD Signature;
  IMAGE_FILE_HEADER FileHeader;     /* 0x04 */
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;   /* 0x18 */
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef struct _IMAGE_FILE_HEADER {
  WORD  Machine;
  WORD  NumberOfSections;
  DWORD TimeDateStamp;
  DWORD PointerToSymbolTable;
  DWORD NumberOfSymbols;
  WORD  SizeOfOptionalHeader;
  WORD  Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

typedef struct _IMAGE_OPTIONAL_HEADER {  
 
   /* Standard fields */
   WORD  Magic; /* 0x10b or 0x107 */ /* 0x00 */
   BYTE  MajorLinkerVersion;
   BYTE  MinorLinkerVersion;
   DWORD SizeOfCode;
   DWORD SizeOfInitializedData;
   DWORD SizeOfUninitializedData;
   DWORD AddressOfEntryPoint;        /* 0x10 */
   DWORD BaseOfCode;
   DWORD BaseOfData;
 
   /* NT additional fields */
   DWORD ImageBase;
   DWORD SectionAlignment;       /* 0x20 */
   DWORD FileAlignment;
   WORD  MajorOperatingSystemVersion;
   WORD  MinorOperatingSystemVersion;
   WORD  MajorImageVersion;
   WORD  MinorImageVersion;
   WORD  MajorSubsystemVersion;      /* 0x30 */
   WORD  MinorSubsystemVersion;
   DWORD Win32VersionValue;
   DWORD SizeOfImage;
   DWORD SizeOfHeaders;
   DWORD CheckSum;           /* 0x40 */
   WORD  Subsystem;
   WORD  DllCharacteristics;
   DWORD SizeOfStackReserve;
   DWORD SizeOfStackCommit;
   DWORD SizeOfHeapReserve;      /* 0x50 */
   DWORD SizeOfHeapCommit;
   DWORD LoaderFlags;
   DWORD NumberOfRvaAndSizes;
   IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */
 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

복잡;;;

 

먼저 IMAGE_NT_HEADERS 부터 봅시다...

typedef struct _IMAGE_NT_HEADERS {
  DWORD Signature;
  IMAGE_FILE_HEADER FileHeader;     /* 0x04 */
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;   /* 0x18 */
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

제일 첫번째 멤버로 Signature(50450000)을 가집니다 'PE'글자를 나타내며 이글자가 보이면 NT header의 시작이라고 볼 수 있습니다.

오오

다음으로 밑에 2개의 구조체가 있네요?

먼저 파일의 대략적인 속성을 나타내는 NT header - File header 입니다.

typedef struct _IMAGE_FILE_HEADER {
  WORD  Machine;
  WORD  NumberOfSections;
  DWORD TimeDateStamp;
  DWORD PointerToSymbolTable;
  DWORD NumberOfSymbols;
  WORD  SizeOfOptionalHeader;
  WORD  Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

이 구조체에서는 4가지 멤버가 중요합니다. (이 값이 정확하지 않으면 파일이 정상실행 되지않는다고 함.)

 

#1. Machine

Machine 넘버는 CPU 별로 고유한 값이며 32비트 Intel x86 호환 칩은 14C의 값을 가짐

=> 쉽게말해 이게 어떤 환경에 돌아가는지 알려줌 ^호^

014C면 intel 386 환경!

Machine의 넘버링은 아래를 참조하자!

/* These are the settings of the Machine field. */
#define IMAGE_FILE_MACHINE_UNKNOWN  0
#define IMAGE_FILE_MACHINE_I860     0x014d
#define IMAGE_FILE_MACHINE_I386     0x014c
#define IMAGE_FILE_MACHINE_R3000    0x0162
#define IMAGE_FILE_MACHINE_R4000    0x0166
#define IMAGE_FILE_MACHINE_R10000   0x0168
#define IMAGE_FILE_MACHINE_WCEMIPSV2    0x0169
#define IMAGE_FILE_MACHINE_ALPHA    0x0184
#define IMAGE_FILE_MACHINE_SH3      0x01a2
#define IMAGE_FILE_MACHINE_SH3DSP   0x01a3
#define IMAGE_FILE_MACHINE_SH3E     0x01a4
#define IMAGE_FILE_MACHINE_SH4      0x01a6
#define IMAGE_FILE_MACHINE_SH5      0x01a8
#define IMAGE_FILE_MACHINE_ARM      0x01c0
#define IMAGE_FILE_MACHINE_THUMB    0x01c2
#define IMAGE_FILE_MACHINE_ARMNT    0x01c4
#define IMAGE_FILE_MACHINE_ARM64    0xaa64
#define IMAGE_FILE_MACHINE_AM33     0x01d3
#define IMAGE_FILE_MACHINE_POWERPC  0x01f0
#define IMAGE_FILE_MACHINE_POWERPCFP    0x01f1
#define IMAGE_FILE_MACHINE_IA64     0x0200
#define IMAGE_FILE_MACHINE_MIPS16   0x0266
#define IMAGE_FILE_MACHINE_ALPHA64  0x0284
#define IMAGE_FILE_MACHINE_MIPSFPU  0x0366
#define IMAGE_FILE_MACHINE_MIPSFPU16    0x0466
#define IMAGE_FILE_MACHINE_AXP64    IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE  0x0520
#define IMAGE_FILE_MACHINE_CEF      0x0cef
#define IMAGE_FILE_MACHINE_EBC      0x0ebc
#define IMAGE_FILE_MACHINE_AMD64    0x8664
#define IMAGE_FILE_MACHINE_M32R     0x9041
#define IMAGE_FILE_MACHINE_CEE      0xc0ee

[Machine 넘버 참조]

 

#2. NumberOfSections

PE 파일은 코드, 데이터, 리소스가 각각의 세션에 나뉘어져서 저장됩니다.

이 멤버는 그 섹션의 개수를 나타낸다고 합니다. 이 값은 반드시 0보다 커야하며 정의된 섹션 개수와 실제 섹션이 다르면 실행 에러가 발생합니다.

 

위 그림에서 4개의 섹션을 가지고 있다고 합니다. PEview를 통해서 확인해볼까요?

놀랍군요! ㅋㅋㅋㅋㅋ

#3. SizeofOptionalHeader

IMAGE_NT_HEADERS 구조체체의 마지막 멤버는 IMAGE_OPTIONAL_HEADER32 입니다.

이 멤버는 바로 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타냅니다.

PE32+의 경우에는  IMAGE_OPTIONAL_HEADER64 구조체를 활용합니다. (두개의 구조체 크기는 다름)

=> 이렇게 구조체의 범위를 미리 잡아놓고 그만큼의 데이터를 파일의 정보로 받아들임 ㅋ.

 

#4. Characteristics

파일의 속성을 나타내는 값으로, 실행 가능한 형태인지 혹은 DLL파일인지의 정보들이 bit OR 형식으로 조합됨

ex) '0102' = '0100' + '0002'

=> PE 파일이 어떤 속성을 가지고 있는지 조합된 숫자로 파악한다!

 

0002와 2000은 기억하도록 하자(실행가능과 DLL)

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  
                                                     // (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from 
                                                     // file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, 
                                                     // copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, 
                                                     // copy and run from the swap file.
#define IMAGE_FILE_SYSTEM                    0x1000  // System File.
#define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.

[Characteristic]

'Reversing' 카테고리의 다른 글

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

Crack me #2 알고리즘 알아내기

- x32dbg로 abex' crack me 씨리얼 생성 알고리즘을 파악해보자.

 

먼저 abex' crack me #2를 디버거로 오픈한다.

 

저번장에서 우린이미 프로그램을 크랙했었다. 이제 시리얼 생성 알고리즘을 파악할 시간이다.

 

1. Serial 생성 알고리즘 찾기

프로그램의 check 버튼을 누르면 시리얼과 일치하는지를 검사하도록 되어있다.

전에 봤던 조건분기문은 분명히 [Check] 버튼의 이벤트로 발생하는 event handler? 일 것이다.

그렇다면 함수의 시작 부분부터 쭉~ 보다보면 다음과 같은 코드를 볼 수 있다.

 

* (ID는 ReverseCore로 입력했다)

호호

위의 push, mov는 전형적인 스택프레임을 구성할때 보이는 모습이고, 이부분이 함수의 시작임을 직관적으로(?) ㅇㅇ

따라서 이부분이 함수의 시작임을 알 수 있음.

그래서 여기에 캠프차리고 디버깅을 시작함.

[F8]막 눌러보면서 변화를 살펴보자!

 

몇번의 시행착오를 겪다보면 다음과 같은 코드를 만나게됨

AX값이 0이면 바로밑의 jmp문을 수행

에러문구를 보니 Name에 4글자가 안되면 AX가 0이 안되나보다! 아무튼 4글자 이상의 Name을 넣고 점프를 해보면!

다음과 같은 곳으로 뛰게된다.

짜잔!

mov, push는 스택프레임을 생성할 때 보이는 전형적인 패턴임을 알고간다.

루프문의 구조는 다음과 같다.

 

MOV EBX, 4								; 4개만 돌림
...
CALL DWORD PTR DS:[&~__vbaVarForInit]		
TEST EAX,EAX								; if(EAX==0) [loop start] 0되면 루프나감 ^^
JE abexcme2-.004032A5						
...
CALL dWORD PTR DS:[&~__vbaVarForNext]		
JMP abexcme2-00403197							; loop end
MOV EAX, DWORD PTR SS;[EBP+8]

여기가 원하는 알고리즘을 생성하는 부분이다.

그렇다면 이제 암호화 방법에 대해서 알아보자! 밑으로 내리다보면 다음의 부분이있다.

첫번째 PUSH(004031F6)에서 Name에서 가져온 유니코드값을 넣는다. ('R')

이후에 그 유니코드를 ASCII 변환을 한다 (004031F7)

 

0040323D까지 실행한후에 스택을 살펴보자.

ECX : 0018F3C4

EAX : 0018F384

EDX : 0018F40C

 

[CPU 범용 레지스터 까묵;;]

 

[보안] CPU 범용 레지스터

CPU 범용 레지스터 (EAX,EBX,ECX,EDX,EBP,ESI,EDI,ESP) - CPU 범용 레지스터란 범용적으로 사용되는(?) 레지스터 입니다. 모든 레지스터들은 각각 4byte(32bit)의 크기를 가진다고 하네요! 8개의 레지스터에서 상..

kyumoonhan.tistory.com

그럼 이제 각 메모리주소를 살펴본다.

ECX
EAX
EDX

다음을보면 ECX에서 18F3CC는 결과저장용 버퍼이다.

EAX의 18F38C에 암호화키 (64),

EDX에서 18F414는 Name에서 문자열의 첫 ASII키 값(내가입력한 'R' 52) 이다.

 

이 후에 밑의 함수를 실행시키면 ECX 레지스터가 가리키는 버퍼에 암호화된 값이 저장된다.

52 + 64 = B6 이겠죠?
ECX 결과값을 보니 정답!

계산결과 B6은 보다시피 원본값('R')에 64를 더해서 생성한 값임을 알 수 있었다!

이후에 아래의 부분을 이용해 'B6'값을 유니코드로 변경한다.

마지막 함수 호출시 변환

함수 호출직후 EAX가 가르키는 버퍼(0018F3C4)를 살펴보자

B6이 성공적으로 들어감!

이렇게 4번을 돌리겠죠? 끝으로 생성된 문자열을 붙여주는 코드가 뒤에 있다.

 

문자열 붙이기

- vbaVarCat() : 문자열 이어붙이기

함수 호출시 : old(ECX) + add(EDX) = NEW serial(EAX) ^오^

 

이런식으로 알고리즘이 짜여져있다.

그렇게 마지막 루프를 실행하게 되면

 

Serial = old("B6C9DA") + add("C9") = "B6C9DAC9"

이렇게 암호화 알고리즘을 풀었다.

(Reve의 아스키값에 64씩 더헀네)

 

정리

1. Name 문자열을 앞에서 한 문자씩 4회 읽는다.

2. Ascii로 변환한다.

3. 변환된거에 64를 더한다.

4. 다시 유니코드로 변환한다.

5. 변환된 문자를 연결시킨다.

 

 

 

 

 

'Reversing' 카테고리의 다른 글

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

 앙 규문띠 

 

 

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

Lord of SQLinjection - gremlin

오늘부터!!!!! LOS문제를 한문제씩 풀어 보겠어 (바쁘지않다면...)

주소 : los.rubiya.kr

 

첫번째 문제 - gremlin

마지막 코드를 살펴보니 id 값만 넘겨주면 문제가 풀리는것 같다.

  if($result['id']) solve("gremlin");

SQLinjection 기본 구문을 사용해보았다.

id값은 넘겨주기만 하면 되기 때문에 아무거나 넣고 싱글쿼터를 이용해서 닫아준다.

 

이후에 항상 참인 1=1과 함께 SQL문에서 주석을 의미하는 '#'을 URI encoding하여 23%로 요청을 보냈다!

 

GREMLIN CLEAR!!

 

'Lord of SQLinjection' 카테고리의 다른 글

LordofSQLinjection - goblin  (0) 2019.04.04
Lord of SQLinjection - cobolt  (0) 2019.04.02

 앙 규 문띠

Crack me 1번

abex' crack me 문제가 1번부터 5번까지 있다고 하는데 하나씩 풀어볼 예정이다.

두근두근!


abex' crackme #1 


crackme1.exe

먼저 .exe를 실행시켜보자! 



[다음과 같은 창이 뜬다 HD=CD-Rom ?????? 확인을 눌러본다]



[냥! CD-ROM드라이브가 아니다!]


리버싱 문제니까 디버거를 켜야겠지??



[F9]를 눌러 EP를 찾고보니 주석에 OK~ 메세지박스가 출력되도록 하면 성공인것 같습니다.


조금더 자세히 살펴보면




401026에 JE 조건 분기 명령어가 보입니다.

401024의 CMP eax와 esl을 비교해서 참거짓 유무에 따라서 이동하는 명령어 입니다.



실제로 해당줄까지 디버깅해보면 EAX=1, ESI=3으로 같지 않음을 알 수 있습니다. (JE는 두값이 같으면 점프, 다르면 밑으로 진행)


여기서 우리는 패치를 위해 조건분기문 JE가 아닌 JMP 점프명령어를 써서 우리가 원하는 문구를 출력하도록 해줍시다.



[JE 조건문을 다음과 같이 수정!!]


[성공 !]


- 정리


디버깅을 하면서 어떠한 순서로 프로그램이 실행되는지 확인한다.

x32dbg같은 경우 조건분기 명령 수행시 점선으로 표시된다.


* 스택의 주소는 아래로 자란다. 스택의 FILO구조에 따르게 된다 ^호^ (디버깅을 한줄씩 하면서 주소의 변화를 파악해본다)





'Abex' crackme' 카테고리의 다른 글

Abex' crackme #2  (0) 2019.04.02

바이트 오더링(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

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

- CPU 범용 레지스터란 범용적으로 사용되는(?) 레지스터 입니다. 모든 레지스터들은 각각 4byte(32bit)의 크기를 가진다고 하네요!


8개의 레지스터에서 상위 4개의 레지스터는 산술연산의 변수 값의 저장용도로, 하위 4개는 메모리 주소를 저장하는 포인터로 자주 사용된다고 합니다.

하지만 상황에따라서 레지스터의 용도가 다르게(?) 쓰일 수도 있습니다.


(* 앞의 E는 32bit 에서, 64bit는 R을 씀, RAX, RBX 이런식으로...)


* 요약ㅎㅎ

1) EAX : 산술 및 논리연산을 수행한 후에 함수의 반환값이 저장되는 용도 (쉽게 말해 함수의 리턴 값)

2) EBX : 메모리 주소를 저장하기 위한 용도

3) ECX : 반복문(for,while)에서 반복 카운트(loop count)로 사용 (루프 돌때마다 ECX 1씩감소)

4) EDX : EAX와 같이 쓰이고, 부호 확장 명령에 사용

5) ESI : 데이터를 조작 or 복사할 때 데이터의 주소가 저장됨

6) EDI : 데이터를 복사할 때 목적지 주소가 저장

7) ESP/EBP : ESP는 Stack의 끝 주소, EBP는 Stack의 첫 시작 주소를 의미함


그리고 각각의 레지스터는 32bit인데 16bit의 하위 호환을 위해 X로 나눠지고 X는 다시 H/L 구역으로 나눠짐 즉 EAX를 예로들면 32bit를 다 쓰고 싶으면 EAX를 사용하고 2바이트(16bit)만 쓰고 싶으면 EAX의 하위 비트 부분인 AX를 이용하면 되는 것, 1바이트만 쓰고싶으면 AH쓰면 되겠쥬? (알뜰하죠 ?ㅋ)



레지스터의 용도 정도만 간단하게 알고 넘어가야지~



'보안' 카테고리의 다른 글

Tomcat,JCE 취약점 ppt  (0) 2019.03.29
[보안] Tomcat 취약점  (0) 2019.03.26
[보안] 티스토리 블로그 취약점 문의  (0) 2019.03.25
[보안] 리버싱 입문기  (0) 2019.03.25
이번 주 해야할 것  (0) 2019.03.25

+ Recent posts