woonadz :)

WIN32 API 프로그래밍 - 특정 파일 XOR 암호화하기(1):파일 데이터 불러와서 암호화하기 (CreateFileA, ReadFile) 본문

IT/malware dev

WIN32 API 프로그래밍 - 특정 파일 XOR 암호화하기(1):파일 데이터 불러와서 암호화하기 (CreateFileA, ReadFile)

C_scorch 2023. 10. 30. 19:04
반응형

CreateFileA 함수 : 파일 또는 I/O 디바이스를 열거나 생성합니다.

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

[in] lpFileName

만들거나 열 파일 또는 디바이스의 이름입니다. 이 이름에는 슬래시(/) 또는 백슬래시(\)를 사용할 수 있습니다.

기본적으로 이름은 MAX_PATH 문자로 제한됩니다. 이 제한을 32,767자로 확장하려면 경로에 "\\?\"를 앞에 추가합니다. 자세한 내용은 파일 이름 지정, 경로 및 네임스페이스를 참조하세요.

⇒ 암호화를 원하는 파일 경로를 지정해주겠습니다.

 

[in] dwDesiredAccess

파일 또는 디바이스에 대한 요청된 액세스 권한으로, 읽기, 쓰기, 둘 다 또는 0으로 요약할 수 있습니다.

가장 일반적으로 사용되는 값은 GENERIC_READGENERIC_WRITE 또는 둘 다(GENERIC_READ | GENERIC_WRITE)입니다. 자세한 내용은 일반 액세스 권한파일 보안 및 액세스 권한, 파일 액세스 권한상수 및 ACCESS_MASK 참조하세요.

이 매개 변수가 0인 경우 애플리케이션은 GENERIC_READ 액세스가 거부된 경우에도 해당 파일 또는 디바이스에 액세스하지 않고 파일, 디렉터리 또는 디바이스 특성과 같은 특정 메타데이터를 쿼리할 수 있습니다.

열려 있는 핸들이 이미 있는 열린 요청에서 dwShareMode 매개 변수로 지정된 공유 모드와 충돌하는 액세스 모드를 요청할 수 없습니다.

⇒ 데이터를 읽어 xor로 암호화 한 후에 다시 파일에 작성할 것이기 때문에 GENERIC_READ | GENERIC_WRITE 로 설정하겠습니다.

 

[in] dwShareMode

파일 또는 디바이스의 요청된 공유 모드로, 읽기, 쓰기, 모두, 삭제, 모두 또는 없음(다음 표 참조)입니다. 특성 또는 확장 특성에 대한 액세스 요청은 이 플래그의 영향을 받지 않습니다.

이 매개 변수가 0이고 CreateFile 이 성공하면 파일 또는 디바이스를 공유할 수 없으며 파일 또는 디바이스에 대한 핸들이 닫혀 있을 때까지 다시 열 수 없습니다. 자세한 내용은 주의 섹션을 참조하세요.

열린 핸들이 있는 기존 요청에 지정된 액세스 모드와 충돌하는 공유 모드를 요청할 수 없습니다. CreateFile 이 실패하고 GetLastError 함수가 ERROR_SHARING_VIOLATION 반환합니다.

다른 프로세스에 파일 또는 디바이스가 열려 있는 동안 프로세스에서 파일 또는 디바이스를 공유할 수 있도록 하려면 다음 값 중 하나 이상의 호환 가능한 조합을 사용합니다. 이 매개 변수와 dwDesiredAccess 매개 변수의 유효한 조합에 대한 자세한 내용은 파일 만들기 및 열기를 참조하세요.

⇒ 0으로 설정하여 파일을 수정하는 동안 다른 프로세스가 접근할 수 없게 하겠습니다.

 

[in, optional] lpSecurityAttributes

별도의 두 개의 관련 데이터 멤버인 선택적 보안 설명자 및 반환된 핸들을 자식 프로세스에서 상속할 수 있는지 여부를 결정하는 부울 값이 포함된 SECURITY_ATTRIBUTES 구조체에 대한 포인터입니다.

이 매개 변수는 NULL일 수 있습니다.

이 매개 변수가 NULL인 경우 CreateFile 에서 반환된 핸들은 애플리케이션이 만들 수 있는 자식 프로세스에서 상속할 수 없으며 반환된 핸들과 연결된 파일 또는 디바이스는 기본 보안 설명자를 가져옵니다.

구조체의 lpSecurityDescriptor 멤버는 파일 또는 디바이스에 대한 SECURITY_DESCRIPTOR 지정합니다. 이 멤버가 NULL인 경우 반환된 핸들과 연결된 파일 또는 디바이스에 기본 보안 설명자가 할당됩니다.

CreateFile 은 기존 파일 또는 디바이스를 열 때 lpSecurityDescriptor 멤버를 무시하지만 bInheritHandle 멤버를 계속 사용합니다.

구조체의 bInheritHandle 멤버는 반환된 핸들을 상속할 수 있는지 여부를 지정합니다.

⇒ optional 값이기에 NULL 값을 주어 생략하겠습니다.

 

[in] dwCreationDisposition

존재하거나 존재하지 않는 파일 또는 디바이스에서 수행할 작업입니다.

파일 이외의 디바이스의 경우 이 매개 변수는 일반적으로 OPEN_EXISTING 설정됩니다.

자세한 내용은 주의 섹션을 참조하세요.

이 매개 변수는 결합할 수 없는 다음 값 중 하나여야 합니다.

⇒ 이미 존재하는 파일에 대해 암호화하는 것이기에 OPEN_EXISTING으로 설정하겠습니다.

 

[in] dwFlagsAndAttributes

파일 또는 디바이스 특성 및 플래그이며 , FILE_ATTRIBUTE_NORMAL 파일의 가장 일반적인 기본값입니다.

이 매개 변수는 사용 가능한 파일 특성(FILE_ATTRIBUTE_*)의 조합을 포함할 수 있습니다. 다른 모든 파일 특성은 FILE_ATTRIBUTE_NORMAL 재정의합니다.

이 매개 변수는 파일 또는 디바이스 캐싱 동작, 액세스 모드 및 기타 특수 용도 플래그를 제어하기 위한 플래그(FILE_FLAG_)의 조합을 포함할 수도 있습니다. 이러한 값은 모든 FILE_ATTRIBUTE_ 값과 결합합니다.

이 매개 변수는 SECURITY_SQOS_PRESENT 플래그를 지정하여 SQOS(서비스 품질) 정보를 포함할 수도 있습니다. 추가 SQOS 관련 플래그 정보는 특성 및 플래그 테이블 다음에 테이블에 표시됩니다.

⇒ FILE_ATTRIBUTE_NORMAL 로 설정하겠습니다.

 

[in, optional] hTemplateFile

GENERIC_READ 액세스 권한이 있는 템플릿 파일에 대한 유효한 핸들입니다. 템플릿 파일은 생성되는 파일에 대한 파일 특성 및 확장 특성을 제공합니다.

이 매개 변수는 NULL일 수 있습니다.

기존 파일을 열 때 CreateFile 은 이 매개 변수를 무시합니다.

암호화된 새 파일을 열 때 파일은 부모 디렉터리에서 임의 액세스 제어 목록을 상속합니다. 자세한 내용은 파일 암호화를 참조하세요.

⇒ optional 값이기에 생략하겠습니다.

 

반환 값

함수가 성공하면 반환 값은 지정된 파일, 디바이스, 명명된 파이프 또는 메일 슬롯에 대한 열린 핸들입니다. 함수가 실패하는 경우 반환 값은 INVALID_HANDLE_VALUE입니다. 확장 오류 정보를 가져오려면 GetLastError를 호출합니다.

 

정리하면 다음과 같은 매개변수를 지정합니다.

HANDLE CreateFileA(
  [in]           LPCSTR                C:\\\\Users\\\\Owner\\\\Documents\\\\test\\\\1.txt,
  [in]           DWORD                 GENERIC_READ | GENERIC_WRITE,
  [in]           DWORD                 0,
  [in, optional] LPSECURITY_ATTRIBUTES NULL,
  [in]           DWORD                 OPEN_EXISTING,
  [in]           DWORD                 FILE_ATTRIBUTE_NORMAL,
  [in, optional] HANDLE                NULL
);

 

ReadFile 함수 : 지정된 파일 또는 I/O(입력/출력) 디바이스에서 데이터를 읽습니다.

BOOL ReadFile(
  [in]                HANDLE       hFile,
  [out]               LPVOID       lpBuffer,
  [in]                DWORD        nNumberOfBytesToRead,
  [out, optional]     LPDWORD      lpNumberOfBytesRead,
  [in, out, optional] LPOVERLAPPED lpOverlapped
);

[in] hFile

디바이스에 대한 핸들(예: 파일, 파일 스트림, 물리적 디스크, 볼륨, 콘솔 버퍼, 테이프 드라이브, 소켓, 통신 리소스, mailslot 또는 파이프).

hFile 매개 변수는 읽기 액세스 권한으로 만들어졌어야 합니다. 자세한 내용은 일반 액세스 권한 및 파일 보안 및 액세스 권한을 참조하세요.

비동기 읽기 작업의 경우 hFile은 CreateFile 함수에서 FILE_FLAG_OVERLAPPED 플래그로 연 핸들 또는 소켓 또는 accept 함수에서 반환된 소켓 핸들일 수 있습니다.

⇒ 앞서 CreateFileA 함수의 반환값으로 가져왔던 Handle 값을 반환해줍니다.

 

[out] lpBuffer

파일 또는 디바이스에서 읽은 데이터를 수신하는 버퍼에 대한 포인터입니다.

이 버퍼는 읽기 작업 기간 동안 유효한 상태를 유지해야 합니다. 호출자는 읽기 작업이 완료될 때까지 이 버퍼를 사용하지 않아야 합니다.

⇒ output 값으로 설정해줍니다.

 

[in] nNumberOfBytesToRead

읽을 최대 바이트 수입니다.

⇒ 실제 랜섬웨어를 개발하여 모든 파일을 탐색할 때는 각각의 파일의 크기를 정확히 알 수 없습니다. 그래서 저는 이 값을 1024의 값으로 설정하여 파일의 끝 데이터까지 불러오도록 하겠습니다. 더 자세한 사항은 코드에서 설명하도록 하겠습니다.

 

[out, optional] lpNumberOfBytesRead

동기 hFile 매개 변수를 사용할 때 읽은 바이트 수를 수신하는 변수에 대한 포인터입니다. ReadFile 은 작업 또는 오류 검사를 수행하기 전에 이 값을 0으로 설정합니다. 잠재적으로 잘못된 결과를 방지하려면 비동기 작업인 경우 이 매개 변수에 NULL 을 사용합니다.

이 매개 변수는 lpOverlapped 매개 변수가 NULL이 아닌 경우에만 NULL일 수 있습니다.

⇒ optional로 지정되어있기는 하지만 파일의 끝 데이터까지 탐색했음을 알아보기 위해 매개변수를 지정하여 사용하도록 하겠습니다.

 

[in, out, optional] lpOverlapped

hFile 매개 변수가 FILE_FLAG_OVERLAPPED 열린 경우 OVERLAPPED 구조체에 대한 포인터가 필요하고, 그렇지 않으면 NULL일 수 있습니다.

hFile이 FILE_FLAG_OVERLAPPED 사용하여 열리는 경우 lpOverlapped 매개 변수는 유효하고 고유한 OVERLAPPED 구조를 가리킬 수 있어야 합니다. 그렇지 않으면 함수가 읽기 작업이 완료되었음을 잘못 보고할 수 있습니다.

바이트 오프셋을 지원하는 hFile 의 경우 이 매개 변수를 사용하는 경우 파일 또는 디바이스에서 읽기를 시작할 바이트 오프셋을 지정해야 합니다. 이 오프셋은 OVERLAPPED 구조체의 Offset 및 OffsetHigh 멤버를 설정하여 지정됩니다. 바이트 오프셋을 지원하지 않는 hFile 의 경우 Offset 및 OffsetHigh 는 무시됩니다.

lpOverlapped 및 FILE_FLAG_OVERLAPPED 다양한 조합에 대한 자세한 내용은 설명 섹션 및 동기화 및 파일 위치 섹션을 참조하세요.

⇒ FILE_FLAG_OVERLAPPED는 비동기 작업을 지원할 수 있습니다. 하지만 CreateFile을 기본 속성으로 열었기 때문에 NULL 값으로 지정하겠습니다.

 

반환 값

함수가 성공하면 반환 값은 0이 아닌 값(TRUE)입니다.

함수가 실패하거나 비동기적으로 완료되는 경우 반환 값은 0(FALSE)입니다. 확장 오류 정보를 가져오려면 GetLastError 함수를 호출합니다.

#include <Windows.h>
#include <stdio.h>
#include <fileapi.h>
#define buffersize 1024

int main(void) {
	LPCSTR path = "C:\\\\Users\\\\Owner\\\\Documents\\\\test\\\\1.txt"; //읽어올 파일 경로
	LPVOID buffer[buffersize];
	DWORD BytesRead = 0;
	BYTE* data = NULL;
	DWORD totalBytesRead = 0;

	HANDLE CreateFile_result = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	printf("%p\\n", CreateFile_result); //파일 열기 성공 여부 지정

	while (ReadFile(CreateFile_result, buffer, buffersize, &BytesRead, NULL) && BytesRead > 0) { 
	//파일이 1024BYTE가 넘을 때 파일을 끝까지 읽을 때까지 반복
		data = (BYTE*)realloc(data, totalBytesRead + BytesRead); 
		memcpy(data + totalBytesRead, buffer, BytesRead);
		totalBytesRead += BytesRead;
	}

	for (DWORD i = 0; i < totalBytesRead; i++) {
		printf("%c", data[i]);
	}

	free(data); //동적 할당한 메모리 해제
	CloseHandle(CreateFile_result); // 파일 핸들을 닫음
	
	return 0;
}

위 코드는 파일을 열고 읽어서 출력하는 것까지의 코드입니다. 저는 파일이 1024BYTE가 넘을 때 파일을 끝까지 읽을 때까지 반복하는 코드를 작성할 때 조금 뻘짓(?)을 했습니다. ReadFile 자체 API에서 파일을 buffersize 만큼 읽은 후 남은 데이터가 있다면 읽어온 데이터부터 다시 읽게 한다는 것을 몰랐습니다. 그래서 처음에는 데이터를 얼마만큼 읽었는지 기록하고 그 부분부터 다시 읽게끔 구현하려고 했었습니다.

 

 

이제는 불러온 데이터를 XOR로 암호화를 해보겠습니다.

#include <Windows.h>
#include <stdio.h>
#include <fileapi.h>
#define buffersize 1024

const BYTE xorKey[] = { 'M', 'a', 'l', 'F', 'F', 'l', 'e', 'R' }; //암호화 키 할당

int main(void) {
	LPCSTR path = "C:\\\\Users\\\\Owner\\\\Documents\\\\test\\\\1.txt"; //읽어올 파일 경로
	LPVOID buffer[buffersize];
	DWORD BytesRead = 0;
	BYTE* data = NULL;
	DWORD totalBytesRead = 0;

	HANDLE CreateFile_result = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	printf("%p\\n", CreateFile_result); //파일 열기 성공 여부 지정

	while (ReadFile(CreateFile_result, buffer, buffersize, &BytesRead, NULL) && BytesRead > 0) { //파일이 1024BYTE가 넘을 때 파일을 끝까지 읽을 때까지 반
		data = (BYTE*)realloc(data, totalBytesRead + BytesRead);
		memcpy(data + totalBytesRead, buffer, BytesRead);
		totalBytesRead += BytesRead;
	}

	for (DWORD i = 0; i < totalBytesRead; i++) {
		data[i] ^= xorKey[i % 8]; // 8바이트 키를 순환하며 XOR 연산
	}
	for (DWORD i = 0; i < totalBytesRead; i++) {
		printf("%c", data[i]); //암호화 된 데이터 출력
	}

	free(data); //동적 할당한 메모리 해제
	CloseHandle(CreateFile_result); // 파일 핸들을 닫음
	
	return 0;
}

암호화한 결과 값은 다음과 같습니다. 불러온 파일에는 1부터 16까지의 숫자가 반복되어 적혀있는 데이터가 들어있었습니다. 

반응형