{*******************************************************} { } { Tocsg.Prefetch } { } { Copyright (C) 2022 kku } { } {*******************************************************} unit Tocsg.Prefetch; interface uses Tocsg.Obj, System.SysUtils, System.Classes, Winapi.Windows, System.Generics.Collections; const PF_VER_1 = 17; PF_VER_2 = 23; PF_VER_3 = 26; PF_VER_4 = 30; type TPfHeader = record dwVer: DWORD; arrSig: array[0..3] of AnsiChar; dwUnknown1, dwFSize: DWORD; arrFName: array[0..29] of Char; // array[0..59] of AnsiChar; dwPfHash, dwUnknown2: DWORD; end; TPfFileInfoVer1 = record dwOffset, dwEntCnt, dwOffsetTrace, dwEntCntTrace, dwOffsetFName, dwFNameSize, dwOffsetVolInfo, dwVolNum, dwVolSize: DWORD; ftLastExeTime: TFileTime; arrUnknown1: array [0..15] of AnsiChar; dwExeCounter: DWORD; dwUnknown2: DWORD; end; TPfFileInfoVer2 = record dwOffset, dwEntCnt, dwOffsetTrace, dwEntCntTrace, dwOffsetFName, dwFNameSize, dwOffsetVolInfo, dwVolNum, dwVolSize: DWORD; arrUnknown1: array [0..7] of AnsiChar; ftLastExeTime: TFileTime; arrUnknown2: array [0..15] of AnsiChar; dwExeCounter: DWORD; arrUnknown3: array [0..83] of AnsiChar; end; TPfFileInfoVer34 = record dwOffset, dwEntCnt, dwOffsetTrace, dwEntCntTrace, dwOffsetFName, dwFNameSize, dwOffsetVolInfo, dwVolNum, dwVolSize: DWORD; arrUnknown1: array [0..7] of AnsiChar; arrLastExeTime: array [0..7] of TFileTime; arrUnknown2: array [0..15] of AnsiChar; dwExeCounter: DWORD; arrUnknown3: array [0..95] of AnsiChar; end; TAssocFileEntVer1 = record dwStartTime, dwDuration, dwFNameOffset, dwFNameLen, dwUnknown: DWORD; end; TAssocFileEntVer234 = record dwStartTime, dwDuration, dwAverageDuration, dwFNameOffset, dwFNameLen, dwUnknown: DWORD; arrRefNTFS: array [0..7] of AnsiChar; end; TTgPrefetchAnal = class(TTgObject) private ms_: TMemoryStream; PfHeader_: TPfHeader; nRunCnt_: Integer; ExeDtList_: TList; AssocFList_: TStringList; sPfPath_, sPath_, sFName_: String; function GetPfHeader(aStream: TStream): Boolean; procedure ExtrPfInfoVer1; procedure ExtrPfInfoVer2; procedure ExtrPfInfoVer34; function GetRunCount: Integer; public Constructor Create; Destructor Destroy; override; function LoadFromStream(aStream: TStream): Boolean; function LoadFromFile(sPath: String): Boolean; procedure Clear; function GetExeDateTimeToText(sDm: String = ''; sDtFormat: String = ''): String; function GetExeDateEnum: TEnumerator; property RunCount: Integer read GetRunCount; property ExeDtList: TList read ExeDtList_; property FilePath: String read sPath_; property FileName: String read sFName_; end; implementation uses Tocsg.Safe, Tocsg.Exception, Tocsg.NTDLL.Decompress, Tocsg.DateTime, Tocsg.Strings, System.Math, Tocsg.Path; { TTgPrefetchAnal } Constructor TTgPrefetchAnal.Create; begin Inherited Create; sPfPath_ := ''; sPath_ := ''; sFName_ := ''; ms_ := nil; ExeDtList_ := TList.Create; AssocFList_ := TStringList.Create; end; Destructor TTgPrefetchAnal.Destroy; begin FreeAndNil(AssocFList_); FreeAndNil(ExeDtList_); if ms_ <> nil then FreeAndNil(ms_); Inherited; end; function TTgPrefetchAnal.GetPfHeader(aStream: TStream): Boolean; var RtlHeader: TRtlHeader; pSrc, pBuf: TBytes; dwDecomLen: DWORD; begin Result := false; if aStream.Size < SizeOf(PfHeader_) then exit; ZeroMemory(@PfHeader_, SizeOf(PfHeader_)); aStream.Position := 0; ZeroMemory(@RtlHeader, SizeOf(RtlHeader)); if aStream.Read(RtlHeader, SizeOf(RtlHeader)) <> SizeOf(RtlHeader) then exit; if not CompareMem(@RtlHeader.arrSig[0], @AnsiString('MAM')[1], 3) then exit; SetLength(pSrc, aStream.Size - aStream.Position); aStream.Read(pSrc[0], Length(pSrc)); SetLength(pBuf, RtlHeader.dwSize); dwDecomLen := RtlDecompress(@pSrc[0], @pBuf[0], Length(pSrc), RtlHeader.dwSize, COMPRESSION_FORMAT_XPRESS_HUFF); if dwDecomLen > 0 then begin ms_ := TMemoryStream.Create; ms_.Write(pBuf[0], dwDecomLen); ms_.Position := 0; ms_.Read(PfHeader_, SizeOf(PfHeader_)); if PfHeader_.arrSig = 'SCCA' then Result := true; end; end; procedure TTgPrefetchAnal.ExtrPfInfoVer1; var FInfo: TPfFileInfoVer1; begin if ms_ = nil then exit; ms_.Read(FInfo, SizeOf(FInfo)); nRunCnt_ := FInfo.dwExeCounter; ExeDtList_.Add(ConvFileTimeToDateTime(FInfo.ftLastExeTime)); end; procedure TTgPrefetchAnal.ExtrPfInfoVer2; var FInfo: TPfFileInfoVer2; begin if ms_ = nil then exit; ms_.Read(FInfo, SizeOf(FInfo)); nRunCnt_ := FInfo.dwExeCounter; ExeDtList_.Add(ConvFileTimeToDateTime(FInfo.ftLastExeTime)); end; procedure TTgPrefetchAnal.ExtrPfInfoVer34; var FInfo: TPfFileInfoVer34; i: Integer; dwPos, dwEntSize, dwNameOffset: DWORD; AssocFileEnt: TAssocFileEntVer234; pBuf: TBytes; sPath: String; begin if ms_ = nil then exit; ms_.Read(FInfo, SizeOf(FInfo)); nRunCnt_ := FInfo.dwExeCounter; for i := Low(FInfo.arrLastExeTime) to High(FInfo.arrLastExeTime) do if (FInfo.arrLastExeTime[i].dwLowDateTime > 0) and (FInfo.arrLastExeTime[i].dwHighDateTime > 0) then ExeDtList_.Add(ConvFileTimeToDateTime_Local(FInfo.arrLastExeTime[i])); // 로컬 타임으로 가져옴 22_0905 15:54:59 kku // ExeDtList_.Add(ConvFileTimeToDateTime(FInfo.arrLastExeTime[i])); SetLength(pBuf, 1024); dwPos := FInfo.dwOffset; dwNameOffset := FInfo.dwOffsetFName; dwEntSize := SizeOf(AssocFileEnt); for i := 0 to FInfo.dwEntCnt - 1 do if dwPos < ms_.Size then begin ms_.Position := dwPos; if ms_.Read(AssocFileEnt, dwEntSize) <> dwEntSize then exit; dwPos := ms_.Position; with AssocFileEnt do begin if (dwNameOffset + dwFNameOffset + (dwFNameLen * 2)) < ms_.Size then begin ms_.Position := dwNameOffset + dwFNameOffset; ZeroMemory(pBuf, 1024); if ms_.Read(pBuf[0], dwFNameLen * 2) <> (dwFNameLen * 2) then exit; sPath := UpperCase(String(PChar(@pBuf[0]))); if GetFileExt(sPath) = 'EXE' then begin sPath_ := ExtractFilePath(sPath); sFName_ := ExtractFileName(sPath); end; AssocFList_.Add(sPath); end; end; end; end; function TTgPrefetchAnal.LoadFromStream(aStream: TStream): Boolean; begin Clear; Result := false; try if (aStream = nil) or (aStream.Size = 0) then exit; Result := GetPfHeader(aStream); if not Result then exit; case PfHeader_.dwVer of PF_VER_1 : ExtrPfInfoVer1; PF_VER_2 : ExtrPfInfoVer2; PF_VER_3, PF_VER_4 : ExtrPfInfoVer34; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. LoadFromStream()'); end; end; function TTgPrefetchAnal.LoadFromFile(sPath: String): Boolean; var fs: TFileStream; begin Result := false; if not FileExists(sPath) then exit; try sPfPath_ := sPath; Guard(fs, TFileStream.Create(sPath, fmOpenRead)); Result := LoadFromStream(fs); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. LoadFromFile()'); end; end; procedure TTgPrefetchAnal.Clear; begin if ms_ <> nil then FreeAndNil(ms_); Finalize(PfHeader_); ZeroMemory(@PfHeader_, SizeOf(PfHeader_)); nRunCnt_ := 0; ExeDtList_.Clear; AssocFList_.Clear; sPfPath_ := ''; sFName_ := ''; sPath_ := ''; end; function TTgPrefetchAnal.GetRunCount: Integer; begin Result := Max(nRunCnt_, ExeDtList_.Count); end; function TTgPrefetchAnal.GetExeDateTimeToText(sDm: String = ''; sDtFormat: String = ''): String; var i: Integer; begin Result := ''; try if sDm = '' then sDm := ','; for i := 0 to ExeDtList_.Count - 1 do if sDtFormat <> '' then SumString(Result, FormatDateTime(sDtFormat, ExeDtList_[i]), sDm) else SumString(Result, DateTimeToStr(ExeDtList_[i]), sDm); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetExeDateTimeToCommaText()'); end; end; function TTgPrefetchAnal.GetExeDateEnum: TEnumerator; begin Result := ExeDtList_.GetEnumerator; end; end.