首先在这里申明, 只用于学习, 不作其它用, 其它问题别找我.
因为这两天想把以前vcl写的播放器用fmx写下,加强下fmx的熟练度, 看到以前的图片资源又少,也不怎么好看,寻思着扒下kg的资源, 发现他的kugou.skn文件不是压缩文件, 于是想它是不是加密了,OD载入分析了下他并没有加密, 于是用WinHex打开文件, 明文发现他的前面是存储的文件名称, 往后面是图片的资源, 由是着手分析了下
首先前4个字节发现为文件的数量 32 07 00 00
接下来4字节为这条记录的长度
再来就1字节为文件名的长度, 文件名为Unicode格式
再最后4个字节 32 01 00 00 存储的为该文件的长度
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
循环读取文件长次数之后就是图片的起始位置了, 可以看到下面,没有加密的.,
用Delphi写了个类处理这个文件, 下面是代码
// 7.5.85.14422
// kugou.skn
// ying32
// QQ:1444386932
TKugouSkinRead = class
public
type
PKugouResRec = ^TKugouResRec;
TKugouResRec = packed record
Index: Integer;
FileSize: Integer;
FileName: string;
Position: Int64;
end;
private
FList: TList;
FFileName: string;
FMemStream: TMemoryStream;
function GetFileName(P: Pointer): string;
public
constructor Create;
destructor Destroy; override;
procedure LoadFromFile(AFileName: string);
procedure Clear;
procedure Extract(AIndex: Integer; out AStream: TMemoryStream); overload;
procedure Extract(AFileName: string; out AStream: TMemoryStream); overload;
procedure ExtractAll(ADir: string);
procedure ExtractToFile(AIndex: Integer; AFileName: string = '');
end;
implementation
{ TKugouSkinRead }
procedure TKugouSkinRead.Clear;
var
I: Integer;
begin
for I := 0 to FList.Count - 1 do Dispose(FList[I]);
FList.Clear;
end;
constructor TKugouSkinRead.Create;
begin
inherited Create;
FList := TList.Create;
FMemStream := TMemoryStream.Create;
end;
destructor TKugouSkinRead.Destroy;
begin
Clear;
FList.Free;
FMemStream.Free;
inherited;
end;
procedure TKugouSkinRead.Extract(AIndex: Integer; out AStream: TMemoryStream);
var
Buffer: array of Byte;
Item: PKugouResRec;
begin
if (AIndex >= 0) and (AIndex < FList.Count) and (Assigned(AStream)) then
begin
AStream.Position := 0;
Item := PKugouResRec(FList[AIndex]);
SetLength(Buffer, Item^.FileSize);
try
FMemStream.Position := Item^.Position;
FMemStream.Read(Pointer(Buffer)^, Item^.FileSize);
AStream.Write(Pointer(Buffer)^, Item^.FileSize);
AStream.Position := 0;
finally
SetLength(Buffer, 0);
end;
end;
end;
procedure TKugouSkinRead.Extract(AFileName: string;
out AStream: TMemoryStream);
var
I: Integer;
begin
for I := 0 to FList.Count - 1 do
if SameStr(LowerCase(GetFileName(FList[I])), LowerCase(AFileName)) then
begin
Extract(I, AStream);
Exit;
end;
end;
procedure TKugouSkinRead.ExtractAll(ADir: string);
var
I: Integer;
Mem: TMemoryStream;
begin
Mem := TMemoryStream.Create;
try
if not DirectoryExists(ADir) then
CreateDir(ADir);
if LastDelimiter('\', ADir) <> Length(ADir) then
ADir := ADir + '\';
for I := 0 to FList.Count - 1 do
begin
Mem.Clear;
Extract(I, Mem);
Mem.SaveToFile(ADir + GetFileName(FList[I]));
end;
finally
Mem.Free;
end;
end;
procedure TKugouSkinRead.ExtractToFile(AIndex: Integer; AFileName: string);
var
Mem: TMemoryStream;
begin
Mem := TMemoryStream.Create;
try
Extract(AIndex, Mem);
if Mem.Size > 0 then
if Length(AFileName) = 0 then
Mem.SaveToFile(GetFileName(FList[AIndex]))
else Mem.SaveToFile(AFileName);
finally
Mem.Free;
end;
end;
function TKugouSkinRead.GetFileName(P: Pointer): string;
begin
Result := '';
if Assigned(P) then
Result := PKugouResRec(P)^.FileName;
end;
procedure TKugouSkinRead.LoadFromFile(AFileName: string);
var
FileCount, I: Integer;
Item: PKugouResRec;
RecLen: Integer; // 每条记录长度
LNameLen: Byte; // 文件名的长度
LName: array[Byte] of Char;
FileStartPosi: Int64;
begin
Clear;
FFileName := AFileName;
try
FMemStream.LoadFromFile(AFileName);
FMemStream.Read(FileCount, 4);
for I := 1 to FileCount do
begin
New(Item);
Item^.Index := I;
FillChar(LName, SizeOf(LName), 0);
FMemStream.Read(RecLen, 4);
FMemStream.Read(LNameLen, 1);
FMemStream.Read(LName, LNameLen);
Item^.FileName := string(LName);
FMemStream.Seek(RecLen - (4 + 1 + LNameLen) - 4, soCurrent);
FMemStream.Read(Item^.FileSize, 4);
FList.Add(Item);
end;
FileStartPosi := FMemStream.Position;
for I := 0 to FList.Count - 1 do
begin
Item := PKugouResRec(FList[I]);
Item^.Position := FileStartPosi;
Inc(FileStartPosi, Item^.FileSize);
end;
except
end;
end;