728x90

IDA에 내장되어있는 Python 기능이다.

내부의 소스코드 실행가능

 

idc.ScreenEA() : 현재 위치의 주소를 리턴한다.

here() : 현재 위치의 주소를 리턴

 

MainEA() : 최소주소를 리턴

MaxEA() : 최대 주소를 리턴

Segment 출력

SegName(address) : address에 대한 세그먼트 이름을 리턴

SegStart(address) : address에 대한 세그먼트 시작 주소를 리턴

SegEnd(address) : adadress에 대한 세그먼트 마지막 주소를 리턴

 

세그먼트별 주소 출력 소스코드

for seg in Segments():

       print SegName(seg), hex(SegStart(seg)),hex( SegEnd(seg))

 

함수 목록 출력

Functions(start_addr, end_addr) : 함수 목록을 리스트 형태로 리턴(범위 지정 가능)

GetFuncName(function_address) : 함수 주소의 이름을 리턴

 

함수목록 출력 소스코드

for func in Functions() :

       print GetFunctionName(func), hex(func)

 

 

현재 코드의 디스어셈블리 코드 출력하기

GetDisasm(here())

NextHead(here()) : 다음 명령 주소

PrevHead(here()) : 이전 명령 주소

 

 

 

함수의 주소 받아오기

GetFunction(address, attr) : address에 해당하는 함수의 attr(함수의 시작 or 끝 주소) 리턴

    - Attr : FUNCATTR_START ( 함수의 시작), FUNCATTR_END(함수의 끝)

 

현재 함수 디스어셈블 출력하기 소스코드

ea = here()

start = GetFunctionAttr(ea, FUNCATTR_START)

end = GetFunctionAttr(ea, FUNCATTR_END)

cur_addr = start

while cur_addr <= end:

print hex(cur_addr), GetDisasm(cur_addr)

cur_addr = NextHead(cur_addr, end)

플래그 받아오기

GetFunctionFlags(address) : address의 플래그 값을 리턴

Flags

FUNC_LIB : 라이브러리 코드를 찾는데 사용

FUNC_STATIC : 정적함수로 컴파일된 함수를 식별하는데 사용

FUNC_FRAME : 프레임 포인터 push ebp를 사용하는 함수를 식별하는데 사용

FUNC_BOTTOMBP : FUNC_FRAME과 마찬가지로 프레임 포인터 pop ebp를 추적하는데 사용

FUNC_THUNK : 다른 함수로 점프하는 간단한 함수인 THUNK를 식별하는데 사용

 

라이브러리 함수 목록 및 주소 출력 코드

for func in Functions():

        flags = GetFunctionFlags(func)

        if flags & FUNC_LIB:

                 print hex(func), "FUNC_LIB", GetFunctionName(func)

 

 

 

 

 

 

 

 

728x90

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

IDA - 사용법 및 단축키  (0) 2021.02.04
728x90

IDA 사용법

 

IDA는 바이너리 파일을 어셈블리어로 디스어셈블(disassemble) 해주는 프로그램 입니다.

바이너리 -> 어셈블리어 -> C언어 까지 변환을 해줍니다.

 

IDA를 통해 특정 바이너리를 열었을때

위처럼 바이너리의 함수들과 해당 함수들의 어셈블리어 코드들을 확인할 수 있다.

 

Hex-Ray

특정 함수 선택 및 f5 입력

어셈블리어를 C언어 소스코드로 변환해서 보여준다.

Search

Alt + T : 문자열 및 어셈블리어 검색

 

function list

Ctrl + p

함수목록을 보여주고 더블클릭시 해당 함수로 이동이 가능하다.

파일의 정보

TEXT View에서 가장 위쪽에 파일의 대략적인 정보를 확인할 수 있다.

 

 

어셈블리어 코드 수정하기

수정하고싶은 어셈블리 코드 클릭후 Alt + F2

어셈블리어 구문 삽입

바이너리 수정이아닌 분석 소스코드만 수정

 

 

Text View -> Layout Graph

Space bar 입력

text view상태에서 space를 입력하면 아래처럼 그래프 모드로 전환된다.

 

 

Stack Frame 확인하기

아래의 초록색 변수를 더블클릭하면 각 함수의 Stack Frame을 확인할 수 있고 각 변수들의 위치도 확인할 수 있다.

 

 

Jump to structure offset

이동하고 싶은 주소를 입력하면 해당 주소로 이동

단축키 : g

 

 

Search name

Ctrl + L

 

문자열 목록확인

Shift + F12

문자열 및 api 확인 가능

더블클릭시 해당 주소로 이동

이전단계로 돌아가기

ESC

ida에서 진행한 이동을 이전단계로 돌아간다.

 

xref call 따라가기

단축키  : x

특정함수 클릭 후 x를 입력하면 해당함수를 호출한 위치가 나온다. 더블클릭시 이동가능

 

함수 또는 변수 이름 바꾸기

단축키 : N

N입력시 아래처럼 함수명이나 이름을 변경할 수 있다.

 

북마크 기능

특정 주소 클릭 후 Alt + m

북마크 설명 설정

북마크 주소 목록 출력

Ctrl + M

북마크로 설정한 주소들이 출력된다.

 

 

IDA Debugging 모드

break point

F2

break point로 설정할 소스코드를 클릭하고 F2를 입력하면 아래사진처럼 bp가 설정된다.

Process run

F9

F9를 입력하면 process가 실행된다.

F7 : step into

F8 : step over

F5 : IDA View에서는 hexray 기능이고 debugging 모드에서는 run 기능이다.

Ctrl + F2 : 디버깅 종료

 

728x90

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

IDA Python  (0) 2021.02.06
728x90

디렉터리 관련 함수 및 그밖의 함수들

 

디렉터리의 생성과 소멸

디렉터리 생성함수

BOOL CreateDirectory(

   LPCTSTR lpPathName,

   LPSECURITY_ATTRIBUTES lpSecurityAttributes

);

1.lpPathName : 생성하고자 하는 디렉터리 이름을 지정한다.

2.lpSecurityAttributes : 보안 속성을 지정할 때 사용된다. 기본적으로 NULL을 전달해서 디폴트 보안속성을 지정한다.

 

디렉터리 소멸함수

BOOL RemoveDirectory(

   LPCTSTR lpPathName

);

1.소멸하고자 하는 디렉터리 이름 지정

 

CreateDirectory 첫번째 인자에 경로를 포함하지 않은 문자열만 전달할 시 해당 프로그램이 존재하는 곳에 디렉터리가 생성된다. 경로를 포함할 시 해당 경로에 디렉터리가 생성된다.

 

현재 디렉터리, 시스템 디렉터리, Windows디렉터리

현재 디렉터리(Current Directory)

현재 디렉터리는 프로그램이 존재하는 디렉터리이다.

초기에는 프로그램이 로드(Load)된 디렉터리로 설정되며, 이후 변경 가능하다.

->프로그램이 실행된 이후 현재 디렉터리는 언제든지 변경될 수 있다.

 

현재디렉터리 참조 함수

DWORD GetCurrentDirectory(

    DWORD nBufferLength,

    LPTSTR lpBuffer

);

1.nBufferLength : 두번째 인자로 전달된 버퍼의 길이를 지정한다.

2.pBuffer : 현재 디렉터리 정보를 저장할 버퍼의 주소값을 지정한다.

 

현재 디렉터리 변경함수

BOOL SetCurrentDirectory(

   LPCTSTR lpPathName

);

1.lpPathName : 변경하고자 하는 현재 디렉터리 정보를 지정한다.

 

시스템 디렉터리(System Directory) & Windows 디렉터리

시스템 디렉터리는 각종 라이브러리(DLL) 및 드라이버 파일처럼 Windows 시스템에 중요한 파일들이 존재하는 위치였고, Windows 디렉터리는 초기화 및 실행파일들이 존재하는 위치이다.

 

시스템 디렉터리와 Windows 디렉터리는 변경이 불가능 하며 변경해서는 안된다.

 

 시스템 디렉터리 위치 정보 확인 함수

UINT GetSystemDirectory(

    LPTSTR lpBuffer,

    UINT uSize

);

1.lpBuffer : 시스템 디렉터리 정보를 저장할 버퍼의 주소값을 지정한다.

2.uSize : 첫번째 인자로 전달된 버퍼의 길이를 지정한다.

 

윈도우 디렉터리 위치 정보 확인 함수

UINT GetWindowsDirectory(

   LPTSTR lpBuffer,

   UINT uSize

);

1.lpBuffer : Windows 디렉터리 정보를 저장할 버퍼의 주소값을 지정한다.

2.uSize : 첫번째 인자로 전달된 버퍼의 길이를 지정한다.

 

windef.h에 MAX_PATH가 정의되어있다. 이는 디폴트 최대경로 길이 248을 고려하여 선언된 매크로 상수이다. 따라서 파일이름(완전 경로를 포함하는)을 저장하기 위한 문자열 버퍼를 선언할 때 활용할 수 있다.

#define MAX_PATH 260

 

디렉터리에서 파일 찾기

파일의 위치정보나 특정 디렉터리 내에 존재하는 파일 정보를 얻고자 할 때 사용할 수 있는 함수들

 

SearchPath 함수는 문자열로 지정한 경로에 특정 파일이 존재하는지 확인하는 용도로 사용할 수 있다. 파일 이름을 인자로 하여 해당 파일이 저장되어 있는 완전경로를 얻고자 할 때에 사용할 수 있다.

 

DWORD SearchPath(

   LPCTSTR lpPath,

   LPCTSTR lpFileName,

   LPCTSTR lpExtension,

   DWORD nBufferLength,

   LPCTSTR lpBuffer,

   LPCTSTR* lpFilePart

);

1.lpPath : 대상 경로를 지정한다. 이 인자를 통해서 지정된 경로에서 파일을 찾게 된다. 인자로 NULL이 전달되면, 다음과 같은 순서로 파일을 찾게된다. 아래의 경로를 가리켜 표준 검색 경로라 한다.

1_실행 중인 프로그램이 로드(Load)된 디렉터리(실행 파일이 존재하는 디렉터리)

2_현재 디렉터리(Current Directory)

3_시스템 디렉터리(System Directory)

4_Windows 디렉터리

5_마지막으로 환경변수 PATH에 등록된 디렉터리

2.lpFileName : 찾고자 하는 파일 이름

3.lpExtension : 확장자를 지정하는 인자로서 첫번째 문자는 반드시 '.'으로 시작해야 한다. lpFileNAme이 확장자를 포함하고 있거나 확장자를 지정해 줄 필요가 없을 때에는 NULL을 지정한다.

4.nBufferLength : 완전 경로명을 저장할 버퍼의 길이를 요구한다.

5.lpBuffer : 완전 경로명을 저장할 버퍼의 주소를 지정한다.

6.lpFilePart : 함수 호출 결과로 얻게되는 완전경로 명(lpBuffer로 지정된 버퍼에 저장되는 완전경로명)의 마지막에는 파일 이름도 추가된다. 이 전달인자를 통해서 파일 이름이 저장된 위치 정보(포인터 값)를 얻게된다.

 

FindFirstFile 함수

HANDLE FindFirstFile(

   LPCTSTR lpFileName,

   LPWIN32_FIND_DATA lpFindFileData

);

1.lpFileName : 파일이나 디렉터리 이름 지정. 지정된 정보를 근거로 파일(혹은 디렉터리)정보를 얻게된다.

2.lpFindFileData : 발견된 파일이나 디렉터리 정보를 담을 WIN32_FIND_DATA 구조체 변수의 주소값을 전달한다.

 

FindFirstFile 함수의 반환 타입이 HANDLE이다. FindFirstFile 함수를 통해서 조건에 맞는 파일 중 가장 첫번째 검색된 파일 정보만 얻을 수 있다.

 

FindNextFile 함수

BOOL FindNextFile(

   HANDLE hFindFile, 

   LPWIN32_FIND_DATA lpFindFileData

);

1.hFindFile : FindFirstFile 함수 호출을 통해서 얻은 핸들을 전달한다. 

2.lpFindFileData : 발견된 파일이나 디렉터리의 정보를 담을 WIN32_FIND_DATA구조체 변수의 주소값을 전달한다.

 

BOOL FindClose(

   HANDLE hFindFile

);

1.hFindFile : FindFirstFile 함수를 통해서 얻은 핸들을 반환한다.

 

FindFirstFile 함수와 FindNextFile함수의 사용법은 LinkedList의 LFirst와 LNext함수 사용법과 유사하다.

FindFirstFile을 통해 조건에 부합하는 첫번째 파일에 대한 파일 정보를 두번째 인자에 저장한다.

이후 FindNextFile에 FindFirstFile함수 반환 핸들을 인자로 넣어 조건에 부합하는 다음 파일에 대한 정보를 받아온다.

이후 FindClose함수를 통해 FindFirstFile 함수 반환 핸들을 반환해 준다.

728x90
728x90

파일의 특성(Attribute) 정보 얻어오기

파일의 특성정보는 파일의 속성에서 확인할 수 있다.

파일의 특성 정보는 읽기전용, 숨김, 보관 등에 대한 정보이다. 이러한 특성 정보는 프로그램 코드 상에서도 변경 및 확인이 가능하다.

 

특성 정보 확인 함수

DWORD GetFileAttributes(

   LPCTSTR lpFileName

);

lpFileName : 특성 정보를 얻고자 하는 파일의 이름을 지정한다. 파일의 특성정보는 반환값을 통해서 얻게된다.

 

위 함수를 통해 파일 특성에 대한 정보들을 얻을 수 있다. 

 

파일 특성 정보의 표현 방식

위 그림은 32비트 자료형 DWORD을 보여주고 있다.

첫번째 비트가 1인 경우의 값 0x00000001을 "읽기 전용 특성"정보를 표현하는데 사용하기로 정의되어 있다.

 

두번째 인덱스 비트가 1인 경우에 해당하는 값 0x00000002는 "숨김 특성" 정보를 표현하는데 사용하기로 정의되어 있다.

 

위 두가지 특성정보 외에도 파일이 가질 수 있는 특성정보의 종류는 12가지정도가 더 된다.

 

따라서 반환값이 인덱스 기준으로 0번째 비트와 1번째 비트가 각각 1로 설정되어 있으면, 그 파일은 읽기 전용이자 숨김 특성까지 지닌 파일이 된다.

프로그램 구현의 용이성을 위해서 읽기 전용특성과 숨김 특성을 다음과 같이 매크로 상수로 정의해 놓고 있다.

#define FILE_ATTRIBUTE_READONLY 0x00000001

#define FILE_ATTRIBUTE_HIDDEN 0x00000002

 

파일 특성 정보 변경 함수

BOOL SetFileAttributes(

  LPCTSTR lpFileName,

  DWORD dwFileAttributes

);

1.lpFileName : 특성 정보를 변경하고픈 파일의 이름을 지정한다.

2.dwFileAttributes : 변경할 특성 정보를 전달한다.

 

GetFileAttributes함수와 SetFileAttributes 함수는 일반적인 파일 처리 함수와 차이를 보인다. 그것은 핸들을 필요로 하지 않는다는 점이다. 대신 위치 경로를 포함하는 파일의 이름을 필요로 한다.

 

 

파일의 특성(Attribute) 정보 핸들로부터 얻어오기 + α

BOOL GetFileInformationByHandle(

  HANDLE hFile,

  LPBY_HANDLE_FILE_INFORMATION lpFileInformation

);

1.hFile : 정보를 얻고자 하는 파일의 핸들을 지정한다.

2.LPBY_HANDLE_FILE_INFORMATION : 구조체 BY_HANDLE_FILE_INFORMATION의 포인터 타입이다. 여기로 전달되는 주소의 변수에 파일 정보가 채워진다.

 

GetFileInformationByHandle 함수를 통해서 얻는 정보는 파일의 시간 정보, 파일의 크기정보, 파이르이 시스템 정보들을 얻을 수 있다.

BY_HANDLE_FILE_INFORMATION 선언

typedef struct _BY_HANDLE_FILE_INFORMATION

{

   DWORD dwFileAttributes;

   FILETIME ftCreationTime;

   FILETIME ftLastAccessTime;

   FILETIME ftLastWriteTime;

   DWORD dwVolumeSerialNumber;

   DWORD nFileSizeHigh;

   DWORD nFileSizeLow;

   DWORD nNumberOfLinks;

   DWORD nFileIndexHigh;

   DWORD nFileIndexLow;

}BY_HANDLE_FILE_INFORMATION, *BYHANDLE_FILE_INFORMATION;

 

GetFileInformationByHandle 함수를 이용하여 파일에 대한 정보를 가져와 각 멤버에 접근하여 관련정보를 알 수 있다.

 

파일의 경로(Path)정보 얻어오기

DWORD GetFullPathName(

  LPCTSTR lpFileName,

  DWORD nBufferLenth,

  LPTSTR lpBuffer,

  LPTSTR* lpFilePart

);

1.lpFileName : 완전 경로(Full Path)를 확인하고자 하는 파일 이름 전달.

2.nBufferLength : 완전 경로를 저장할 버퍼에 저장 가능한 문자열 길이를 지정한다. 버퍼의 바이트 단위 크기가 아니라 저장가능한 문자열의 길이이다.

3.lpBuffer : 완전 경로를 저장할 버퍼의 주소값을 지정한다.

4.lpFilePart : 완전 경로가 문자열로 버퍼에 저장된 이후, 버퍼의 특정 위치를 가리키는 포인터 값이 저장된다. 마지막 역 슬래쉬(\)다음 번지 주소값이 저장된다.

 

파일 포인터의 이동

ANSI표준함수 fseek에 해당하는 windows 시스템 함수는 SetFilePointer이다.

 

DWORD SetFilePointer(

   HANDLE hFile,

   LONG lDistanceToMove,

   PLONG lpDistanceToMoveHigh,

   DWORD dwMoveMethod

);

1.hFile : 파일 포인터 위치를 이동시킬 대상 파일의 핸들을 지정한다.

2.lDistanceToMove : 이동시킬 거리를 지정한다. 만약에 64비트 기반으로 구현한다면, 하위 4바이트 정보를 표현하게 된다.

3.lDistatnceToMoveHigh : 64비트 기반의 대용량 파일에서만 의미를 지닌다. 상위 4바이트 정보를 표현하게 된다. 32비트 기반에서는 NULL을 전달한다.

4.dwMoveMethod : 파일 포인터 이동시 기준이 되는 위치를 지정한다.

파이르이 가장 앞부분을 기준으로 설정하고자 할 때에는 FILE_BEGIN을 인자로 전달한다. fseek 함수의 SEEK_SET에 해당한다. SEEK_CUR과 같은 의미로는 FILE_CURRENT, SEEK_END와 같은 의미로는 FILE_END를 인자로 전달한다.

 

SetFilePointer함수를 이용하여 파일 포인터 이동 후 ReadFile함수를 이용하여 파일을 읽어들이면 설정한 파일 포인터 부터 읽어들인다.

 

32비트 기반의 파일에서는 3번째 인자에 NULL을 전달하고, 64비트 기반의 대용량 파일에서는 상위 4바이트 정보를 받을 변수의 주소 값을 인자로 넣어준다.

 

 

728x90
728x90

기본적인 파일 처리 함수들

Windows 기반 파일 입출력 함수

 

파일 열기 : CreateFile

HANDLE CreateFile(

  LPCTSTR lpFileName,

  DWORD dwDesiredAccess,

  DWORD dwShareMode, 

  LPSECURITY_ATTRIBUTES lpSecurityAttributes,

  DWORD dwCreationDisposition,

  DWORD dwFlagsAndAttributes,

  HANDLE hTemplateFile

);

1.lpFileName : 개방(Open)할 파일 이름을 지정한다.

2.dwDesiredAccess : 읽기/쓰기 모드를 지정한다. or(|)연산으로 결합 가능하다.

 - GENERIC_READ 읽기모드 지정

 - GENERIC_WRITE 쓰기모드 지정

3.dwShareMode : 파일 공유방식을 지정한다.

 - 0 : 다른 프로세스에 절대 공유 불가, 이미 개방된 파일은 중복 개방 불가

 - FILE_SHARE_READ : 다른 프로세스에서 이 파일에 동시 읽기 접근 가능

 - FILE_SHARE_WRITE : 다른 프로세스에서 이 파일에 동시 쓰기 접근 가능, 단 동시에 같은 영역에 데이터를 쓰는 문제를 피해야 함.

4.lpSecurityAttributes : 보안 속성을 지정한다. 핸들을 자식 프로세스에게 상속할 것인지 말 것인지를 결정하기 위한 용도로 사용할 수 있다. 디폴트 보안 속성을 지정하고자 하는 경우 NULL 전달

5.dwCreationDisposition : 파일이 생성되는 방법을 지정한다.

- CREATE_ALWAYS : 항상 새 파일을 생성한다.

- CREATE_NEW : 새 파일 생성, 같은 이름의 파일이 존재하면 생성 실패!

- OPEN_ALWAYS : 기존 파일 개방, 없으면 새로 생성

- OPEN_EXISTING : 기존 파일 개방, 존재하지 않으면 함수 호출

6.dwFlagsAndAttributes : 파일의 특성정보를 설정한다. 둘이상의 특성 정보는 OR(|) 연산자를 통해서 지정할 수 있으며, 기본적으로 FILE_ATTRIBUTE_NORMAL 값 사용

7.hTemplateFile : 기존에 존재하는 파일과 동일한 특성을 가지는 새파일을 만들 때 사용되는 전달인자이다.

 

위 함수 호출시, 파일의 핸들이 반환된다.

 

CreateFile로 파일을 열고 파일을 종료할 때에는 여느 커널 오브젝트의 핸들과 마찬가지로 CloseHandle 함수를 호출하면 된다.

CreateFile 함수는 핸들을 반환한다. 해당 핸들의 커널오브젝트에는 파일에 대한 정보로 가득 차있을 것이다.

 

파일 읽기 & 쓰기 함수

파일 읽기 함수

BOOL ReadFile(

   HANDLE hFile,

   LPVOID lpBuffer,

   DWORD nNumberOfBytesToRead,

   LPDWORD lpNumberOfBytesRead,

   LPOVERLAPPED lpOverlapped

);

1.hFile : 데이터를 읽을 파이르이 핸들을 지정한다.

2.lpBuffer : 읽어들인 데이터를 저장할 버퍼(배열, 메모리)의 주소(포인터)를 지정한다.

3.nNumberOfBytesToRead  : 파일로 부터 읽고자 하는 데이터의 크기를 바이트 단위로 지정한다.

4.lpNumberOfBytesRead : 실제 읽어들인 데이터 크기를 얻기위한 변수의 주소를 지정한다.

 

파일 쓰기 함수

BOOL WriteFile(

  HANDLE hFile,

  LPCVOID lpBuffer,

  DWORD nNumberOfBytesToWrite,

  LPDWORD lpNumberOfBytesWritten,

  LPOVERLAPPED lpOverlapped

);

1.hFile : 데이터를 저장할 파일의 핸들을 지정한다.

2.lpBuffer : 데이터를 저장하고 있는 버퍼(배열, 메모리)의 주소(포인터)를 지정한다.

3.nNumberOfBytesToWrite : 파일에 저장하고자 하는 데이터 크기를 바이트 단위로 지정한다.

4.lpNumberOfBytesWritten : 파일에 실제 저장된 데이터 크기를 얻기위해 변수의 주소를 지정한다.

 

파일의 시간 정보 얻어오기

Windows에서는 파일의 만든날짜, 수정한 날짜(마지막으로 수정한 날짜), 엑세스한 날짜(마지막으로 엑세스한 날짜)등의 정보를 확인할 수 있다.

 

프로그램상에서 위와같은 정보를 얻거나 변경하기 위한 함수

BOOL GetFileTime(

  HANDLE hFile,

  LPFILETIME lpCreationTime,

  LPFILETIME lpLastAccessTime,

  LPFILETIME lpLastWriteTime

);

1. hFile : 시간 관련 정보를 얻을 대상 파일의 핸들을 지정한다.

2. lpCreationTime : 파일이 생성된 시간을 얻기 위해 FILETIME 구조체 변수의 주소 값을 전달한다. NULL을 전달하는 것도 가능하다.

3. lpLastAccessTime : 파일의 마지막 접근 시간을 얻기위해 FILETIME 구조체 변수의 주소값을 전달한다. NULL을 전달하는 것도 가능하다.

4.lpLastWriteTime : 파일의 마지막 데이터 갱신(덮어쓰기 포함) 시간을 얻기위해 FILETIME 구조체 변수의 주소값을 전달한다. NULL을 전달하는 것도 가능하다.

 

FILTIME 구조체

typedef struct _FILETIME{

  DWORD dwLowDataTime,

  DWORD dwHighDataTime,

}FILETIME, *PFILETIME

 

FILETIME 구조체는 시간 정보를 나타내는 8바이트(DWORD *2)자료형이다. 이 구조체는 UTC 기반으로 시간을 표현한다.

 

UTC:Coordinated Universal Time의 간략한 표현으로써 세계시간의 기준을 만들기 위해 정의된 시간이다.

 

FileTimeToSystemTime 함수의 호출로 변경되는것은 단지 포맷이다.

SYSTEMTIME 구조체 선언

typedef struct _SYSTEMTIME{

  WORD wYear,

  WORD wMonth,

  WORD wDayOfWeek,

  WORD wDay,

  WORD wHour,

  WORD wMinute,

  WORD wSecond,

  WORD wMilliseconds

}SYSTEMTIME, *PSYSTEMTIME;

FileTimeToSystemTime 함수를 통해 FILETIME 표맷을 SYSTEMTIME포맷으로 변경한다.

 

SystemTimeToTzSpecificLocalTime 함수는 UTC를 지역별, 국가별 시간대(Time zone)로 변경하는 기능을 지닌다.

SystemTimeToTzSpecificLocalTime(NULL, &stCreateUTC, &StCreateLocal)

첫번째 인자는 변경하고자 하는 시간대에 대한 정보이다. NULL이 전달되면 현재 시스템의 시간대 정보가 기준이 된다. 두번째 전달인자는 변경할 대상이 되는 UTC 기반정보이고, 세번째 전달인자는 변환된 시간정보가 채워질 변수의 주소값이다. 

 

GetFileTime 함수를 이용하여 파일의 시간정보를 얻어올 수 있고, SetFileTime 함수를 이용하여 파일의 시간정보를 변경할 수도 있다.

 

파일 사이즈 얻어오기

일반적으로 파일 사이즈를 얻어오는 코드

FILE * fp = fopen("abc.dat", "rb");

 

fseek(fp, 0, SEEK_END);

DWORD sizeofFile = ftell(fp);

위 방법은 fseek 함수로 파일 포인터를 끝으로 이동시킨 다음, ftell 함수를 호출해서 현재 위치정보를 얻어온다. 파일 포인터가 파일의 끝으로 설정되어 있기 때문에 현재 위치 정보는 파일의 크기가 된다.

 

Windows 시스템함수는 파일 크기를 직접 계산해서 반환하는 함수를 제공한다.

DWORD GetFileSize(

  HANDLE hFile,

  LPDWORD lpFileSizeHigh

);

1.hFile : 파일 핸들을 지정한다. 이 핸들이 가리키는 파이르이 크기정보를 얻게된다.

2.lpFileSizeHigh : 반환 타입을 보면 4바이트 DWORD로 선언되어있다. 따라서 4G바이트 이상의 파일 크기를 반환값으로 얻는것은 불가능하다. 4G바이트 이상되는 파일의 크기를 얻을 때 사용된다. 이 전달인자를 통해 4G바이트를 넘는 파일의 상위 4바이트 정보를 얻을 수 있다.

 

GetFileSize 함수는 4G바이트 이상의 파일에 대해서 상위 4바이트와 하위 4바이트를 각각 다른 경로를 통해서 얻어야 한다. 그러나 GetFileSizeEx함수를 사용하면 한번에 얻을 수 있다.

BOOL GetFileSizeEx(

  HANDLE hFile,

  PLARGE_INTEGER lpFileSize

);

1.hFile : 크기를 얻고자 하는 파일의 핸들을 지정한다.

2.lpFileSize : 파일 크기를 저장하기 위한 변수의 포인터(주소 값)을 인자로 전달한다. PLARGE_INTEGER는 LARGE_INTEGER의 포인터 타입이고, LARGE_INTEGER는 다음과 같이 선언되어 있다. 중요한 사실은 4바이트가 아니라 8바이트 자료형이라는 것이다.

 

typedef union _LARGE_INTEGER

{

   struct {

   DWORD LowPart;

   LONG HighPart;

   };

   LONGLONG QuadPart;

}LARGE_INTEGER, *PLARGE_INTEGER;

728x90
728x90

보통 malloc을 호출하였다면 반환되는 주소가 NULL이 아닌지 확인해야 하고, 프로세스나 쓰레드, 그리고 동기화 오브젝트들을 생성할 때에도 해당 함수 호출 후에 오류가 발생했는지 확인하는 코드를 삽입해야한다.

 

프로그램 실행시 발생하는 문제점 대부분을 예외라고 인식하자.

처리 불가능한 문제가 발생하면 프로그램은 종료하게 되는데, 문제가 발생한다고 해서 프로그램이 종료된다면 이 또한 문제가 아닐 수 없다. 외부적인 요소에 의한 문제점이든 내부적인 요소에 의한 문제점이든 구조적 예외처리 기법 관점에서 대부분 해결 가능해야한다.

프로그램 실행시 예측 가능한 대부분의 문제점을 예외로 간주하고, 처리가능하도록 프로그램을 구현해야 한다.

 

하드웨어 예외와 소프트웨어 예외

하드웨어 예외 : 하드웨어에서 인식하고 알려주는 예외

ex) 정수를 0으로 나누는 연산은 하드웨어 예외를 발생시킨다.

사칙연산을 수행하는 주체는 CPU이다. 연산을 수행하는 과정에서 정수를 0으로 나누는 요청이 들어올 경우 문제가 있다는 신호를 운영체제에 전달할 것이다. 이를 통해 운영체제는 예외 상황이 발생했음을 알고, 구조적 예외 처리 메커니즘에 의해 예외상황이 처리되도록 일을 진행시킨다.

 

소프트웨어 예외 : 소프트웨어에서 감지하는 예외이다.

작성한 프로그램 뿐만아니라 운영체제도 포함한다. 소프트웨어 예외는 프로그래머가 직접 정의할 수 있는 예외이다.

 

종료 핸들러(Termination Handler)

SEH(Structured Exception Handling), 즉 구조적 예외처리 메커니즘은 기능적 특성에 따라 크게 두가지로 나뉜다.

1.종료 핸들러(Termination Handler)

2.예외 핸들러(Exception Handler)

 

종료 핸들러의 기본 구성과 동작 원리

종료핸들러에서 사용되는 키워드 : __try, __finally

 

__try는 예외 핸들러에서도 사용된다.

__try와 __finally는 다음과 같은 형태로 구성된다.

 

__try

{

     //code

}

__finally

{

  //종료 처리

}

__try만 올 수 없고, __finally만 올 수도 없다. 이 둘 사이에는 다른 어떠한 코드도 삽입될 수 없다.

 

위 소스코드는 __try블록을 실행하면 반드시 __finally블록을 실행하라는 의미를 가진 코드이다.

__try 내부에 return문이 존재해도 return이 실행되기에 앞서서 __finally를 실행시키는것이 컴파일러에 의해 보장되어 있다.

컴파일러는 return문에 의해 반환되는 값을 임시 변수(컴파일러가 만들어내는 변수)에 저장하고, __finally블록을 실행한다음, 값을 반환하도록 바이너리 코드를 구성한다.

 

ExitProcess, ExitThread, exit 함수에 의한 프로세스 또는 쓰레드의 강제 종료는 __finally블로그이 실행으로 이어지지 않는다.

 

종료 핸들러 활용

종료핸들러를 보면 문제의 발생 유무에 상관없이 반드시 실행되어야 하는 코드를 실행시킬 수 있다.

예를들어 파일의 개방과 이에따른 종료상황이다.

개방된 파일을 적절히 닫아주지 못한다면, 데이터의 일부가 손실될 수도 있기 때문이다.

동적할당도 같은 맥락에서 생각할 수 있다. 동적할당한 메모리를 해제하지 않고 함수를 빠져나가게 된다면 할당된 메모리는 유출된 상태로 남아있게 된다.

 

종료 핸들러의 경우 "무조건 실행"이라는 특성을 가진다.

 

예외 핸들러(Exception Handler)

예외 핸들러는 "예외 상황 발생시 선별적 실행"

 

예외 핸들러와 필터(Exception Handler & Filter)

예외 처리 핸들러 기본 구성

__try

{

    //예외 발생 경계

}

__except(예외 처리 방식)

{

   //예외 처리를 위한 코드

}

 

__try 블록은 예외 상황이 발생 가능한 영역을 묶는데 사용된다.

__try 블록에서 예외 상황이 발생하면 __except블록에서 이 상황을 처리하게 된다.

 

__except문의 "예외 처리 방식"부분을 가리켜 예외 필터(Exception Filter)라 한다.

예외 필터를 통해 예외처리 메커니즘의 동작 방식을 결정한다.

예외 필터값은 총 세가지 이다. 즉 예외처리 핸들러가 동작하는 방식이 세가지라는 것이다.

 

예외 필터를 EXCEPTION_EXECUTE_HANDLER 로 선언할 경우, __try 블록의 예외 발생 라인 이후를 건너 뛰게 된다.

 __try블록의 범위는 예외 발생 가능영역 뿐만 아니라, 예외처리 이후에 실행위치도 고려해서 결정해야만 한다.

 

__try블록내에서 호출하는 함수내에서 발생하는 예외도 __try블록 내에서 발생하는 예외로 인식한다.

 

예외의 종류와 예외를 구분하는 방법

예외 상황에 따라서 처리하는 방식이 달라져야 하기 때문에 발생한 예외의 종류를 구분할 수 있어야 한다.

어떠한 종류의 예외가 발생했는지를 확인하기 위해서 GetExceptionCode함수를 호출하면 된다.

DWORD GetExceptionCode(void)

 

예외 정보

EXCEPTION_ACCESS_VIOLATION과 EXCEPTION_INT_DIVIDE_BY_ZERO는 시스템에서 발생할 수 있는 예외로서 이미 약속된 예외의 종류에 해당한다.

EXCEPTION_ACCESS_VIOLATION : 메모리 참조 오류

EXCEPTION_INT_DIVIDE_BY_ZERO : 정수를 0으로 나누는 예외

EXCEPTION_STACK_OVERFLOW :  스택 메모리가 넘쳤을 때(부족할 때)발생하는 예외

 

GetExceptionCode 함수는 __except 블록 내에서나 예외 필터 표현식을 지정하는 위치에서만 호출이 가능하다는 특징이 있다.

 

EXCEPTION_CONTINUE_EXECUTION & EXCEPTION_CONTINUE_SEARCH 예외 필터 표현식

EXCEPTION_CONTINUE_EXECUTION의 표현식의 경우 __except 블록의 내부코드를 실행하지 않고 예외가 발생한 그 위치로 이동하여 실행을 이어나간다.

 

EXCEPTION_CONTINUE_SEARCH 란 다른곳에 있는 예외 핸들러를 통해서 예외를 처리하라는 것이다.

예외 핸들러를 찾을 때는 함수가 호출된 순서(스택이 쌓여있는 순서)를 바탕으로 예외핸들러를 찾게된다.

EXCEPTION_CONTINUE_SEARCH은 예외가 처리되어야 하는 위치를 별도로 지정하기 위한 용도로 사용된다.

 

소프트웨어 예외(Software Exception)의 발생

void RaiseException(

    DWORD dwExceptionCode,

    DWORD dwExceptionFalgs,

    DWORD nNumberOfArguments,

    const ULONG_PTR * lpArguments

);

1.dwExceptionCode : 발생시킬 예외의 형태를 지정한다.

2.dwExceptionFlags : 예외 발생 이후의 실행방식에 있어서 제한을 둘때 사용한다.

3.nNumberOfArguments : 추가정보의 개수를 지정한다.

4.lpArguments : 추가정보를 전달한다.

위 함수는 예외 발생을 알리기 위한 용도로 사용된다.

이 함수가 호출되면 SEH 메커니즘이 작동되면서, 예외처리가 전개된다.

 

GetExceptionCode 함수호출을 통해 얻게되는 예외코드를 통해서 부가적인 정보를 얻을 수 있다.

 

RaiseException의 첫번째 인자로 EXCEPTION_NONCONTINUEABLE이 올수있다.

EXCEPTION_NONCONTINUABLE은 예외가 발생하면 프로그램을 종료시키는 인자이다. EXCEPTION_CONTINUE_EXECUTION은 예외처리흐름을 막는 용도로 사용한다.

 

GetExceptionInformation

예외발생시 GetExceptionCode가 반환해 주는 정보보다 더 많은 정보를 얻기 원한다면, GetExceptionInformation 함수호출을 고려할 수 있다. 이 함수는 "예외 필터 표현식을 지정하는 부분"에서만 호출 가능하다.

 

LPEXCEPTION_POINTERS GetExceptionInformation(void);

GetExceptionInformation 함수가 호출되면, EXCEPTION_POINTERS 구조체 변수의 주소값이 반환된다.

 

EXCEPTION_POINTERS 구조체

typedef struct _EXCEPTION_POINTERS

{

    PEXCEPTION_RECORD ExceptionRecord;

    PCONTEXT ContextRecord;

 }EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

 

EXCEPTION_POINTERS 구조체는 EXCEPTION_RECORD구조체 포인터 PEXCEPTION_RECORD와 CONTEXT의 구조체 포인터 PCONTEXT로 구성되어 있음을 알 수 있다.

즉 예외발생후 GetExceptionInformation 함수가 호출되면 다음과 같은 구조로 구성된 예외 관련 정보를 얻게된다.

EXCEPTION_POINTERS 구조체 변수에는 두가지 정보가 채워진다. 하나는 예외 자체에 대한 정보가, 또 하나는 프로세서(CPU)의 레지스터 데이터를 비롯한 프로세서의 종속적인 정보가 채워진다.

728x90
728x90

1.물리 주소(Physical Address)

임베디드 시스템과 범용 시스템의 가장 큰 차이점은 하드디스크의 존재유무 이다.

범용 시스템은 windows 운영체제에서부터 각종 소프트웨어를 하드디스크에 저장해 놓고, 전원이 인가되면 저장된 소프트웨어를 기반으로 동작하게 한다.

 

임베디드 시스템은 하드디스크가 없다. 하드디스크가 없어서 운영체제를 비롯한 각종 소프트웨어를 플래쉬 메모리에 저장하고 전원이 들어오면 플래쉬 메모리 데이터를 RAM에 올린다.

 

프로그램이 메인메모리에 올라갈때 메인메모리의 크기에 따라 접근 가능한 영역이 정해진다. 

ex) 램 크기가 16M라면 CPU가 접근가능한 메인 메모리 영역은 0번지~(16 * 1024 *1024)-1 번지이다.

 

위와같은 주소는 실제 물리적인 메인 메모리의 주소 범위에 해당하며, 이렇게 주소를 할당하는 것을 가리켜 물리적 주소 지정(Physical Addressing)이라 한다.

 

물리적 주소 지정은 메인메모리 크기에 따라 지정가능한 주소의 범위가 결정된다.

물리적 주소 지정은 CPU 입장에서 접근 가능한 주소의 범위가 제한된다는 뜻이다. 또한 프로그래머가 할당할 수 있는 주소 범위가 제한적이라는 뜻도된다.

 

메인 메모리에는 항상 운영체제가 돌아가고 있다. 메인 메모리의 시작부터 일정영역을 커널영역이라 하고 컴퓨터가 켜지면 해당 영역에 운영체제(커널 포함)가 적재된다.

그렇기 때문에 메인 메모리 크기가 16M바이트 라면 이 범위 안에서 운영체제와 프로그램을 로딩(Loading)하고 프로그램 실행과정에서 메모리를 할당해야만 한다.

 

가상주소(Virtual Address)시스템 1

32비트 시스템에서 프로세스 생성시 4G바이트의 메모리를 할당받을 수 있다. 할당 받을 수 있는 메모리에 비해 메인 메모리는 매우 작다.

따라서 프로세스 생성시 할당받는 4G바이트는 실제로 존재하지 않는 가사으이 주소이다.

이렇게 주소를 지정하는 것을 가상 주소 지정(Virtual Addressing)이라 하며, 가상 주소 지정을 통해서 할당받는 4G바이트를 가리켜 가상 메모리 공간(Virtual Address Space)이라 한다.

 

MMU(Memory Management Unit) : MMU는 CPU와 하나로 패키징 되어있는 장치이다. MMU는 16K바이트 밖에 존재하지 않는 메모리를 64K바이트가 존재하는 것처럼 CPU가 느끼도록 컨트롤 하는 역할을 한다.(가상 주소 시스템)

 

페이지&페이지 프레임

프로그램의 스페이셜 로컬리티 특성을 반영하여 블록 단위로 메모리에 매핑된다. 블록을 메인 메모리 입장에서는 페이지 프레임(Page Frame)이라 하고, 소프트웨어 입장에서는 페이지(Page)라 한다.

페이지 프레임은 실제 메인 메모리를 의미하고, 페이지는 가상 메모리 블록을 의미한다. 페이지 프레임과 페이지의 크기는 일치한다.

 

 위 그림을 보면 현재 가상메모리 0K-4K가 물리메모리에 매핑되어있다.(MMU 에 의해 매핑)CPU가 0K - 4K 사이에 존재하는 데이터를 요구할 경우 MMU는 매핑된 물리 메모리를 참조해서 데이터를 전송해준다.

 

가상 주소(Virtual Address) 시스템 2

메모리 부족 문제 해결

하드디스크도 램(RAM)과 비교해서 속도를 제외하면 그 기능에 있어서 부족함이 없는 메모리이다.

하드디스크를 이용하여 램의 메모리 부족 문제를 해결할 수 있다. 스왑파일(Swap File)이라는 개념은 램(RAM)에 해당하는 메인 메모리를 하드 디스크로 까지 확장한 개념이다.

스왑파일을 통해 메인메모리 부족 문제를 해결해 줄 수 있다.

 

메인메모리의 부족한 부분은 스오바파일을 통해 하드디스크로 해결할 수 있다.

 

 

 여러가지 프로세스를 실행시킬때 각 프로세스의 전환을 프로세스에 해당하는 스왑파일 전환으로 실행시킨다.

프로세스 A실행중 프로세스 B 를 실행시킬 때 램에 존재하는 프로세스 A의 실행을 위한 데이터 모두를 프로세스 A의 스스왑파일에 저장하고, 프로세스 B 실행을 위한 데이터를 프로세스 B의 스왑파일로 부터 램에 가져다 놓는다.

페이징 기법은 하나의 프로세스 내에서 개별 페이지들을 관리하는 기법이다.

스왑핑은 메인 메모리에 실행중인 프로세들을 관리하는 기법이다.

 

페이징기법을 통해 작은 메인메모리가 프로세스의 모든 주소에 접근할 수 있게 하였다.

추가적으로 메모리 스왑핑(Swapping)을 통해서 여러 프로세스가 작은 메인메모리에서 실행될수 있게 하였다.

위 두 기법을 통해 효율적으로 메모리를 참조할 수 있다.

 

*메모리 스왑핑 : 프로세스 실행에 필요한 데이터를 하드디스크에 저장(Store)하고 실행시킬 프로세스를 스왑파일에서 로드(LOAD)함

  

728x90

'Programming > Windows System Programming' 카테고리의 다른 글

파일 I/O 와 디렉터리 컨트롤(1)  (0) 2020.08.16
SEH(Structured Exception Handling)  (0) 2020.08.10
캐쉬(cache)와 캐쉬 알고리즘  (0) 2020.08.07
메모리 계층  (0) 2020.08.07
쓰레드 풀링(Pooling)  (0) 2020.08.05
728x90

컴퓨터 프로그램의 일반적인 특성

컴퓨터 프로그램을 유심히 관찰해 보면, 공통적으로 지니는 일반적인 특성이 하나 존재한다.

 

대부분의 함수가 특정 연산을 하기위해 지역변수를 선언한다. 이러한 지역변수의 특성은 선언 및 초기화 이후 다양한 값으로 변경도 되고, 값을 얻기 위한 참조도 빈번하게 일어난다는 것이다. 이러한 특성을 템퍼럴 로컬리티(Temporal Locality)라 한다.

 

템퍼럴 로컬리티(Temporal Locality)란, 프로그램 실행시 한번 접근이 이뤄진 주소의 메모리 영역은 자주 접근하게 된다는 프로그램 특성을 표현할 때 사용하는 말이다.

 

스페이셜 로컬리티(Spatial Locality)란, 프로그램 실행 시 접근하는 메모리 영역은 이미 접근이 이루어진 영여그이 근처일 확률이 높다는 프로그램 성격을 표현할 때 사용하는 말이다.

즉 0x12번지 메모리에 접근 했다면, 다음번 메모리 접근은 그 주소(0x12 번지)에서 멀리 떨어지지 않은 곳일 확률이 높다는 프로그램의 성격을 표현한다.

 

위와같은 특성들을 고려하여 구현된 코드를 캐쉬 프렌드리 코드(Cache Friendly Code)라 한다.

 

캐쉬 알고리즘

 ALU연산 과정중 필요한 데이터가 있다면 이를 레지스터로 이동시켜야 한다. 필요한 데이터가 0x1000번지에 존재하는 데이터라면 이 주소에 해당하는 데이터를 레지스터로 가져오기 위해서 데이터가 존재하는 곳을 찾아봐야 한다. L1캐쉬에 해당 데이터가 존재하는 곳을 찾아봐야한다.

 

 L1캐쉬에 찾는 데이터가 존재할 경우 캐쉬 힛(Cache Hit)이 발생했다고 하며, 이 데이터를 레지스터로 이동시킨다.

L1캐쉬에 데이터가 존재하지 않을 경우 이를 가리켜 캐쉬 미스(Cache Miss)가 발생했다 하고, 캐쉬 미스가 발생하면 L2캐쉬에서 해당 데이터를 가져오게 된다.

(L2캐쉬에 존재하지 않으면 메인메모리에서, ->하드디스크까지 단계별로 탐색한다.)

 

 캐쉬와 메인메모리, 하드디스크 간에 데이터의 이동은 블록단위로 진행이 된다.

블록단위로 전송을 함으로써 스페이셜 로컬리티(Spatial Locality)의 특성을 성능 향상에 십분 활용하게 된다.

->블록 단위란 필요한 주소의 데이터만을 가져오는 것이 아니라 근처의 주소들을 포함하여 하나의 '블록'의 단위로 데이터를 전송하는 것이다.

 

위의 캐쉬 알고리즘 그림을 보면 메모리의 피라미드 구조상 아래로 내려갈수록 블록 크기는 커지게 된다. 이를 통해 아래에 존재하는 메모리 일수록 접근횟수를 줄이는 효과가 있다. 아래에 존재하는 메모리 일수록 속도가 느리기 때문에 접근 횟수를 줄이는 것이 성능향상에 많은 도움이 된다.

 

캐쉬미스가 발생할때 L1캐쉬에서  L2캐쉬로 부터 데이터 블록을 읽어 들일 때 저장할 공간이 없는 경우가 있다.

꽉차 있는 L1캐쉬에 데이터를 저장하려면 당연히 기존에 저장한 데이터를 밀어내야 하는데 이때 블록 교체 알고리즘에 의해서 데이터를 밀어내게 된다.

블록교체 알고리즘은 캐쉬 교체 정책(Cache's Replacement Policy)에 따라서 달라질 수 있다.

가장 보편적인 블록교체 알고리즘은 LRU(Least-Recently Used)알고리즘 이다. 이 알고리즘은 가장 오래전에 참조된 블록을 밀어내는 알고리즘이다.

 

캐쉬 프렌드리 코드(Cache Friendly Code)작성 기법

템퍼럴 로컬리티와 스페이셜 로컬리티의 특성을 이용해야한다.

ex)

...

int total = 0;

 

for(int i=0; i<10; i++)

{

    for(int j=0; j<10;j++)

    {

        total += arr[j][i];

    }

}

...

위 코드를 보면 10x10배열을 모두 더하는 코드라는 것을 알 수 있다.

 

변수 total의 값이 빈번하게 갱신되고 있다는 점에서 템퍼럴 로컬리티는 만족이된다.

그러나 배열의 접근이 열 단위로도 이루어지는 점에서 스페이셜 로컬리티를 만족시키지 못한것이다.

행 단위접근이던 열단위 접근이던 스페이셜 로컬리티를 어느정도 만족하지만, 배열의 크기가 커질수록 스페이셜 로컬리티의 만족도는 떨어지게 된다.

그러나 배열의 크기와 상관없이 행단위로 접근한다면 그것은 스페이셜 로컬리티를 만족시킨다.

 

 

 

 

 

 

 

 

 

 

 

 

728x90

'Programming > Windows System Programming' 카테고리의 다른 글

SEH(Structured Exception Handling)  (0) 2020.08.10
가상 메모리(Virtual Memory)  (0) 2020.08.08
메모리 계층  (0) 2020.08.07
쓰레드 풀링(Pooling)  (0) 2020.08.05
타이머(Timer) 기반 동기화  (0) 2020.08.03

+ Recent posts