보안/리버싱 (7)
2024-09-01 21:53:12

문제 파일을 다운받고, 실행하면 아무것도 없는 그냥 빈 화면이 나온다.
이렇게 패킹된 것은 ida로 열어서 정적분석해도 큰 의미는 없을 것 같다,
바로 x32dbg를 켜서 파일을 올리면

위와 같은 화면. 현재 ep는 0040A04B.

이 상황에서 아래로 쭉 내려서 확인을 해 보았다. OEP는 아마 현재 주소부터 어느 지점까지 실행되다가 특정 주소로 이동하는 지점이 있을 것이다. 실행의 마지막에 점프되는 주소가 oep이지 않을까 하는 생각임.

점프문이 있는지 살펴보면서 내려봤는데,

저 마지막 점프문이 의심스럽다.
브레이크포인트 걸고 실행해주자.
실행 결과 무사히 브레이크지점까지 실행되었고, 이제 한 줄 더 실행하면

00401150으로 점프되었다.
여기가 oep일 것으로 예상
일단 생각한 로직은 oep로 가려면 현재 주소에서 어느 부분까지가 반복문이든 뭐든 실행되다가, 결국은 점프문으로 실제 oep로 점프할 거라고 생각해서 점프되는 부분을 찾았고, 그 부분이
위에 브레이크포인트 걸어준 부분입니다.

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10
2024-08-18 20:47:15
  • (13.1~13.6)

13.1~13.2 PE File Format

PE 파일 소개

  • PE(portable executable): windows 운영체제에서 사용되는 실행 파일 형식
  • 32비트의 실행 파일을 의미한다.
  • 64비트의 경우 PE+, PE32+ 라고 부름

PE File Format

PE 파일의 종류

  • 실행 계열: EXE, SCR
  • 라이브러리 계열: DLL, OCX, CPL, DRV
  • 드라이버 계열: SYS, VXD
  • 오브젝트 파일 계열: OBJ(이걸 제외하고 위 모든 것들은 실행 가능함)

헤더

  • PE 헤더: 어떻게 메모리에 적재되고, 어디서부터 실행되어야 하는지, 실행에 필요한 DLL들은 어떤 것이 있는지 등등 exe파일이 실행되기 위해 필요한 모든 정보가 적혀있다고 할 수 있다.

PE파일의 기본 구조

실습 내용에서 확인한 바와 같이 notepad.exe는 일반적인 PE파일의 구조를 따르는데
DOS header ~Section header: PE헤더
그 밑의 sections들은 합쳐 PE바디 라고 한다.

  • 파일과 메모리: 파일에서는 offset, 메모리에서는 VA(Virtual Address, 절대주소)로 표현되기 때문에 파일이 메모리에 로딩되면 모양이 달라진다.

주소(VA&RVA)

- VA(Virtual Address)

프로세스 가상 메모리의 절대 주소

-RVA(Relative Virtual Address)

어느 기준 위치(Image Base)에서부터 상대주소
따라서 관계식을 아래와 같이 쓸 수 있다.

  • RVA + Image Base = VA

PE헤더 내의 정보는 RVA로 된 것이 많다.
PE파일이 프로세스 가상 메모리의 특정 위치에 로딩되는 순간 그 위치에 이미 다른 PE파일이 로딩되어 있을 수 있다. 이런 경우에 재배치(relocation)를 통해 비어 있는 다른 위치로 파일을 로딩시켜야하는데, VA( 절대 주소) 로 헤더 정보들이 표현되어있으면 정상적인 액세스를 진행할 수 없다.
RVA를 쓰면 relocation을 해도 ‘기준위치에 대한 상대주소’는 변하지 않기 때문에 정상적으로 작동된다.


13.3 PE 헤더

PE헤더는 많은 구조체로 이루어져있다. 여기서 다룰 것은

  • DOS Header
  • DOS Stub
  • NT Header
    • File Header
    • Optional Header

1. DOS Header

  • 마이크로소프트가 PE file format을 만들 때 당시 널리 사용되던 DOS하위 호환성 고려해서 만듦-> PE헤더의 맨 앞에 IMAGE_DOS_HEADER구조체가 있다.(DOS EXE HEADER의 확장)
  • IMAGE_DOS_HEADER의 구조체 크기는 고정되어있고 40이다.
  • 중요 멤버로는 e_magic, e_1fanew가 있는데
    • e_magic: 모든 PE파일은 시작 부분인 e_magic에 DOS signature “MZ”(마크 주비코브스키의 이니셜이다)(4D5A)가 있음
    • e_1fanew: 끝부분인 e_1fanew가 가리키는 위치에 NT Header(IMAGE_NT_HEADERS)구조체가 존재해야한다.

2. DOS Stub

  • DOS Header밑에 위치한다.
  • 없어도 실행에는 문제가 없기 때문에 존재 여부는 옵션이고, 크기도 일정하지 않다.
    • 존재 여부가 옵션이라 개발 도구에서 지원해줘야 한다.(VB, VC++등은 기본 지원한다고 한다.)
  • 코드와 데이터의 혼합으로 이루어져 있다.

3. NT Header

  • DOS Header를 설명하면서 간략히 언급하였는데, e_1fanew가 가리키는 위치에 이게 있다.
  • IMAGE_NT_HEADERS는 3개의 멤버로 되어있는데
      1. Signature: 50450000h(“PE”00)값을 가진다.
      1. File Header
      1. Optional Header

3-1. NT Header -File Header

  • 파일의 개략적인 속성을 나타낸다.
  • 아래 멤버가 정확히 세팅되어있지 않으면 파일은 정상적으로 실행되지 않는데
    • Machine, NumberOfSections, SizeOfOptionalHeader, Characteristics

- Machine

CPU별로 고유한 값을 가진다. 인텔 x86은 14C의 값을 가진다.

- NumberOfSections

PE파일은 코드, 데이터 리소스 등이 각각의 섹션에 나뉘어서 저장되는데, 이떄 그 섹션의 수를 의미한다. 이 값은 0보다 커야하고, 정의된 섹션과 실제 섹션이 다르면 실행 에러가 발생한다.

- SizeOfOptionalHeader

IMAGE_NT_HEADER32의 크기를 나타내는 멤버인데, 이건 C언어의 구조체이기 때문에 이미 크기가 결정되어있다. 그럼에도 이게 따로 필요한 이유는 Windows의 PE로더가 SizeOfOptionalHeader값을 보고 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 인식하기 때문이다.
64비트에서(PE32+)는 IMAGE_OPTIONAL_HEADER64구조체를 사용하는데, IMAGE_OPTIONAL_HEADER32와 크기가 다르기 때문에 SizeOfOptionalHeader멤버에 구조체 크기를 명시한다.

-Characteristics

파일의 속성을 나타내는 값이다.
실행 가능 여부(executable or not), DLL or not등의 정보들이 bit OR형식으로 조합된다.


3-2. NT Header -Optional Header

  • PE 헤더 구조체 중에 가장 크기가 큰 IMAGE_OPTIONAL_HEADER32
  • 아래 멤버들이 잘못 세팅되면 파일이 정상 실행되지 않는다
  • Magic, AddressOfEntryPoint, ImageBase, SectionAlignment, FileAlignment, SizeOfImage, SizeOfHeader, Subysystem, NumberOfRvaAndSizes, DataDirectory

1. Magic

IMAGE_OPTIONAL_HEADER32구조체의 경우 10B, 64의 경우 20B값을 가진다.

2. AddressOfEntryPoint

EP(Entry Point)의 RVA값을 가지고 있다. 이 값은 프로그램에서 최초로 실행되는 코드의 시작 주소이므로 매우 중요하다.

3. ImageBase

프로세스의 가상 메모리는 0~FFFFFFFF범위라서 매우 광활한데, ImageBase는 이 범위에서 PE파일이 로딩되는 시작 주소를 나타낸다.

  • EXE, DLL -> 0~7FFFFFFF(user memory)
  • SYS -> 80000000~FFFFFFFF(kernel memory)
    일반적으로 개발 도구가 만들어내는 EXE파일의 ImageBase는 00400000, DLL 파일의 ImageBase 값은 10000000이라고 한다. PE로더는 PE파일을 실행시키기 위해 프로세스를 생성하고 파일을 메모리에 로딩 후 EIP 레지스터 값을 ImageBase + AddressOfEntryPoint 값으로 세팅한다.

4. SectionAlignment, FileAlignment

PE파일의 Body는 섹션으로 나눠지는데

  • 파일에서 섹션의 최소단위 -> FileAlignment
  • 메모리에서 섹션의 최소단위 -> SectionAlignment
    한 파일에서 위 두 값은 같을 수도, 다를 수도 있다.
    그러나 파일과 메모리의 섹션 크기는 반드시 각각 FileAlignment, SectionAlignment의 배수가 되어야 한다.(최소 단위의 기준이므로)

5. SizeOfImage

PE파일이 메모리에 로딩되었을 때 가상메모리에서 PE Image가 차지하는 크기를 나타낸다. 일반적으로 파일의 크기와 메모리에 로딩된 크기는 같지 않다고 한다.

6. SizeOfHeader

PE 헤더의 전체 크기를 나타낸다. 섹션 크기에서와 마찬가지로 FileAlignment의 배수 크기여야 한다. 파일 시작에서 SizeofHeader 옵셋만큼 떨어진 위치에 첫 번째 섹션이 위치한다.

7. Subysystem

Subysystem 값을 보고 시스템 드라이버인지, 일반 실행 파일인지 구분 가능하다.

8. NumberOfRvaAndSizes

IMAGE_OPTIONAL_HEADER32 구조체의 마지막 멤버 DataDirectory 배열의 개수를 나타낸다.
구조체 정의에 배열 개수가 IMAGE_NUMBEROF_DIRECTORY_ENTRIES(16)라고 명시되어 있으나 PE 로더는 이 값을 보고 배열의 크기를 인식한다.

9. DataDirectory

위에서 잠깐 언급했지만 IMAGE_DATA_DIRECTORY구조체의 배열이다. 배열의 각 항목마다 정의된 값을 가진다.
Directory란 어떤 구조체의 배열로 보면 되는데, 이 중 IMPORT, EXPORT Direcotry구조는 PE헤더에서 매우 중요하다.

DataDirectory[0] = EXPORT Directory
DataDirectory[1] = IMPORT Directory
DataDirectory[2] = RESOURCE Direcotry
DataDirectory[3] = EXCEPTION Directory
...
DataDirectory[9] = TLS Directory
...
DataDirectory[F] = Reserved Directory

섹션 헤더

각 섹션의 속성을 정의한 것이 섹션 헤더이다.
PE파일은 코드, 데이터, 리소스 등을 각각 섹션으로 나눠서 저장한다고 했는데, 책에서 설명하기로 이렇게 PE파일을 여러 개의 섹션 구조로 만드는 이유는 프로그램의 안정성에 있다고 한다.
코드와 데이터가 하나의 섹션에 있다면 복잡하기도 하지만 안정성에 문제가 생길 수 있다. 데이터에 오버플로우와 같은 문제가 생겼을 때 코드와 리소스도 하나의 섹션에 모두 모여있다면 오버플로우가 코드를 그대로 덮어써버리게 될 것이다.
이러한 이유로 PE File Format 설계자들은 비슷한 성격의 자료를 섹션으로 나눠두었다. 따라서 섹션의 속성을 나타내기 위해 각각의 섹션에 섹션 헤더가 필요한 것이다.
코드, 데이터, 리소스 각각의 섹션마다 특성이나 접근 권한을 다르게 설정할 필요가 있다.

섹션 헤더는 각 섹션별 IMAGE_SECTION_HEADER 구조체의 배열로 되어있는데,

이 중 알아야 할 중요 멤버는

  • VirtualAddress, PointerToRawData는 각각 SectionAlignment와 FileAlignment에 맞게 결정된다.(아무 값이나 들어가선 안된다.)
  • VirtualSize와 SizeOfRawData는 일반적으로 다른 값을 가지는데, 위에서 언급했듯 파일에서의 섹션 크기와 메모리에 로딩된 섹션의 크기가 다르다는 말이다.
  • Characteristics는 아래 표시된 값들의 조합(bit OR)로 이루어진다.

Name멤버는 NULL로 끝나야 한다거나 ASCII값만 와야한다는 제한도 없는데, PE 스펙에는 섹션 네임에 대해 명시적인 규칙이 없어서 어떠한 값을 넣어도 상관없다. 심지어 NULL값도 가능하다!

13.4 RVA2RAW

PE파일이 메모리에 로딩되었을 때 각 섹션에서 메모리의 주소(RVA)와 파일 옵셋을 매핑하는 것을 RVA to RAW라고 부른다.방법은

  1. RVA가 속해 있는 섹션을 찾고
  2. 비례식을 통해 파일 옵셋(RAW)을 계산

IMAGE_SECTION_HEADER구조체에 의하면 비례식은

RAW - PointerToRawData = RVA - VirtualAddress
RAW = RVA - VirtualAddress + PointerToRawData

13.5 IAT

IAT란 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블이다.

DLL

IAT를 설명하기 위해 DLL의 개념을 먼저 짚고 넘어가자.
DLL(Dynamic Linked Library), 동적 연결 라이브러리는 메모리 절약을 위해 고안되었다고 볼 수 있는데, 이게 있기 전까지는 C라이브러리의 함수의 바이너리 코드를 그대로 가지고 와서 썼다. 그런데 멀티 태스킹 시 여러 프로그램이 동시에 실행되는데 도일한 라이브러리가 매번 포함되는 경우 심각한 메모리 낭비가 발생한다.
그래서 프로그램에 라이브러리를 포함시키지 말고 별도의 파일로 구성하여 필요할 떄마다 불러오자는 전략을 취했는데, 이 파일이 DLL이다.

DLL 로딩 방식

  • Explicit Linking: 프로그램에서 사용되는 순간에 로딩하고 사용이 끝나면 메모리에서 해제되는 방법
  • Implicit Linking: 프로그램 시작 시 로딩되어 프로그램 종료 시 해제됨
    DLL은 위 두 로딩 방식이 있는데 IAT는 Implicit Linking에 대한 매커니즘을 제공한다.

notepad에서 살펴보면 01001104주소에 있는 값을 가져와서 호출하게 된다. 위 주소의 값은 7C8107F0인데 이걸 바로 CALL 7C8107F0으로 호출하지 않는 이유는

  • notepad.exe 프로그램을 컴파일하는 순간에 어떤 윈도우즈 버전, 어떤 언어, 어떤 서비스팩 등 어떤 환경에서 실행될지 알 수 없는데, 위와 같은 환경의 변화에 따라 kernel32.dll버전과 CreateFileW함수의 주소가 달라지기 때문이다. 따라서 주소가 저장될 위치만 준비해야하고, 실제 값은 하드코딩할 수 없다.
  • DLL Relocation문제가 있다. 로딩하려는 위치가 이미 사용중인 메모리인 경우 비어있는 메모리 공간에 로딩시켜주게 되므로(relocation) 하드코딩할 수 없다.

IMAGE_IMPORT_DESCRIPTER

PE파일은 자신이 어떤 라이브러리를 임포트하고 있는지 IMAGE_IMPORT_DESCRIPTER에 명시하고 있다.

'보안 > 리버싱' 카테고리의 다른 글

reversing.kr - easy unpack  (0) 2024.09.01
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10
2024-08-18 20:25:34
  • exe파일이므로 마찬가지로 윈도우에서 실행하자.
  • 사용 도구는 x32dbg
  • 실행하면

와 같이 이름을 먼저 묻고 시리얼을 묻는다.
조건을 만족하지 않는 값을 넣으면 Wrong이 출력된다.

같이 첨부된 리드미 파일을 열어보면

Find the Name when the Serial is 5B134977135E7D13
라고 한다. 시리얼이 주어져있고, 이름을 찾아내면 되는듯하다

ida를 열어, 메인함수를 디컴파일하면

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // ebp
  int i; // esi
  int v6; // [esp+0h] [ebp-13Ch]
  int v7; // [esp+0h] [ebp-13Ch]
  int v8; // [esp+Ch] [ebp-130h]
  char v9[100]; // [esp+10h] [ebp-12Ch] BYREF
  char Buffer[197]; // [esp+74h] [ebp-C8h] BYREF
  __int16 v11; // [esp+139h] [ebp-3h]
  char v12; // [esp+13Bh] [ebp-1h]

  memset(v9, 0, sizeof(v9));
  memset(Buffer, 0, sizeof(Buffer));
  v11 = 0;
  v12 = 0;
  LOWORD(v8) = 8208;
  BYTE2(v8) = 48;
  sub_4011B9(aInputName, v6);
  scanf("%s", v9);
  v3 = 0;
  for ( i = 0; v3 < (int)strlen(v9); ++i )
  {
    if ( i >= 3 )
      i = 0;
    sprintf(Buffer, "%s%02X", Buffer, v9[v3++] ^ v9[i - 4]);
  }
  memset(v9, 0, sizeof(v9));
  sub_4011B9(aInputSerial, v7);
  scanf("%s", v9);
  if ( !strcmp(v9, Buffer) )
    sub_4011B9(aCorrect, v8);
  else
    sub_4011B9(aWrong, v8);
  return 0;
}

위와 같다.
사용자가 입력한 name을 받아 v9에 저장하고, 반복문 내 연산을 수행한 후 Buffer에 저장하는 것으로 보인다. 그 후 v9와 Buffer 를 비교하여 일치 여부에 따라 Correct, Wrong이 나타나는 듯하다.
리드미에 있는 시리얼 값을 이용하여 name을 역연산하면 해결할 수 있을 것이다.

x32dbg를 열어서 확인해보면

브레이크 포인트가 걸린 곳은 scanf로 name을 받는 곳이고, 계속 한줄씩 실행하다보면 해당 지점으로 계속 돌아온다. 이 부분이 위의 반복문임을 확인할 수 있다.


전체 뷰를 확인하면 위와 같다.

# 키값으로 이름 찾는 알고리즘 
# find name algorithm using key value
# 5B134977135E7D13
hexx = ['5b','13','49','77','13','5e','7d','13']
count = 1
full = ''

for x in hexx:
    for i in range(0,200):
        a = (16 * count) ^ i
        a = hex(a)
        a = a[2:]

        if a == x:
            print('원래값:',i,"hexx값:",x,'count값체크:',count)
            full = full + chr(i)

            if count == 3:
                count = 1                
                break

            else:
                count = count + 1                
                break
print(full)
원래값: 75 hexx값: 5b count값체크: 1
원래값: 51 hexx값: 13 count값체크: 2
원래값: 121 hexx값: 49 count값체크: 3
원래값: 103 hexx값: 77 count값체크: 1
원래값: 51 hexx값: 13 count값체크: 2
원래값: 110 hexx값: 5e count값체크: 3
원래값: 109 hexx값: 7d count값체크: 1
원래값: 51 hexx값: 13 count값체크: 2
K3yg3nm3

'보안 > 리버싱' 카테고리의 다른 글

reversing.kr - easy unpack  (0) 2024.09.01
1주차  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10
2024-08-18 18:03:45

exe파일이므로 윈도우에서 실행하면
input를 묻고

IDA를 키면

input을 묻는 부분을 바로 찾을 수 있다.


메인함수를 디컴파일해보니 위와 같다.
추정컨대 sub_140011B0으로 인풋 메시지를 출력하고
sub_14001210으로 값을 받아서 저장하는 것으로 보인다.

우리가 알아야 하는것은 Correct가 뜨는 조건인데 그 조건과 관련된 함수는
sub_14001000으로 보인다.

그 함수도 디컴파일했는데 결과가 위와 같다. 인풋값을 ac배열과 비교하는 로직이다.
따라서 ac배열을 조사해볼 필요가 있다.

ac배열을 클릭해서 들어가보면
Comp4re_the_arr4y를 확인할 수 있다.

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10
picoCTF ARMssembly0  (0) 2024.08.08
2024-08-11 20:31:58
  • exe 파일이므로 윈도우에서 실행해보자.
  • 실행하면 패스워드를 받는 구조인데,

비밀번호를 현재는 모르니 아무거나 입력하면 아래와 같은 메시지박스를 출력한다.

 

 

 

 

따라서 Incorrect Password를 검색하여 그 전후로 비밀번호를 검증하는 로직을 찾을 수 있을 것이다.
틀린 경우에 위와 같은 메시지박스를 출력할 것이고, 맞을 때는 현재는 알 수 없다

.

x32dbg에서 Incorrect Password를 찾으면 아래와 같은 위치에 있다.

 

 

 

 

조금 올려보면 Congratulation이 있다. Congratulation이 되는 조건을 찾으면 될 것으로 보인다. 입력 방식을 보아 Congratulations 위로 문자열을 비교하는 로직들을 확인하면 될 것 같다.
그 위로 esp+4와 E(코멘트로 45:E)를 비교하는 로직이 있다.

 

 

더 더 위로 올려보면

 

 

위와 같이 어떤 문자열과 비교하는 과정들이 있다.
주석처리를 통해 저것들이 무엇인지 알 수 있으며, 플래그가 저기 적힌 a, 5y, R3versing과 관련이 있을 것으로 보인다.

Ea5yR3versing을 입력하게 되면

 

 

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
ELF x86 - Basic  (0) 2024.08.10
picoCTF ARMssembly0  (0) 2024.08.08
2024-08-10 00:53:28

파일을 다운받고, 압축을 해제하면 ch2.bin이 나온다.
제목에서도 예상했지만 32비트 ELF파일이다.

file ch2.bin
ch2.bin: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.8, with debug_info, not stripped

프로그램을 미리 실행해보면, username을 먼저 받고 username이 틀리면 그 이후로 넘어가지 않는다.
여기까지만 보고 예상해 보건대 username과 password를 모두 맞춰야 통과되는 것 같다.

그런데,
이걸 IDA로 열면 사실 굉장히 직관적으로 답이 보인다...
아이다를 열면 성공적으로 메인함수로 바로 보여준다.

열자마자 john, the ripper라는 문자열이 보인다.
그리고 아래에 아까 봤던 익숙한 시작 화면 문자열이 보인다. username을 받고 분기점을 보니 username이 맞을 때 password를 입력할 수 있도록 넘어가는 것이다.

usename에 john을, password에 the ripper를 넣으면

./ch2.bin
############################################################
##        Bienvennue dans ce challenge de cracking        ##
############################################################

username: john
password: the ripper
Bien joue, vous pouvez valider l'epreuve avec le mot de passe : 987654321 !

플래그가 987654321이라고 한다. 대충지었군..

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
picoCTF ARMssembly0  (0) 2024.08.08
2024-08-08 10:16:41

What integer does this program print with arguments 266134863 and 1592237099? File: chall.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})


[WRITEUP]

파일을 다운받고

file chall.S
chall.S: assembler source, ASCII text

확인해보면 어떤 어셈블리 소스코드임을 확인할 수 잇다.

cat chall.S

로 내용을 출력해보면

cat chall.S          
    .arch armv8-a
    .file    "chall.c"
    .text
    .align    2
    .global    func1
    .type    func1, %function
func1:
    sub    sp, sp, #16
    str    w0, [sp, 12]
    str    w1, [sp, 8]
    ldr    w1, [sp, 12]
    ldr    w0, [sp, 8]
    cmp    w1, w0
    bls    .L2
    ldr    w0, [sp, 12]
    b    .L3
.L2:
    ldr    w0, [sp, 8]
.L3:
    add    sp, sp, 16
    ret
    .size    func1, .-func1
    .section    .rodata
    .align    3
.LC0:
    .string    "Result: %ld\n"
    .text
    .align    2
    .global    main
    .type    main, %function
main:
    stp    x29, x30, [sp, -48]!
    add    x29, sp, 0
    str    x19, [sp, 16]
    str    w0, [x29, 44]
    str    x1, [x29, 32]
    ldr    x0, [x29, 32]
    add    x0, x0, 8
    ldr    x0, [x0]
    bl    atoi
    mov    w19, w0
    ldr    x0, [x29, 32]
    add    x0, x0, 16
    ldr    x0, [x0]
    bl    atoi
    mov    w1, w0
    mov    w0, w19
    bl    func1
    mov    w1, w0
    adrp    x0, .LC0
    add    x0, x0, :lo12:.LC0
    bl    printf
    mov    w0, 0
    ldr    x19, [sp, 16]
    ldp    x29, x30, [sp], 48
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

와 같이 나오는 것을 볼 수 있다. (코드가 짧아서 전체를 집어넣었다.)

func1 함수

func1:
    sub    sp, sp, #16
    str    w0, [sp, 12]
    str    w1, [sp, 8]
    ldr    w1, [sp, 12]
    ldr    w0, [sp, 8]
    cmp    w1, w0
    bls    .L2
    ldr    w0, [sp, 12]
    b    .L3

위에서 핵심은 cmp w1, w0이다.
w0이 w1보다 작으면 L2로 분기한다고 한다.

L2, L3

.L2:
    ldr    w0, [sp, 8]
.L3:
    add    sp, sp, 16
    ret
    .size    func1, .-func1
    .section    .rodata
    .align    3
.LC0:
    .string    "Result: %ld\n"
    .text
    .align    2
    .global    main
    .type    main, %function

main

main:
    stp    x29, x30, [sp, -48]!
    add    x29, sp, 0
    str    x19, [sp, 16]
    str    w0, [x29, 44]
    str    x1, [x29, 32]
    ldr    x0, [x29, 32]
    add    x0, x0, 8
    ldr    x0, [x0]
    bl    atoi
    mov    w19, w0 
    ldr    x0, [x29, 32]
    add    x0, x0, 16
    ldr    x0, [x0]
    bl    atoi 
    mov    w1, w0
    mov    w0, w19
    bl    func1 ; func1호출
    mov    w1, w0 
    adrp    x0, .LC0
    add    x0, x0, :lo12:.LC0
    bl    printf
    mov    w0, 0
    ldr    x19, [sp, 16]
    ldp    x29, x30, [sp], 48
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

결과적으로 들어오는 인자 중 더 큰 값이 반환된다.
따라서 위 인풋에서 들어오는 값 중 더 큰 것은 1592237099 이므로,
플래그 형식에 맞게 32비트 16진수로 바꾼 플래그는
picoCTF{5ee79c2b}

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10