분류 전체보기 (34)
2024-09-16 19:09:28

보호글입니다.
비밀번호를 입력하시면
내용을 보실 수 있습니다.


2024-09-16 19:08:46

보호글입니다.
비밀번호를 입력하시면
내용을 보실 수 있습니다.


2024-09-16 19:07:46

보호글입니다.
비밀번호를 입력하시면
내용을 보실 수 있습니다.


2024-09-16 19:06:31

보호글입니다.
비밀번호를 입력하시면
내용을 보실 수 있습니다.


2024-09-14 20:57:24

문제 요약:

테스트 케이스 T가 처음에 주어지고, 그 횟수만큼 최소 이동 횟수를 구하면 되는 문제.

이동 로직:

  • 이전 시기에 k만큼 움직였다면 다음 턴에 k-1, k, k+1만큼 이동할 수 있다.
  • 초기에는 -1, 0, 1 선택 가능하나(사실상 1로 시작하는것), 마지막에는 이동거리가 1로 고정되어야한다.

문제 풀이

  • 마지막에 1로 움직일 거리가 고정되어야하므로 직전에는 0, 1, 2만큼 움직여야 가능하나, 0만큼 움직이는 건 무의미하므로 1이나 2로 끝나야 함.
  • 그렇다면 마지막에서 두 번쨰는 최대 3까지 이동, 세 번째는 최대횟수 4...이러한 패턴이 있다
  • 각각 지점에서 최대 이동거리를 취했다가 다시 돌아온다고 가정하면 1->2->3->...n->n-1->...->2->1과 같이 된다.
  • 그리고 저런 식의 패턴은 합이 $n^2$이고, 한 턴에 이동한 최대거리가 최종적으론 n이 되는것이다.
  • 저런 패턴에서 1이나 2를 마지막에 추가하여 가능한 길이를 더 만들 수도 있다.1->2->3->2->2->1->1...이런 식으로

문제 상황을 분석하자면 위와 같은 느낌이다.
예시를 적용해보자면 9는 1->2->3->2->1로 저기서의 n은 3이며 2n+1번만큼의 이동에서 최소 이동횟수를 갖는다. 제곱수의 경우 저 로직에서 벗어나지 않을 것으로 보인다. 그렇다면 제곱수가 아닌 경우, 즉 $n^2+\alpha$와 같은 수에서 저게 어떻게 작동되는지 알면 사실상 해결되었다고 봐도 될 것으로 보임. 1이나 2 추가하는 것은 그닥 문제가 아니라고 했는데, 사실 n(최대 이동거리) 이 친구보다 $\alpha$가 같거나 작으면 한 번만 추가해도 되고, 그렇지 않다면 쪼개야 하는 것이다. 쪼갤 때는 n이하의 수들로 분할하면서, 이동 횟수는 최소한이 되도록 쪼개야 할 것이다. 우리가 구해야하는 건 최소 횟수인데, 만약 쪼갠다고 할 때 최대로 쪼갠다면 1->2->...n->n->n->...와 같이 추가될 수 있으므로 $\alpha$를 n으로 나누면 복잡한 로직 없이 해결될 것 같다.

#include <cmath>
#include <iostream>

using namespace std;

int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int t;
  long long x, y;
  long long move, max;
  // 이동 횟수와 최대 이동거리
  cin >> t;
  for (int i = 0; i < t; i++) {
    cin >> x >> y;
    move = 0;
    max = 0;
    while (max * max <= y - x)
      max++;
    //y-x는 거리, 제곱수로 표현 가능한 최대 max를 찾는다
    max--;
    move = 2 * max - 1;
    long long alpha = y - x - max * max;
    //거리에서 제곱수 말고 남는 부분.
    alpha = (long long)ceil((double)alpha / (double)max);
    //최대 횟수로 나눠준 후 올림을 통해 정수인 횟수로 나오게 해야한다. 
    move += alpha;
    cout << move << '\n';
  }
}

구현은 위와 같이 했다. ceil은 올림을 해주는 함수이다. 일반적으로 round를 쓸 일이 많은 것 같은데, 이동 횟수 자체는 반올림으로 떨어지게 하는 것이 더 처리에 불편할 것 같으므로 위와 같이 ceil을 쓰는 게 더 간단해보이는 부분...

'알고리즘 > 백준' 카테고리의 다른 글

백준 5430 c++  (1) 2024.09.26
백준 13172 c++  (0) 2024.09.03
백준 11005 [C언어]  (0) 2024.07.17
2024-09-03 10:54:46

문제

너무 길어서 요약했다.
M개의 주사위가 있고, i번쨰 주사위가 $N_{i}$ 번째 주사위이며 모든 면에 적힌 숫자를 더한 값이 $S_{i}$일때 각 주사위에 대해 각 면이 나올 확률이 동일하다면, 모든 주사위를 한 번씩 던졌을 때 나온 숫자들의 합의 기댓값을 구하는 문제다.

이는 아래 방법을 따르는데,

$S_{1}/N_{1} + S_{2}/N_{2} + … + S_{M}/N_{M}$
를 계산할 때 따르는 통분 문제를 해결하기 위해 분수를 모듈러 상의 정수로 가지도록 한다.

기약 분수 $a/b = a * b^{-1}mod X$ 로 계산한다.
$b^{-1} * b = 1(modX)$

페르마의 소정리:

소수 모듈러에서
$b^{X-1} * b = 1(modX)$
가 성립하므로
$b^{X-2} = b^{-1}(modX)$

그러나 위와 같은 방식에서 서로 다른 분수를 모듈러 상 같은 정수로 저장하는 경우가 있고, 분모가 소인수X로 가지는 경우에 역원을 계산할 수 없어서 모둘러를 1,000,000,007과 같은 큰 소수로 한다.
이 방식을 최종으로 하여 문제를 푼다.

입력

첫 번째 줄에는 주사위의 수를 나타내는 정수 M(1 ≤ M ≤ 104)이 주어진다.

다음 M개의 줄은 각 주사위의 정보를 나타내며, 이 중 i(1 ≤ i ≤ M)번째 줄에는 Ni, Si(1 ≤ Ni, Si ≤ 109)가 공백으로 구분되어 주어진다.

출력

모든 주사위를 한 번씩 던졌을 때 나온 숫자들의 합의 기댓값을 출력한다. 정확한 판별을 위해, 답을 기약분수로 나타내었을 때 a/b가 된다면, (a × b-1) mod 1,000,000,007을 대신 출력하도록 한다. b-1은 b의 모듈러 곱셈에 대한 역원이다. 이 문제에서는 가능한 모든 입력에 대해 답이 존재한다.


풀이

#include <iostream>
#define MOD 1'000'000'007

using namespace std;
typedef long long ll;

ll power(ll a, ll b) {
  ll ret = 1;
  while(b) {
    if (b & 1)
      ret = ret * a % MOD;
    b /= 2;
    a = a * a % MOD;
  }
  return ret;
}

int main(void) {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);

  int M;
  cin >> M;
  ll ans = 0;

  for (int i = 0; i < M; i++) {
    int n, s;
    cin >> n >> s;
    ans += (s * power(n, MOD - 2)) % MOD;
    }
  cout << ans % MOD << '\n';
  return 0;
}

'알고리즘 > 백준' 카테고리의 다른 글

백준 5430 c++  (1) 2024.09.26
백준 1011 c++  (0) 2024.09.14
백준 11005 [C언어]  (0) 2024.07.17
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-20 03:25:32
  • 전처리
  • 컴파일
  • 어셈블
  • 링킹

전처리

전처리 과정에서는 소스코드에서 주석을 제거하고, 전처리 지시문을 처리한다.(#include #define)같은 것들이 전처리 지시문이다.

컴파일

전처리를 마친 소스코드를 어셈블리 언어로 변환한다. C언어나, C++, 파이썬 등의 하이레벨 언어가 어셈블리로 변환되는 과정인데, 이의 역과정(어셈블리어를 하이레벨 스타일의 언어로 변환하는 것)은 디컴파일이라고 한다.

어셈블

컴파일된 어셈블리 코드를 바이너리 코드(기계어)로 바꾸는 과정이다. 어셈블하게 되면 기계어로 작성된 오브젝트 파일이 된다.

링킹

위의 과정까지 마쳤으면 CPU가 실행 가능한 바이너리 파일이 되는데, 링킹을 통해 여러 오브젝트 파일들을 결합하여 실행 가능한 프로그램으로 만들게 된다.

'knockon' 카테고리의 다른 글

bubble  (0) 2024.09.16
REV-basic  (0) 2024.09.16
[3주차 TIL] Knockon Bootcamp 컴퓨터 아키텍쳐  (0) 2024.08.20
[2주차 TIL] KnockOn Bootcamp 탐색 알고리즘  (0) 2024.08.13
[2주차 TIL] KnockOn Bootcamp 정렬 알고리즘  (0) 2024.08.13
2024-08-20 02:55:14

COMPUTER ARCHITECTURE


  • 컴퓨터 구조
  • 명령어 집합 구조
  • 범용 레지스터
  • 세그먼트 레지스터
  • 플래그 레지스터
  • 명령어 포인터 레지스터

- 컴퓨터 구조

- 폰 노이만 구조

  • 중앙처리장치(central processing unit):
    산술논리장치(Arithmetic Logic Unit): 산술, 논리연산
    제어장치(Control Unit): CPU 제어
    레지스터(Register): CPU에 필요한 데이터 저장
  • 기억장치(memory)
    램(Random-Access Memory): 주기억장치
    보조기억장치: HDD, SDD
    레지스터, 캐시: 데이터 병목현상 방지 저장장치. 보조기억장치 아님
  • 버스(bus):
    data bus
    address bus
    control bus
    the software that carries data -> also called bus

- 컴퓨터 아키텍쳐란?

  • 시스템의 설계와 구성 요소를 정의하는 구조, 프로세서나 메모리, 입출력 장치 간 상호작용 등등이 포함된다.

종류

  • X86(CISC)
    인텔의 32비트 아키텍쳐. 주로 데스크톱, 노트북, 서버에서 많이 사용되었으나, 현재는 64비트가 주류
  • x86_64(CISC)
    x86의 확장판으로 볼 수 있는 64비트 아키텍쳐. 현재는 이게 많이 사용된다.
  • ARM(RISC)
    인텔의 두 아키텍쳐보다 저전력, 단순한 명령어 집합. 에너지 효율성이 높다.
  • MIPS(RISC)
    임베디드 시스템에 많이 사용된다.
  • RISC-V(RISC)
    마찬가지로 임베디드나 교육적 목적에 주로 사용.

- 명령어 집합 구조(Instruction Set Architecture)

  • IA-32, x86-64(x64), MIPS, AVR

- x86-64 architecture

WORD:32 비트, 64비트...:

CPU가 이해할 수 있는 데이터의 단위. 32비트 아키텍처-> ALU가 32비트까지 계산 가능. 설계상 CPU는 32비트까지의 데이터 처리 가능. 워드가 크면 가상메모리가 크다. 64비트 아키텍처에서 메모리 자원이 부족해서 소프트웨어의 성능이 떨어지는 경우는 거의 발생하지 않음.

레지스터: CPU 내부의 저장장치

1. 범용 레지스터

주 용도는 있으나 그 외에도 자유롭게 사용 가능

rax(accumulator register)함수의 반환값 
rbx(base register)x64에서는 주된 용도 없음 
rcx(counter register)반복문의 반복 횟수, 연산 시행 횟수 
rdx(data register)x64에서는 주된 용도 없음 
rsi(source index)데이터를 옮길 때 원본을 가리키는 포인터 
rdi(destination index)데이터를 옮길 때 목적지를 가리키는 포인터 
rsp(stack pointer)사용중인 스택의 위치를 가리키는 포인터 
rbp(stack base pointer)스택의 바닥을 가리키는 포인터 
2. 세그먼트 레지스터

과거에는 메모리 세그먼테이션 or 가용 메모리 확장
현재는 주로 메모리 보호를 위해 사용
cs, ss, ds, es, fs, gs: 각각 16비트의 레지스터
코드, 스택 메모리, 데이터, 나머지는 운영체제별 용도

3. 명령어 포인터 레지스터

CPU가 어느 부분의 코드를 실행할지 가리킴.
기계어로 작성.
x64아키텍처에서 명령어 레지스터: rip, 8바이트

4. 플래그 레지스터

CPU의 현재 상태 표현

CF(Carry Flag)부호 없는 수의 연산 결과가 비트의 범위를 넘을 경우
ZF(Zero Flag)연산의 결과가 0일 경우
SF(Sign Flag)연산의 결과가 음수일 경우
OF(Overflow Flag)부호 있는 수의 연산 결과가 비트 범위를 넘을 경우
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