이것은 사용자가 2024-10-31 12:28에 https://maldevacademy.com/modules/7?all_modules=1을(를) 위해 저장한 이중 언어 스냅샷 페이지로, 몰입형 번역에 의해 제공된 이중 언어 지원이 있습니다. 저장하는 방법을 알아보세요?
Module 7 - Introduction To The Windows API
모듈 7 - Windows API 소개
Progress Toggle
Screen Width
Objectives
Terminal

Introduction To The Windows API
Windows API 소개

Introduction 소개

The Windows API provides developers with a way for their applications to interact with the Windows operating system. For example, if the application needs to display something on the screen, modify a file or query the registry all of these actions can be done via the Windows API.
Windows API는 개발자에게 응용 프로그램이 Windows 운영 체제와 상호 작용할 수 있는 방법을 제공합니다. 예를 들어 응용 프로그램이 화면에 무언가를 표시해야 하는 경우 파일을 수정하거나 레지스트리에 쿼리하는 등 모든 작업은 Windows API를 통해 수행할 수 있습니다.

The Windows API is very well documented by Microsoft and can be viewed here.
Windows API는 Microsoft에 의해 잘 문서화되어 있으며 여기에서 볼 수 있습니다.

Windows Data Types Windows 데이터 유형

The Windows API has many data types outside of the well-known ones (e.g. int, float). The data types are documented and can be viewed here.
Windows API에는 잘 알려진 데이터 유형(예: int, float) 이외의 많은 데이터 유형이 있습니다. 데이터 유형은 문서화되어 있으며 여기서 볼 수 있습니다.

Some of the common data types are listed below:
일반적인 데이터 유형 중 일부는 다음과 같습니다.

  • DWORD - A 32-bit unsigned integer, on both 32-bit and 64-bit systems, used to represent values from 0 up to (2^32 - 1).
    DWORD - 32비트 및 64비트 시스템 모두에서 0부터 (2^32 - 1)까지의 값을 나타내는 데 사용되는 32비트 부호 없는 정수입니다.
DWORD dwVariable = 42;
  • size_t - Used to represent the size of an object. It's a 32-bit unsigned integer on 32-bit systems representing values from 0 up to (2^32 - 1). On the other hand, it's a 64-bit unsigned integer on 64-bit systems representing values from 0 up to (2^64 - 1).
    size_t - 객체의 크기를 나타내는 데 사용됩니다. 0부터 (2^32 - 1)까지의 값을 나타내는 32비트 시스템의 32비트 부호 없는 정수입니다. 반면에 64비트 시스템에서는 0부터 (2^64 - 1)까지의 값을 나타내는 64비트 부호 없는 정수입니다.
SIZE_T sVariable = sizeof(int);
  • VOID - Indicates the absence of a specific data type.
    VOID - 특정 데이터 유형이 없음을 나타냅니다.
void* pVariable = NULL; // This is the same as PVOID
  • PVOID - A 32-bit or 4-byte pointer of any data type on 32-bit systems. Alternatively, a 64-bit or 8-byte pointer of any data type on 64-bit systems.
    PVOID - 32비트 시스템에서 모든 데이터 유형의 32비트 또는 4바이트 포인터입니다. 또는 64비트 시스템에서 모든 데이터 유형의 64비트 또는 8바이트 포인터입니다.
PVOID pVariable = &SomeData;
  • HANDLE - A value that specifies a particular object that the operating system is managing (e.g. file, process, thread).
    HANDLE - 운영 체제가 관리하는 특정 개체(예: 파일, 프로세스, 스레드)를 지정하는 값입니다.
HANDLE hFile = CreateFile(...);
  • HMODULE - A handle to a module. This is the base address of the module in memory. An example of a MODULE can be a DLL or EXE file.
    HMODULE - 모듈에 대한 핸들입니다. 이는 메모리에 있는 모듈의 기본 주소입니다. MODULE의 예로는 DLL 또는 EXE 파일이 있습니다.
HMODULE hModule = GetModuleHandle(...);
  • LPCSTR/PCSTR - A pointer to a constant null-terminated string of 8-bit Windows characters (ANSI). The "L" stands for "long" which is derived from the 16-bit Windows programming period, nowadays it doesn't affect the data type, but the naming convention still exists.
    LPCSTR/PCSTR - 8비트 Windows 문자(ANSI)의 상수 Null 종료 문자열에 대한 포인터입니다. "L"은 16비트 Windows 프로그래밍 기간에서 파생된 "long"을 의미하며 현재는 데이터 유형에 영향을 미치지 않지만 명명 규칙은 여전히 ​​존재합니다.

    The "C" stands for "constant" or read-only variable. Both these data types are equivalent to const char*.
    "C"는 "상수" 또는 읽기 전용 변수를 나타냅니다. 이 두 데이터 유형은 모두 const char* 와 동일합니다.
LPCSTR  lpcString   = "Hello, world!";
PCSTR   pcString    = "Hello, world!";
  • LPSTR/PSTR - The same as LPCSTR and PCSTR, the only difference is that LPSTR and PSTR do not point to a constant variable, and instead point to a readable and writable string. Both these data types are equivalent to char*.
    LPSTR/PSTR - LPCSTRPCSTR 과 동일하지만 유일한 차이점은 LPSTRPSTR 상수 변수를 가리키지 않고 대신 읽고 쓸 수 있는 문자열을 가리킨다는 것입니다. 이 두 데이터 유형은 모두 char* 와 동일합니다.
LPSTR   lpString    = "Hello, world!";
PSTR    pString     = "Hello, world!";
  • LPCWSTR\PCWSTR - A pointer to a constant null-terminated string of 16-bit Windows Unicode characters (Unicode). Both these data types are equivalent to const wchar*.
    LPCWSTR\PCWSTR - 16비트 Windows 유니코드 문자(유니코드)의 상수 Null 종료 문자열에 대한 포인터입니다. 이 두 데이터 유형은 모두 const wchar* 와 동일합니다.
LPCWSTR     lpwcString  = L"Hello, world!";
PCWSTR      pcwString   = L"Hello, world!";
  • PWSTR\LPWSTR - The same as LPCWSTR and PCWSTR, the only difference is that 'PWSTR' and 'LPWSTR' do not point to a constant variable, and instead point to a readable and writable string. Both these data types are equivalent to wchar*.
    PWSTR\LPWSTR - LPCWSTRPCWSTR 과 동일하지만 유일한 차이점은 'PWSTR' 및 'LPWSTR'이 상수 변수를 가리키지 않고 대신 읽고 쓸 수 있는 문자열을 가리킨다는 것입니다. 이 두 데이터 유형은 모두 wchar* 와 동일합니다.
LPWSTR  lpwString   = L"Hello, world!";
PWSTR   pwString    = L"Hello, world!";
  • wchar_t - The same as wchar which is used to represent wide characters.
    wchar_t - 와이드 문자를 나타내는 데 사용되는 wchar 와 동일합니다.
wchar_t     wChar           = L'A';
wchar_t*    wcString        = L"Hello, world!";
  • ULONG_PTR - Represents an unsigned integer that is the same size as a pointer on the specified architecture, meaning on 32-bit systems a ULONG_PTR will be 32 bits in size, and on 64-bit systems, it will be 64 bits in size. Throughout this course, ULONG_PTR will be used in the manipulation of arithmetic expressions containing pointers (e.g. PVOID). Before executing any arithmetic operation, a pointer will be subjected to type-casting to ULONG_PTR. This approach is used to avoid direct manipulation of pointers which can lead to compilation errors.
    ULONG_PTR - 지정된 아키텍처의 포인터와 동일한 크기의 부호 없는 정수를 나타냅니다. 즉, 32비트 시스템에서 ULONG_PTR 크기는 32비트이고, 64비트 시스템에서는 64비트입니다. 이 과정 전체에서 ULONG_PTR 포인터(예: PVOID)가 포함된 산술 표현식을 조작하는 데 사용됩니다. 산술 연산을 실행하기 전에 포인터는 ULONG_PTR 로 유형 캐스팅됩니다. 이 접근 방식은 컴파일 오류로 이어질 수 있는 포인터의 직접적인 조작을 방지하는 데 사용됩니다.
PVOID Pointer = malloc(100);
// Pointer = Pointer + 10; // not allowed
Pointer = (ULONG_PTR)Pointer + 10; // allowed

Data Types Pointers 데이터 유형 포인터

The Windows API allows a developer to declare a data type directly or a pointer to the data type.
Windows API를 사용하면 개발자가 데이터 유형을 직접 선언하거나 데이터 유형에 대한 포인터를 선언할 수 있습니다.

This is reflected in the data type names where the data types that start with "P" represent pointers to the actual data type while the ones that don't start with "P" represent the actual data type itself.
이는 "P"로 시작하는 데이터 유형이 실제 데이터 유형에 대한 포인터를 나타내는 반면 "P"로 시작하지 않는 데이터 유형은 실제 데이터 유형 자체를 나타내는 데이터 유형 이름에 반영됩니다.

This will become useful later when working with Windows APIs that have parameters that are pointers to a data type. The examples below show how the "P" data type relates to its non-pointer equivalent.
이는 나중에 데이터 유형에 대한 포인터인 매개변수가 있는 Windows API로 작업할 때 유용합니다. 아래 예에서는 "P" 데이터 유형이 포인터가 아닌 해당 데이터 유형과 어떻게 관련되는지 보여줍니다.

  • PHANDLE is the same as HANDLE*.
    PHANDLE HANDLE* 과 동일합니다.

  • PSIZE_T is the same as SIZE_T*.
    PSIZE_T SIZE_T* 와 동일합니다.

  • PDWORD is the same as DWORD*.
    PDWORD DWORD* 와 동일합니다.

ANSI & Unicode Functions ANSI 및 유니코드 함수

The majority of Windows API functions have two versions ending with either "A" or with "W". For example, there is CreateFileA and CreateFileW. The functions ending with "A" are meant to indicate "ANSI" whereas the functions ending with "W" represent Unicode or "Wide".
대부분의 Windows API 함수에는 "A" 또는 "W"로 끝나는 두 가지 버전이 있습니다. 예를 들어 CreateFileACreateFileW 가 있습니다. "A"로 끝나는 함수는 "ANSI"를 나타내는 반면 "W"로 끝나는 함수는 유니코드 또는 "와이드"를 나타냅니다.

The main difference to keep in mind is that the ANSI functions will take in ANSI data types as parameters, where applicable, whereas the Unicode functions will take in Unicode data types. For example, the first parameter for CreateFileA is an LPCSTR, which is a pointer to a constant null-terminated string of 8-bit Windows ANSI characters. On the other hand, the first parameter for CreateFileW is LPCWSTR, a pointer to a constant null-terminated string of 16-bit Unicode characters.
명심해야 할 주요 차이점은 ANSI 함수는 해당되는 경우 ANSI 데이터 유형을 매개변수로 사용하는 반면, 유니코드 함수는 유니코드 데이터 유형을 사용한다는 것입니다. 예를 들어 CreateFileA 의 첫 번째 매개 변수는 8비트 Windows ANSI 문자의 상수 null 종료 문자열에 대한 포인터인 LPCSTR 입니다. 반면, CreateFileW 의 첫 번째 매개변수는 16비트 유니코드 문자의 상수 null 종료 문자열에 대한 포인터인 LPCWSTR 입니다.

Furthermore, the number of required bytes will differ depending on which version is used.
또한, 사용되는 버전에 따라 필요한 바이트 수가 달라집니다.

char str1[] = "maldev"; // 7 bytes (maldev + null byte).
char str1[] = "maldev"; // 7바이트(maldev + null 바이트 ).

wchar str2[] = L"maldev"; // 14 bytes, each character is 2 bytes (The null byte is also 2 bytes)
wchar str2[] = L"maldev"; // 14바이트, 각 문자는 2바이트입니다. (널 바이트도 2바이트입니다.)

In and Out Parameters 입력 및 출력 매개변수

Windows APIs have in and out parameters. An IN parameter is a parameter that is passed into a function and is used for input. Whereas an OUT parameter is a parameter used to return a value back to the caller of the function. Output parameters are often passed in by reference through pointers.
Windows API에는 inout 매개변수가 있습니다. IN 매개변수는 함수에 전달되어 입력에 사용되는 매개변수입니다. OUT 매개변수는 함수 호출자에게 값을 다시 반환하는 데 사용되는 매개변수입니다. 출력 매개변수는 포인터를 통해 참조로 전달되는 경우가 많습니다.

For example, the code snippet below shows a function HackTheWorld which takes in an integer pointer and sets the value to 123. This is considered an out parameter since the parameter is returning a value.
예를 들어 아래 코드 조각은 정수 포인터를 가져와 값을 123 으로 설정하는 HackTheWorld 함수를 보여줍니다. 매개변수가 값을 반환하므로 이는 out 매개변수로 간주됩니다.

BOOL HackTheWorld(OUT int* num){

    // Setting the value of num to 123
    *num = 123;
    
    // Returning a boolean value
    return TRUE;
}

int main(){
    int a = 0;

    // 'HackTheWorld' will return true
    // 'a' will contain the value 123
    HackTheWorld(&a);
}

Keep in mind that the use of the OUT or IN keywords is meant to make it easier for developers to understand what the function expects and what it does with these parameters. However, it is worth mentioning that excluding these keywords does not affect whether the parameter is considered an output or input parameter.
OUT 또는 IN 키워드를 사용하면 개발자가 함수에서 기대하는 것과 이러한 매개변수를 사용하여 수행하는 작업을 더 쉽게 이해할 수 있습니다. 그러나 이러한 키워드를 제외해도 매개변수가 출력 매개변수로 간주되는지 아니면 입력 매개변수로 간주되는지에 영향을 미치지 않는다는 점은 언급할 가치가 있습니다.

Windows API Example 윈도우 API 예

Now that the fundamentals of the Windows API have been laid out, this section will go through the usage of the CreateFileW function.
이제 Windows API의 기본 사항이 설명되었으므로 이 섹션에서는 CreateFileW 함수의 사용법을 살펴보겠습니다.

Find the API Reference API 참조 찾기

It's important to always reference the documentation if one is unsure about what the function does or what arguments it requires. Always read the description of the function and assess whether the function accomplishes the desired task. The CreateFileW documentation is available here.
함수가 수행하는 작업이나 필요한 인수가 무엇인지 확실하지 않은 경우 항상 설명서를 참조하는 것이 중요합니다. 항상 기능에 대한 설명을 읽고 기능이 원하는 작업을 수행하는지 평가하십시오. CreateFileW 문서는 여기에서 볼 수 있습니다.

Analyze Return Type & Parameters
반환 유형 및 매개변수 분석

The next step would be to view the parameters of the function along with the return data type. The documentation states If the function succeeds, the return value is an open handle to the specified file, device, named pipe, or mail slot therefore CreateFileW returns a HANDLE data type to the specified item that's created.
다음 단계는 반환 데이터 유형과 함께 함수의 매개변수를 보는 것입니다. 설명서에 따르면 함수가 성공하면 반환 값은 지정된 파일, 장치, 명명된 파이프 또는 메일 슬롯에 대한 열린 핸들이므로 CreateFileW 생성된 지정된 항목에 HANDLE 데이터 형식을 반환합니다.

Furthermore, notice that the function parameters are all in parameters. This means the function does not return any data from the parameters since they are all in parameters. Keep in mind that the keywords within the square brackets, such as in, out, and optional, are purely for developers' reference and do not have any actual impact.
또한 함수 매개변수는 모두 매개변수 in 있음을 확인하세요. 이는 매개변수가 모두 매개변수 in 있으므로 함수가 매개변수로부터 어떤 데이터도 반환하지 않는다는 것을 의미합니다. in , out , optional 등 대괄호 안의 키워드는 순전히 개발자 참조용일 뿐 실제 영향은 없다는 점을 명심하세요.

HANDLE CreateFileW(
  [in]           LPCWSTR               lpFileName,
  [in]           DWORD                 dwDesiredAccess,
  [in]           DWORD                 dwShareMode,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  [in]           DWORD                 dwCreationDisposition,
  [in]           DWORD                 dwFlagsAndAttributes,
  [in, optional] HANDLE                hTemplateFile
);

Use The Function 기능을 사용하세요

The sample code below goes through an example usage of CreateFileW. It will create a text file with the name maldev.txt on the current user's Desktop.
아래 샘플 코드는 CreateFileW 의 사용 예를 보여줍니다. 현재 사용자의 데스크탑에 maldev.txt 라는 이름의 텍스트 파일이 생성됩니다.

// This is needed to store the handle to the file object
// the 'INVALID_HANDLE_VALUE' is just to intialize the variable
HANDLE hFile = INVALID_HANDLE_VALUE; 

// The full path of the file to create.
// Double backslashes are required to escape the single backslash character in C
// Make sure the username (maldevacademy) exists, otherwise modify it
LPCWSTR filePath = L"C:\\Users\\maldevacademy\\Desktop\\maldev.txt";

// Call CreateFileW with the file path
// The additional parameters are directly from the documentation
hFile = CreateFileW(filePath, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

// On failure CreateFileW returns INVALID_HANDLE_VALUE
// GetLastError() is another Windows API that retrieves the error code of the previously executed WinAPI function
if (hFile == INVALID_HANDLE_VALUE){
    printf("[-] CreateFileW Api Function Failed With Error : %d\n", GetLastError());
    return -1;
}

Windows API Debugging Errors
Windows API 디버깅 오류

When functions fail they often return a non-verbose error. For example, if CreateFileW fails it returns INVALID_HANDLE_VALUE which indicates that a file could not be created. To gain more insight as to why the file couldn't be created, the error code must be retrieved using the GetLastError function.
함수가 실패하면 상세하지 않은 오류를 반환하는 경우가 많습니다. 예를 들어 CreateFileW 실패하면 파일을 생성할 수 없음을 나타내는 INVALID_HANDLE_VALUE 반환합니다. 파일을 생성할 수 없는 이유에 대해 더 자세히 알아보려면 GetLastError 함수를 사용하여 오류 코드를 검색해야 합니다.

Once the code is retrieved, it needs to be looked up in Windows's System Error Codes List. Some common error codes are translated below:
코드가 검색되면 Windows의 시스템 오류 코드 목록 에서 조회해야 합니다. 몇 가지 일반적인 오류 코드는 다음과 같이 번역됩니다.

  • 5 - ERROR_ACCESS_DENIED

  • 2 - ERROR_FILE_NOT_FOUND

  • 87 - ERROR_INVALID_PARAMETER

Windows Native API Debugging Errors
Windows 네이티브 API 디버깅 오류

Recall from the Windows Architecture module, NTAPIs are mostly exported from ntdll.dll. Unlike Windows APIs, these functions cannot have their error code fetched via GetLastError. Instead, they return the error code directly which is represented by the NTSTATUS data type.
Windows 아키텍처 모듈을 떠올려 보면 NTAPI는 대부분 ntdll.dll 에서 내보내집니다. Windows API와 달리 이러한 함수는 GetLastError 통해 오류 코드를 가져올 수 없습니다. 대신 NTSTATUS 데이터 유형으로 표시되는 오류 코드를 직접 반환합니다.

NTSTATUS is used to represent the status of a system call or function and is defined as a 32-bit unsigned integer value. A successful system call will return the value STATUS_SUCCESS, which is 0. On the other hand, if the call failed it will return a non-zero value, to further investigate the cause of the problem, one must check Microsoft's documentation on NTSTATUS values.
NTSTATUS 시스템 호출이나 함수의 상태를 나타내는 데 사용되며 32비트 부호 없는 정수 값으로 정의됩니다. 성공적인 시스템 호출은 STATUS_SUCCESS 값( 0 )을 반환합니다. 반면 호출이 실패하면 0이 아닌 값이 반환됩니다. 문제의 원인을 자세히 조사하려면 NTSTATUS 값에 대한 Microsoft 설명서를 확인해야 합니다.

The code snippet below shows how error checking for system calls is done.
아래 코드 조각은 시스템 호출에 대한 오류 검사가 수행되는 방법을 보여줍니다.

NTSTATUS STATUS = NativeSyscallExample(...);
if (STATUS != STATUS_SUCCESS){
    // printing the error in unsigned integer hexadecimal format
    printf("[!] NativeSyscallExample Failed With Status : 0x%0.8X \n", STATUS); 
}

// NativeSyscallExample succeeded

NT_SUCCESS Macro NT_SUCCESS 매크로

Another way to check the return value of NTAPIs is through the NT_SUCCESS macro shown here. The macro returns TRUE if the function succeeded, and FALSE it fails.
NTAPI의 반환 값을 확인하는 또 다른 방법은 여기에 표시된 NT_SUCCESS 매크로를 사용하는 것입니다. 매크로는 함수가 성공하면 TRUE 반환하고, 실패하면 FALSE 반환합니다.

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

Below, is an example of using this macro
아래는 이 매크로를 사용하는 예입니다.

NTSTATUS STATUS = NativeSyscallExample(...);
if (!NT_SUCCESS(STATUS)){
    // printing the error in unsigned integer hexadecimal format
    printf("[!] NativeSyscallExample Failed With Status : 0x%0.8X \n", STATUS); 
}

// NativeSyscallExample succeeded