379 lines
9.7 KiB
Plaintext
379 lines
9.7 KiB
Plaintext
unit BsoneUtil;
|
|
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.IOUtils,
|
|
System.Classes,
|
|
System.StrUtils,
|
|
Winapi.Windows,
|
|
Winapi.Messages,
|
|
Winapi.Winsock2,
|
|
Winapi.IPHlpApi,
|
|
Winapi.ActiveX,
|
|
Winapi.WinSpool,
|
|
System.Math,
|
|
GlobalDefine,
|
|
System.NetEncoding,
|
|
BsoneDebug;
|
|
|
|
const
|
|
STATUS_SUCCESS = $00000000;
|
|
|
|
// FS_INFORMATION_CLASS (enum 값을 상수로 정의)
|
|
FileFsVolumeInformation = 1;
|
|
FileFsLabelInformation = 2;
|
|
FileFsSizeInformation = 3;
|
|
FileFsDeviceInformation = 4; // 우리가 필요한 값
|
|
FileFsAttributeInformation = 5;
|
|
FileFsControlInformation = 6;
|
|
FileFsFullSizeInformation = 7;
|
|
FileFsObjectIdInformation = 8;
|
|
FileFsDriverPathInformation = 9;
|
|
FileFsVolumeFlagsInformation = 10;
|
|
|
|
// GetOffLineType에서 사용할 리턴 타입 상수
|
|
OFFTYPE_UNKNOWN = 0;
|
|
OFFTYPE_REMOVABLE = 1;
|
|
OFFTYPE_FLOPPY = 2;
|
|
OFFTYPE_NETWORKDRIVEOUT = 3;
|
|
OFFTYPE_CDROM = 4;
|
|
OFFTYPE_EXTERNALHDD = 5;
|
|
|
|
IOCTL_STORAGE_QUERY_PROPERTY = $002D1400;
|
|
StorageDeviceProperty = 0; // STORAGE_PROPERTY_ID
|
|
PropertyStandardQuery = 0; // STORAGE_QUERY_TYPE
|
|
|
|
type
|
|
|
|
// 이전 질문에서 정의한 STORAGE_BUS_TYPE이 있다고 가정합니다.
|
|
// 만약 다른 유닛에 있다면 uses에 추가하고 이 정의는 주석 처리하세요.
|
|
{$MINENUMSIZE 4}
|
|
STORAGE_BUS_TYPE = (
|
|
BusTypeUnknown = $00,
|
|
BusTypeScsi, BusTypeAtapi, BusTypeAta, BusType1394, BusTypeSsa, BusTypeFibre,
|
|
BusTypeUsb, BusTypeRAID, BusTypeiScsi, BusTypeSas, BusTypeSata, BusTypeSd,
|
|
BusTypeMmc, BusTypeVirtual, BusTypeFileBackedVirtual, BusTypeSpaces,
|
|
BusTypeNvme, BusTypeSCM, BusTypeUfs, BusTypeNvmeof, BusTypeMax,
|
|
BusTypeMaxReserved = $7F
|
|
);
|
|
{$MINENUMSIZE 1}
|
|
|
|
// STORAGE_PROPERTY_QUERY 구조체
|
|
PSTORAGE_PROPERTY_QUERY = ^STORAGE_PROPERTY_QUERY;
|
|
STORAGE_PROPERTY_QUERY = record
|
|
PropertyId: DWORD; // STORAGE_PROPERTY_ID
|
|
QueryType: DWORD; // STORAGE_QUERY_TYPE
|
|
AdditionalParameters: array [0..0] of Byte;
|
|
end;
|
|
|
|
// STORAGE_DESCRIPTOR_HEADER 구조체
|
|
STORAGE_DESCRIPTOR_HEADER = record
|
|
Version: DWORD;
|
|
Size: DWORD;
|
|
end;
|
|
|
|
// STORAGE_DEVICE_DESCRIPTOR 구조체 (필요한 부분만 정의)
|
|
PSTORAGE_DEVICE_DESCRIPTOR = ^STORAGE_DEVICE_DESCRIPTOR;
|
|
STORAGE_DEVICE_DESCRIPTOR = record
|
|
Version: DWORD;
|
|
Size: DWORD;
|
|
DeviceType: Byte;
|
|
DeviceTypeModifier: Byte;
|
|
RemovableMedia: Boolean;
|
|
CommandQueueing: Boolean;
|
|
VendorIdOffset: DWORD;
|
|
ProductIdOffset: DWORD;
|
|
ProductRevisionOffset: DWORD;
|
|
SerialNumberOffset: DWORD;
|
|
BusType: STORAGE_BUS_TYPE; // 여기에 BusType 위치
|
|
RawPropertiesIncluded: DWORD;
|
|
// 이후 가변 길이 배열...
|
|
end;
|
|
|
|
NTSTATUS = LongInt;
|
|
|
|
PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;
|
|
IO_STATUS_BLOCK = record
|
|
case Integer of
|
|
0: (
|
|
Status: NTSTATUS;
|
|
Information: NativeUInt;
|
|
);
|
|
1: (
|
|
Pointer: Pointer;
|
|
);
|
|
end;
|
|
|
|
PFILE_FS_DEVICE_INFORMATION = ^FILE_FS_DEVICE_INFORMATION;
|
|
FILE_FS_DEVICE_INFORMATION = record
|
|
DeviceType: DWORD;
|
|
Characteristics: ULONG;
|
|
end;
|
|
|
|
TNtQueryVolumeInformationFile = function(
|
|
FileHandle: THandle;
|
|
IoStatusBlock: PIO_STATUS_BLOCK;
|
|
FsInformation: Pointer;
|
|
Length: ULONG;
|
|
FsInformationClass: Integer
|
|
): NTSTATUS; stdcall;
|
|
|
|
function UtilGetDriveTypeEx(path: string): DWORD; stdcall;
|
|
function UtilCompareCode(Src: PByte; Code: PByte; CodeSize: DWORD): BOOL; stdcall;
|
|
function UtilFindCode(BaseAddress: PByte; BaseSize: DWORD; Code: PByte;
|
|
CodeSize: DWORD; var nPos: Integer): PByte; stdcall;
|
|
|
|
|
|
function UtilGetBufferHex(pData: PByte; ASize, AMaxSize: DWORD): string;
|
|
procedure UtilSaveBufferToFile(sPath: string; pData: PByte; ASize: DWORD);
|
|
|
|
function UtilGetDriveTypeAndCharacteristics(hDevice: THandle; out DeviceType: DWORD; out Characteristics: ULONG): Boolean;
|
|
function UtilIsFileSizeOverBlockSize(const path: string; OutBlockSize: DWORD): Boolean;
|
|
|
|
implementation
|
|
|
|
|
|
function UtilGetDriveTypeEx(path: string): DWORD; stdcall;
|
|
var
|
|
driver: string;
|
|
P3, P4: Integer;
|
|
begin
|
|
|
|
// driver := TPath.GetPathRoot(path); 해당 API 실행 시 explorer가 죽는 현상이 있음.
|
|
if path = '' then
|
|
begin
|
|
Result := DRIVE_UNKNOWN;
|
|
Exit;
|
|
end;
|
|
|
|
if (Length(path) >= 2) and (path[1] = '\') and (path[2] = '\') then // 2. UNC 경로 검사 (예: \\Server\Share)
|
|
begin
|
|
P3 := PosEx('\', path, 3); // 3번째 '\' (e.g., \\Server[\ ]Share)
|
|
if P3 = 0 then
|
|
begin
|
|
driver := path; // '\\Server' (루트가 됨)
|
|
end
|
|
else
|
|
begin // 4번째 '\' (e.g., \\Server\Share[\ ]Folder)
|
|
P4 := PosEx('\', path, P3 + 1);
|
|
if P4 = 0 then
|
|
driver := path
|
|
else
|
|
// '\\Server\Share\Folder' (루트는 '\\Server\Share\')
|
|
driver := Copy(path, 1, P4);
|
|
end;
|
|
end
|
|
else if (Length(path) >= 3) and (path[2] = ':') and (path[3] = '\') then // 3. 드라이브 문자 경로 검사 (예: C:\)
|
|
begin
|
|
driver := Copy(path, 1, 3); // "C:\"
|
|
end
|
|
else if (Length(path) = 2) and (path[2] = ':') then // 4. 드라이브 문자만 검사 (예: C:)
|
|
begin
|
|
driver := path + '\'; // "C:\"
|
|
end
|
|
else if path[1] = '\' then // 5. 루트 디렉토리 검사 (예: \)
|
|
begin
|
|
driver := '\';
|
|
end
|
|
// 6. 위 모든 경우에 해당하지 않으면 상대 경로로 간주
|
|
else
|
|
begin
|
|
// TPath.GetPathRoot는 빈 문자열을 반환 -> DRIVE_UNKNOWN (혹은 NO_ROOT_DIR)
|
|
Result := DRIVE_NO_ROOT_DIR;
|
|
Exit;
|
|
end;
|
|
|
|
// LOG('UtilGetDriveTypeEx, GetPathRoot--(%s)', [PChar(driver)]);
|
|
if driver = '' then
|
|
begin
|
|
// 루트가 없으면(상대 경로 등) C++ 원본의 의도대로 DRIVE_UNKNOWN 반환
|
|
Result := DRIVE_UNKNOWN;
|
|
end
|
|
else
|
|
begin
|
|
// GetDriveType API는 'C:\' 및 '\\Server\Share\' 같은 UNC 경로를
|
|
// PChar로 전달받아 올바르게 처리합니다.
|
|
Result := GetDriveType(PChar(driver));
|
|
end;
|
|
end;
|
|
|
|
function UtilCompareCode(Src: PByte; Code: PByte; CodeSize: DWORD): BOOL; stdcall;
|
|
begin
|
|
Result := CompareMem(Src, Code, CodeSize);
|
|
end;
|
|
|
|
function UtilFindCode(BaseAddress: PByte; BaseSize: DWORD; Code: PByte;
|
|
CodeSize: DWORD; var nPos: Integer): PByte; stdcall;
|
|
var
|
|
i: Integer;
|
|
p: PByte;
|
|
c: PByte;
|
|
Bytes: Integer;
|
|
sBuff: string;
|
|
begin
|
|
Result := nil;
|
|
nPos := -1;
|
|
p := BaseAddress;
|
|
c := Code;
|
|
|
|
Bytes := Integer(BaseSize) - Integer(CodeSize);
|
|
|
|
if Bytes < 0 then
|
|
Exit;
|
|
|
|
// LOG('UtilFindCode, %d : %d %d', [BaseSize, CodeSize, Bytes]);
|
|
// for i := 0 to CodeSize - 1 do
|
|
// sBuff := sBuff + IntToHex(Code[i], 2);
|
|
//
|
|
// LOG('UtilFindCode: Code(%s)', [sBuff]);
|
|
|
|
// if Bytes = 0 then
|
|
// Bytes := 1
|
|
// else
|
|
// Bytes := Bytes;
|
|
|
|
for i := 0 to Bytes do
|
|
begin
|
|
if CompareMem(p, c, CodeSize) then
|
|
begin
|
|
nPos := i;
|
|
Result := p;
|
|
Exit;
|
|
end;
|
|
|
|
Inc(p);
|
|
end;
|
|
|
|
end;
|
|
|
|
function UtilGetBufferHex(pData: PByte; ASize, AMaxSize: DWORD): string;
|
|
var
|
|
i, inSize: DWORD;
|
|
begin
|
|
Result := '';
|
|
if (pData = nil) or (ASize = 0) then
|
|
Exit;
|
|
|
|
if ASize > AMaxSize then
|
|
inSize := AMaxSize
|
|
else
|
|
inSize := ASize;
|
|
|
|
for i := 0 to inSize - 1 do
|
|
Result := Result + IntToHex(pData[i], 2);
|
|
end;
|
|
|
|
|
|
procedure UtilSaveBufferToFile(sPath: string; pData: PByte; ASize: DWORD);
|
|
var
|
|
fs: TFileStream;
|
|
sFileName: string;
|
|
begin
|
|
if (pData = nil) or (ASize = 0) then
|
|
Exit;
|
|
|
|
try
|
|
sFileName := TPath.Combine(sPath,
|
|
'wpd_data_' + FormatDateTime('yyyymmdd_hhnnss_zzz', Now) + '.bin');
|
|
|
|
fs := TFileStream.Create(sFileName, fmCreate);
|
|
try
|
|
fs.WriteBuffer(pData^, ASize);
|
|
finally
|
|
fs.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
LOG('_DeviceIoControl: FAILED to save file. Error: %s', [PChar(E.Message)]);
|
|
end;
|
|
end;
|
|
|
|
|
|
function UtilGetDriveTypeAndCharacteristics(hDevice: THandle; out DeviceType: DWORD; out Characteristics: ULONG): Boolean;
|
|
var
|
|
hNtDll: HMODULE;
|
|
NtQueryVolumeInformationFile: TNtQueryVolumeInformationFile;
|
|
status: NTSTATUS;
|
|
IoStatusBlock: IO_STATUS_BLOCK;
|
|
FileFsDeviceInfo: FILE_FS_DEVICE_INFORMATION;
|
|
begin
|
|
Result := False;
|
|
DeviceType := 0;
|
|
Characteristics := 0;
|
|
|
|
// 1. ntdll.dll 핸들 얻기
|
|
hNtDll := GetModuleHandle('ntdll.dll');
|
|
if hNtDll = 0 then
|
|
Exit;
|
|
|
|
// 2. 함수 주소 얻기
|
|
@NtQueryVolumeInformationFile := GetProcAddress(hNtDll, 'NtQueryVolumeInformationFile');
|
|
if not Assigned(NtQueryVolumeInformationFile) then
|
|
Exit;
|
|
|
|
// 3. API 호출
|
|
status := NtQueryVolumeInformationFile(
|
|
hDevice,
|
|
@IoStatusBlock,
|
|
@FileFsDeviceInfo,
|
|
SizeOf(FileFsDeviceInfo),
|
|
FileFsDeviceInformation // = 4
|
|
);
|
|
|
|
// 4. 결과 처리 (STATUS_SUCCESS == 0)
|
|
if status = STATUS_SUCCESS then
|
|
begin
|
|
Result := True;
|
|
DeviceType := FileFsDeviceInfo.DeviceType;
|
|
Characteristics := FileFsDeviceInfo.Characteristics;
|
|
end;
|
|
end;
|
|
|
|
|
|
function UtilIsFileSizeOverBlockSize(const path: string; OutBlockSize: DWORD): Boolean;
|
|
var
|
|
Attribute: TWin32FileAttributeData;
|
|
CurrentFileSize: Int64;
|
|
LimitSize: Int64;
|
|
begin
|
|
Result := False;
|
|
|
|
if OutBlockSize = 0 then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
|
|
// 1. 파일 속성 가져오기
|
|
if not GetFileAttributesEx(PChar(path), GetFileExInfoStandard, @Attribute) then
|
|
begin
|
|
// 실패 시 0으로 간주하거나, 예외 처리 (여기서는 0으로 진행)
|
|
Attribute.nFileSizeLow := 0;
|
|
Attribute.nFileSizeHigh := 0;
|
|
end;
|
|
|
|
// 2. [중요] 64비트 파일 크기로 변환 (High + Low)
|
|
// 4GB 이상의 대용량 파일도 정확하게 비교하기 위함
|
|
CurrentFileSize := (Int64(Attribute.nFileSizeHigh) shl 32) or Attribute.nFileSizeLow;
|
|
|
|
// 3. 로그 출력 (현재 크기 vs 제한 크기)
|
|
// OutBlockSize는 MB 단위라고 가정
|
|
OutputDebugString(PChar(Format('UtilIsFileSizeOverBlockSize File Size: %d Bytes, Limit: %d MB',
|
|
[CurrentFileSize, OutBlockSize])));
|
|
|
|
// 4. MB 단위를 Byte로 변환하여 비교
|
|
// 1 MB = 1024 * 1024 Bytes (또는 1 shl 20)
|
|
// Int64로 캐스팅해야 곱셈 과정에서 오버플로우가 발생하지 않음
|
|
LimitSize := Int64(OutBlockSize) * 1024 * 1024;
|
|
|
|
if CurrentFileSize > LimitSize then
|
|
begin
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
end.
|