{*******************************************************} { } { Tocsg.AIP } { } { Copyright (C) 2024 kku } { } {*******************************************************} unit Tocsg.AIP; interface uses System.SysUtils, System.Classes, Winapi.ActiveX; const SIGN_AIP_DRM: array [0..23] of Byte = ($2E, $70, $66, $69, $6C, $65, $03, $00, $00, $00, $00, $00, $00, $00, $8C, $01, $00, $00, $0D, $0A, $0D, $0A, $0D, $0A); // .pfile 로 시작 function IsAipEncrytedPDF(sPath: String): Boolean; function GetAipLabelNameFromPDF(sPath: String): String; function IsAipEncrytedOldOfficeDoc(sPath, sTempDir: String; nMode: Integer = STGM_READ or STGM_SHARE_EXCLUSIVE): Boolean; function GetAipLabelNameFromOldOfficeDoc(sPath: String): String; function IsAipEncrytedOfficeDoc(sPath, sTempDir: String; nMode: Integer = STGM_READ or STGM_SHARE_EXCLUSIVE): Boolean; function GetAipLabelNameFromOfficeDoc(sPath: String): String; function CheckMsPfileExt(sPath: String): Boolean; function CheckAipEncSign(sPath: String): Boolean; function IsAipEncryted(sPath: String; sTempDir: String = 'C:\ProgramData\HE\AEN\'; nMode: Integer = STGM_READ or STGM_SHARE_EXCLUSIVE): Boolean; function ConvAipEncExt(sPath: String): String; implementation uses Tocsg.Exception, Tocsg.Hex, Winapi.Windows, Tocsg.Strings, Tocsg.Safe, AbUnzper, System.Zip, Tocsg.Path, Tocsg.OLE.Stg, Tocsg.Files; function IsAipEncrytedPDF(sPath: String): Boolean; var fs: TFileStream; pBuf: TBytes; sDelPath: String; begin Result := false; try sDelPath := 'C:\ProgramData\HE\AEN\'; if sPath.ToUpper.StartsWith(sDelPath.ToUpper) then exit; // WriteLnFileEndUTF8('C:\ProgramData\HE\ov_log.txt', Format('IsAipEncrytedPDF() .. Path=%s, TempDir=%s', [sPath, sDelPath])); if ForceDirectories(sDelPath) then begin sDelPath := sDelPath + '$' + ExtractFileName(sPath); if CopyFile(PChar(sPath), PChar(sDelPath), false) then begin try Guard(fs, TFileStream.Create(sDelPath, fmOpenRead or fmShareDenyNone)); SetLength(pBuf, 2048); if fs.Read(pBuf[0], 2048) <> 2048 then exit; Result := PosBin(TEncoding.UTF8.GetBytes('MicrosoftIRMServices Protected PDF'), pBuf) <> -1; finally if sDelPath <> '' then DeleteFileForce(sDelPath); end; end; end; except on E: Exception do ETgException.TraceException(E, 'Fail .. IsAipEncryted()'); end; end; function GetAipLabelNameFromPDF(sPath: String): String; //const // p: array [0..13] of Byte = ($FE, $FF, $C7, $7C, $BC, $18, $00, $20, $B8, $08, $C7, $74, $BE, $14); // "일반 레이블" var fs: TFileStream; pBuf, pLabel: TBytes; nBufLen, nPos, nEnd, nLabelLen: Integer; begin Result := 'Unknown'; try if not FileExists(sPath) then exit; nBufLen := 1024; SetLength(pBuf, nBufLen); fs := TFileStream.Create(sPath, fmOpenRead or fmShareDenyNone); // 암호화 레이블로 적용되었는지 확인 if fs.Read(pBuf[0], nBufLen) <> nBufLen then exit; if PosBin(TEncoding.UTF8.GetBytes('MicrosoftIRMServices Protected PDF'), pBuf) <> -1 then begin // 암호화된 레이블 정보 위치 fs.Seek(27440, soBeginning); fs.Read(pBuf[0], nBufLen); end else begin // 일반 레이블 정보 위치 fs.Seek(-1024, soEnd); fs.Read(pBuf[0], nBufLen); end; nPos := PosBin(TEncoding.UTF8.GetBytes('/MSIP_Label_'), pBuf); if nPos > -1 then begin nPos := PosBin(TEncoding.UTF8.GetBytes('_Name ('), pBuf, nPos); if nPos > -1 then begin Inc(nPos, 7); nEnd := PosBin(TEncoding.UTF8.GetBytes(')'), pBuf, nPos); nLabelLen := nEnd - nPos; if (nEnd > -1) and (nLabelLen > 0) then begin SetLength(pLabel, nLabelLen); CopyMemory(pLabel, @pBuf[nPos], nLabelLen); if pLabel[0] = $FE then Result := TEncoding.BigEndianUnicode.GetString(pLabel) else Result := TEncoding.ANSI.GetString(pLabel); end; end; end; except on E: Exception do ETgException.TraceException(E, 'Fail .. GetAipLabelNameFromPDF()'); end; end; function IsAipEncrytedOldOfficeDoc(sPath, sTempDir: String; nMode: Integer = STGM_READ or STGM_SHARE_EXCLUSIVE): Boolean; var // fs: TFileStream; // pBuf: TBytes; // nBufLen: Integer; pvStg: IStorage; sData: WideString; enum: IEnumStatStg; elt: TStatStg; sDelPath: String; begin Result := false; try if sPath.ToUpper.StartsWith(sTempDir.ToUpper) then exit; // WriteLnFileEndUTF8('C:\ProgramData\HE\ov_log.txt', Format('IsAipEncrytedOldOfficeDoc() .. Path=%s, TempDir=%s', [sPath, sTempDir])); pvStg := nil; sDelPath := ''; try // 쉘 익스텐션 GetIconLocation() 에선 STGM_SHARE_EXCLUSIVE 모드 사용 시 스톱되서 아래처럼 처리 25_0122 14:03:25 kku if not StgStorageFileOpen(sPath, pvStg, nMode) then begin if (sTempDir <> '') and ForceDirectories(sTempDir) then begin // MS 파일은 사용중이면 안열려서 이렇게 처리 24_1114 09:24:47 kku sDelPath := sTempDir + '$' + ExtractFileName(sPath); if CopyFile(PChar(sPath), PChar(sDelPath), false) then begin if not StgStorageFileOpen(sDelPath, pvStg) then exit; end else exit; end else exit; end; if pvStg = nil then exit; enum := nil; if StgGetEnumElements(pvStg, enum) then Result := StgGetStatStgLike(enum, 'DRMContent', elt); finally enum := nil; pvStg := nil; if sDelPath <> '' then DeleteFileForce(sDelPath); end; except on E: Exception do ETgException.TraceException(E, 'Fail .. IsAipEncrytedOldOfficeDoc()'); end; end; function GetAipLabelNameFromOldOfficeDoc(sPath: String): String; begin Result := 'Unknown'; end; function IsAipEncrytedOfficeDoc(sPath, sTempDir: String; nMode: Integer = STGM_READ or STGM_SHARE_EXCLUSIVE): Boolean; var pvStg: IStorage; sData: WideString; sDelPath: String; begin Result := false; try if sPath.ToUpper.StartsWith(sTempDir.ToUpper) then exit; // WriteLnFileEndUTF8('C:\ProgramData\HE\ov_log.txt', Format('IsAipEncrytedOfficeDoc() .. Path=%s, TempDir=%s', [sPath, sTempDir])); pvStg := nil; sDelPath := ''; try // 쉘 익스텐션 GetIconLocation() 에선 STGM_SHARE_EXCLUSIVE 모드 사용 시 스톱되서 아래처럼 처리 25_0122 14:03:25 kku if not StgStorageFileOpen(sPath, pvStg, nMode) then begin // MS 파일은 사용중이면 안열려서 이렇게 처리 24_1114 09:24:47 kku if (sTempDir <> '') and ForceDirectories(sTempDir) then begin // MS 파일은 사용중이면 안열려서 이렇게 처리 24_1114 09:24:47 kku sDelPath := sTempDir + '$' + ExtractFileName(sPath); if CopyFile(PChar(sPath), PChar(sDelPath), false) then begin if not StgStorageFileOpen(sDelPath, pvStg) then exit; end else exit; end else exit; end; if pvStg = nil then exit; Result := StgGetStreamToText(pvStg, 'EncryptedPackage', sData); finally pvStg := nil; if sDelPath <> '' then DeleteFileForce(sDelPath); end; except on E: Exception do ETgException.TraceException(E, 'Fail .. IsAipEncrytedOldOfficeDoc()'); end; end; function GetAipLabelNameFromOfficeDoc(sPath: String): String; begin Result := 'Unknown'; end; // MS문서 중 .pfile로 변경 가능성이 있는 확장자인지 체크 25_1222 17:52:57 kku function CheckMsPfileExt(sPath: String): Boolean; var sExt: String; begin sExt := GetFileExt(sPath).ToUpper; Result := (sExt = 'DOC') or (sExt = 'XLS') or (sExt = 'PPT') or (sExt = 'DOT') or (sExt = 'PPS') or (sExt = 'POT') or (sExt = 'XLT') or (sExt = 'XLTM') or (sExt = 'XLTX') or (sExt = 'XPS') or (sExt = 'DOTM') or (sExt = 'PPSM') or (sExt = 'PPSX') or (sExt = 'PPTM') or (sExt = 'XPS'); end; function CheckAipEncSign(sPath: String): Boolean; begin Result := false; try if not FileExists(sPath) then exit; Result := CheckSign(sPath, @SIGN_AIP_DRM[0], 24); except on E: Exception do ETgException.TraceException(E, 'Fail .. CheckAipEncSign()'); end; end; function IsAipEncryted(sPath: String; sTempDir: String = 'C:\ProgramData\HE\AEN\'; nMode: Integer = STGM_READ or STGM_SHARE_EXCLUSIVE): Boolean; var sExt: String; begin Result := false; try if not FileExists(sPath) then exit; if sPath.ToUpper.StartsWith(sTempDir.ToUpper) then exit; // WriteLnFileEndUTF8('C:\ProgramData\HE\ov_log.txt', Format('IsAipEncryted() .. Path=%s, TempDir=%s', [sPath, sTempDir])); Result := CheckAipEncSign(sPath); if not Result then begin sExt := GetFileExt(sPath).ToUpper; if sExt = 'PDF' then begin Result := IsAipEncrytedPDF(sPath); end else if CheckMsPfileExt(sPath) then begin Result := IsAipEncrytedOldOfficeDoc(sPath, sTempDir, nMode); if not Result then Result := IsAipEncrytedOfficeDoc(sPath, sTempDir, nMode); end else if (sExt = 'DOCX') or (sExt = 'XLSX') or (sExt = 'PPTX') or (sExt = 'DOCM') or (sExt = 'DOTX') or (sExt = 'XLSM') or (sExt = 'XLSB') then begin Result := IsAipEncrytedOfficeDoc(sPath, sTempDir, nMode); end; end; except on E: Exception do ETgException.TraceException(E, 'Fail .. IsAipEncryted()'); end; end; function ConvAipEncExt(sPath: String): String; var sExt, sPathNoExt: String; begin Result := sPath; if sPath = '' then exit; sExt := GetFileExt(sPath).ToUpper; sPathNoExt := CutFileExt(sPath); if sExt = 'TXT' then Result := sPathNoExt + '.ptxt' else if sExt = 'XML' then Result := sPathNoExt + '.pxml' else if sExt = 'PNG' then Result := sPathNoExt + '.ppng' else if sExt = 'BMP' then Result := sPathNoExt + '.pbmp' else if sExt = 'JPG' then Result := sPathNoExt + '.pjpg' else if sExt = 'JPEG' then Result := sPathNoExt + '.pjpeg' else if (sExt <> 'DOCX') and (sExt <> 'XLSX') and (sExt <> 'PPTX') and (sExt <> 'PDF') and (sExt <> 'XLSB') and (sExt <> 'XLSM') and (sExt <> 'DOTX') and (sExt <> 'DOCM') and (sExt <> 'DOC') and (sExt <> 'XLS') and (sExt <> 'PPT') and (sExt <> 'DOT') and (sExt <> 'PPS') and (sExt <> 'POT') and (sExt <> 'XLT') and (sExt <> 'XLTM') and (sExt <> 'XLTX') and (sExt <> 'XPS') and (sExt <> 'DOTM') and (sExt <> 'PPSM') and (sExt <> 'PPSX') and (sExt <> 'PPTM') and (sExt <> 'XPS') then begin Result := sPath + '.pfile'; end; end; end.