일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 |
- 사회분업론
- CodeEngn Basic 01
- 리버싱
- 에밀 뒤르켐
- BoB 12기
- bob
- 사회적 사실
- 논문리뷰
- malware
- 코드엔진 basic 5
- CodeEngn
- codeengn basic rce 01
- 자살론
- Best of the Best
- CodeEngn Basic 5
- 코드엔진
- 코드엔진 베이직
- 철학
- BoB 12기 최종합격 후기
- 디지털 포렌식 트랙
- h4ckinggame
- Today
- Total
woonadz :)
WIN32 API 프로그래밍 - 특정 파일 XOR 암호화하기(1):파일 데이터 불러와서 암호화하기 (CreateFileA, ReadFile) 본문
WIN32 API 프로그래밍 - 특정 파일 XOR 암호화하기(1):파일 데이터 불러와서 암호화하기 (CreateFileA, ReadFile)
C_scorch 2023. 10. 30. 19:04CreateFileA 함수 : 파일 또는 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_READ, GENERIC_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까지의 숫자가 반복되어 적혀있는 데이터가 들어있었습니다.
'IT > malware dev' 카테고리의 다른 글
WIN32 API 프로그래밍 - CMD를 이용한 자가 삭제 (1) | 2023.11.03 |
---|---|
WIN32 API 프로그래밍 - 특정 파일 암호화 및 확장자 변경하기: 지금까지의 코드 병합 (1) | 2023.11.01 |
WIN32 API 프로그래밍 - 특정 파일 XOR 암호화하기(2):암호화된 데이터 새로운 확장자로 저장하기 (WriteFile) (1) | 2023.10.31 |
WIN32 API 프로그래밍 - 폴더 내 모든 파일 출력하기(FindFirstFileA, FindNextFileA) (0) | 2023.10.26 |
WIN32 API 프로그래밍 - 폴더 경로 가져오기(SHGetKnownFolderPath) (0) | 2023.10.25 |