mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-05 18:01:34 +00:00
524 lines
14 KiB
C
524 lines
14 KiB
C
/*
|
|
* Dump a shortcut (lnk) file
|
|
*
|
|
* Copyright 2005 Mike McCormack
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
#include "winedump.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
|
|
#include "pshpack1.h"
|
|
|
|
typedef enum {
|
|
SLDF_HAS_ID_LIST = 0x00000001,
|
|
SLDF_HAS_LINK_INFO = 0x00000002,
|
|
SLDF_HAS_NAME = 0x00000004,
|
|
SLDF_HAS_RELPATH = 0x00000008,
|
|
SLDF_HAS_WORKINGDIR = 0x00000010,
|
|
SLDF_HAS_ARGS = 0x00000020,
|
|
SLDF_HAS_ICONLOCATION = 0x00000040,
|
|
SLDF_UNICODE = 0x00000080,
|
|
SLDF_FORCE_NO_LINKINFO = 0x00000100,
|
|
SLDF_HAS_EXP_SZ = 0x00000200,
|
|
SLDF_RUN_IN_SEPARATE = 0x00000400,
|
|
SLDF_HAS_LOGO3ID = 0x00000800,
|
|
SLDF_HAS_DARWINID = 0x00001000,
|
|
SLDF_RUNAS_USER = 0x00002000,
|
|
SLDF_HAS_EXP_ICON_SZ = 0x00004000,
|
|
SLDF_NO_PIDL_ALIAS = 0x00008000,
|
|
SLDF_FORCE_UNCNAME = 0x00010000,
|
|
SLDF_RUN_WITH_SHIMLAYER = 0x00020000,
|
|
SLDF_FORCE_NO_LINKTRACK = 0x00040000,
|
|
SLDF_ENABLE_TARGET_METADATA = 0x00080000,
|
|
SLDF_DISABLE_KNOWNFOLDER_RELATIVE_TRACKING = 0x00200000,
|
|
SLDF_RESERVED = 0x80000000,
|
|
} SHELL_LINK_DATA_FLAGS;
|
|
|
|
#define EXP_SZ_LINK_SIG 0xa0000001
|
|
#define EXP_SPECIAL_FOLDER_SIG 0xa0000005
|
|
#define EXP_DARWIN_ID_SIG 0xa0000006
|
|
#define EXP_SZ_ICON_SIG 0xa0000007
|
|
|
|
typedef struct tagDATABLOCKHEADER
|
|
{
|
|
DWORD cbSize;
|
|
DWORD dwSignature;
|
|
} DATABLOCK_HEADER;
|
|
|
|
typedef struct _LINK_HEADER
|
|
{
|
|
DWORD dwSize; /* 0x00 size of the header - 0x4c */
|
|
GUID MagicGuid; /* 0x04 is CLSID_ShellLink */
|
|
DWORD dwFlags; /* 0x14 describes elements following */
|
|
DWORD dwFileAttr; /* 0x18 attributes of the target file */
|
|
FILETIME Time1; /* 0x1c */
|
|
FILETIME Time2; /* 0x24 */
|
|
FILETIME Time3; /* 0x2c */
|
|
DWORD dwFileLength; /* 0x34 File length */
|
|
DWORD nIcon; /* 0x38 icon number */
|
|
DWORD fStartup; /* 0x3c startup type */
|
|
DWORD wHotKey; /* 0x40 hotkey */
|
|
DWORD Unknown5; /* 0x44 */
|
|
DWORD Unknown6; /* 0x48 */
|
|
} LINK_HEADER, * PLINK_HEADER;
|
|
|
|
typedef struct tagLINK_SZ_BLOCK
|
|
{
|
|
DWORD size;
|
|
DWORD magic;
|
|
CHAR bufA[MAX_PATH];
|
|
WCHAR bufW[MAX_PATH];
|
|
} LINK_SZ_BLOCK;
|
|
|
|
typedef struct _LOCATION_INFO
|
|
{
|
|
DWORD dwTotalSize;
|
|
DWORD dwHeaderSize;
|
|
DWORD dwFlags;
|
|
DWORD dwVolTableOfs;
|
|
DWORD dwLocalPathOfs;
|
|
DWORD dwNetworkVolTableOfs;
|
|
DWORD dwFinalPathOfs;
|
|
} LOCATION_INFO;
|
|
|
|
typedef struct _LOCAL_VOLUME_INFO
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwType;
|
|
DWORD dwVolSerial;
|
|
DWORD dwVolLabelOfs;
|
|
} LOCAL_VOLUME_INFO;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD cbSize;
|
|
DWORD dwSignature;
|
|
DWORD idSpecialFolder;
|
|
DWORD cbOffset;
|
|
} EXP_SPECIAL_FOLDER;
|
|
|
|
typedef struct lnk_string_tag
|
|
{
|
|
unsigned short size;
|
|
union {
|
|
unsigned short w[1];
|
|
unsigned char a[1];
|
|
} str;
|
|
} lnk_string;
|
|
|
|
#include "poppack.h"
|
|
|
|
static unsigned offset;
|
|
|
|
static const void* fetch_block(void)
|
|
{
|
|
const unsigned* u;
|
|
const void* ret;
|
|
|
|
if (!(u = PRD(offset, sizeof(*u)))) return 0;
|
|
if ((ret = PRD(offset, *u))) offset += *u;
|
|
return ret;
|
|
}
|
|
|
|
static const lnk_string* fetch_string(int unicode)
|
|
{
|
|
const unsigned short* s;
|
|
unsigned short len;
|
|
const void* ret;
|
|
|
|
if (!(s = PRD(offset, sizeof(*s)))) return 0;
|
|
len = *s * (unicode ? sizeof(WCHAR) : sizeof(char));
|
|
if ((ret = PRD(offset, sizeof(*s) + len))) offset += sizeof(*s) + len;
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int dump_pidl(void)
|
|
{
|
|
const lnk_string *pidl;
|
|
int i, n = 0, sz = 0;
|
|
|
|
pidl = fetch_string(FALSE);
|
|
if (!pidl)
|
|
return -1;
|
|
|
|
printf("PIDL\n");
|
|
printf("----\n\n");
|
|
|
|
while(sz<pidl->size)
|
|
{
|
|
const lnk_string *segment = (const lnk_string*) &pidl->str.a[sz];
|
|
|
|
if(!segment->size)
|
|
break;
|
|
sz+=segment->size;
|
|
if(sz>pidl->size)
|
|
{
|
|
printf("bad pidl\n");
|
|
break;
|
|
}
|
|
n++;
|
|
printf("segment %d (%2d bytes) : ",n,segment->size);
|
|
for(i=0; i<segment->size; i++)
|
|
printf("%02x ",segment->str.a[i]);
|
|
printf("\n");
|
|
}
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dump_string(const char *what, int unicode)
|
|
{
|
|
const lnk_string *data;
|
|
unsigned sz;
|
|
|
|
data = fetch_string(unicode);
|
|
if (!data)
|
|
return -1;
|
|
printf("%s : ", what);
|
|
sz = data->size;
|
|
if (unicode)
|
|
while (sz) printf("%c", data->str.w[data->size - sz--]);
|
|
else
|
|
while (sz) printf("%c", data->str.a[data->size - sz--]);
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dump_location(void)
|
|
{
|
|
const LOCATION_INFO *loc;
|
|
const char *p;
|
|
|
|
loc = fetch_block();
|
|
if (!loc)
|
|
return -1;
|
|
p = (const char*)loc;
|
|
|
|
printf("Location\n");
|
|
printf("--------\n\n");
|
|
printf("Total size = %d\n", loc->dwTotalSize);
|
|
printf("Header size = %d\n", loc->dwHeaderSize);
|
|
printf("Flags = %08x\n", loc->dwFlags);
|
|
|
|
/* dump out information about the volume the link points to */
|
|
printf("Volume ofs = %08x ", loc->dwVolTableOfs);
|
|
if (loc->dwVolTableOfs && (loc->dwVolTableOfs<loc->dwTotalSize))
|
|
{
|
|
const LOCAL_VOLUME_INFO *vol = (const LOCAL_VOLUME_INFO *)&p[loc->dwVolTableOfs];
|
|
|
|
printf("size %d type %d serial %08x label %d ",
|
|
vol->dwSize, vol->dwType, vol->dwVolSerial, vol->dwVolLabelOfs);
|
|
if(vol->dwVolLabelOfs)
|
|
printf("(\"%s\")", &p[loc->dwVolTableOfs + vol->dwVolLabelOfs]);
|
|
}
|
|
printf("\n");
|
|
|
|
/* dump out the path the link points to */
|
|
printf("LocalPath ofs = %08x ", loc->dwLocalPathOfs);
|
|
if( loc->dwLocalPathOfs && (loc->dwLocalPathOfs < loc->dwTotalSize) )
|
|
printf("(\"%s\")", &p[loc->dwLocalPathOfs]);
|
|
printf("\n");
|
|
|
|
printf("Net Path ofs = %08x\n", loc->dwNetworkVolTableOfs);
|
|
printf("Final Path = %08x ", loc->dwFinalPathOfs);
|
|
if( loc->dwFinalPathOfs && (loc->dwFinalPathOfs < loc->dwTotalSize) )
|
|
printf("(\"%s\")", &p[loc->dwFinalPathOfs]);
|
|
printf("\n");
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const unsigned char table_dec85[0x80] = {
|
|
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
|
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
|
0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
|
|
0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
|
|
0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
|
|
0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
|
|
0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
|
|
0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
|
|
};
|
|
|
|
static int base85_to_guid( const char *str, LPGUID guid )
|
|
{
|
|
DWORD i, val = 0, base = 1, *p;
|
|
unsigned char ch;
|
|
|
|
p = (DWORD*) guid;
|
|
for( i=0; i<20; i++ )
|
|
{
|
|
if( (i%5) == 0 )
|
|
{
|
|
val = 0;
|
|
base = 1;
|
|
}
|
|
ch = str[i];
|
|
if( ch >= 0x80 )
|
|
return 0;
|
|
val += table_dec85[ch] * base;
|
|
if( table_dec85[ch] == 0xff )
|
|
return 0;
|
|
if( (i%5) == 4 )
|
|
p[i/5] = val;
|
|
base *= 85;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int dump_special_folder_block(const DATABLOCK_HEADER* bhdr)
|
|
{
|
|
const EXP_SPECIAL_FOLDER *sfb = (const EXP_SPECIAL_FOLDER*)bhdr;
|
|
printf("Special folder block\n");
|
|
printf("--------------------\n\n");
|
|
printf("folder = 0x%04x\n", sfb->idSpecialFolder);
|
|
printf("offset = %d\n", sfb->cbOffset);
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
static int dump_sz_block(const DATABLOCK_HEADER* bhdr, const char* label)
|
|
{
|
|
const LINK_SZ_BLOCK *szp = (const LINK_SZ_BLOCK*)bhdr;
|
|
printf("String block\n");
|
|
printf("-----------\n\n");
|
|
printf("magic = %x\n", szp->magic);
|
|
printf("%s = %s\n", label, szp->bufA);
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
static int dump_darwin_id(const DATABLOCK_HEADER* bhdr)
|
|
{
|
|
const LINK_SZ_BLOCK *szp = (const LINK_SZ_BLOCK*)bhdr;
|
|
char comp_str[40];
|
|
const char *feat, *comp, *prod_str, *feat_str;
|
|
GUID guid;
|
|
|
|
printf("Advertise Info\n");
|
|
printf("--------------\n\n");
|
|
printf("msi string = %s\n", szp->bufA);
|
|
|
|
if (base85_to_guid(szp->bufA, &guid))
|
|
prod_str = get_guid_str(&guid);
|
|
else
|
|
prod_str = "?";
|
|
|
|
comp = &szp->bufA[20];
|
|
feat = strchr(comp, '>');
|
|
if (!feat)
|
|
feat = strchr(comp, '<');
|
|
if (feat)
|
|
{
|
|
memcpy(comp_str, comp, feat - comp);
|
|
comp_str[feat-comp] = 0;
|
|
}
|
|
else
|
|
{
|
|
strcpy(comp_str, "?");
|
|
}
|
|
|
|
if (feat && feat[0] == '>' && base85_to_guid( &feat[1], &guid ))
|
|
feat_str = get_guid_str( &guid );
|
|
else
|
|
feat_str = "";
|
|
|
|
printf(" product: %s\n", prod_str);
|
|
printf(" component: %s\n", comp_str );
|
|
printf(" feature: %s\n", feat_str);
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dump_raw_block(const DATABLOCK_HEADER* bhdr)
|
|
{
|
|
int data_size;
|
|
|
|
printf("Raw Block\n");
|
|
printf("---------\n\n");
|
|
printf("size = %d\n", bhdr->cbSize);
|
|
printf("magic = %x\n", bhdr->dwSignature);
|
|
|
|
data_size=bhdr->cbSize-sizeof(*bhdr);
|
|
if (data_size > 0)
|
|
{
|
|
int i;
|
|
const unsigned char *data;
|
|
|
|
printf("data = ");
|
|
data=(const unsigned char*)bhdr+sizeof(*bhdr);
|
|
while (data_size > 0)
|
|
{
|
|
for (i=0; i < 16; i++)
|
|
{
|
|
if (i < data_size)
|
|
printf("%02x ", data[i]);
|
|
else
|
|
printf(" ");
|
|
}
|
|
for (i=0; i < data_size && i < 16; i++)
|
|
printf("%c", (data[i] >= 32 && data[i] < 128 ? data[i] : '.'));
|
|
printf("\n");
|
|
data_size-=16;
|
|
if (data_size <= 0)
|
|
break;
|
|
data+=16;
|
|
printf(" ");
|
|
}
|
|
}
|
|
printf("\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static const GUID CLSID_ShellLink = {0x00021401L, 0, 0, {0xC0,0,0,0,0,0,0,0x46}};
|
|
|
|
enum FileSig get_kind_lnk(void)
|
|
{
|
|
const LINK_HEADER* hdr;
|
|
|
|
hdr = PRD(0, sizeof(*hdr));
|
|
if (hdr && hdr->dwSize == sizeof(LINK_HEADER) &&
|
|
!memcmp(&hdr->MagicGuid, &CLSID_ShellLink, sizeof(GUID)))
|
|
return SIG_LNK;
|
|
return SIG_UNKNOWN;
|
|
}
|
|
|
|
void lnk_dump(void)
|
|
{
|
|
const LINK_HEADER* hdr;
|
|
const DATABLOCK_HEADER* bhdr;
|
|
DWORD dwFlags;
|
|
|
|
offset = 0;
|
|
hdr = fetch_block();
|
|
if (!hdr)
|
|
return;
|
|
|
|
printf("Header\n");
|
|
printf("------\n\n");
|
|
printf("Size: %04x\n", hdr->dwSize);
|
|
printf("GUID: %s\n", get_guid_str(&hdr->MagicGuid));
|
|
|
|
printf("FileAttr: %08x\n", hdr->dwFileAttr);
|
|
printf("FileLength: %08x\n", hdr->dwFileLength);
|
|
printf("nIcon: %d\n", hdr->nIcon);
|
|
printf("Startup: %d\n", hdr->fStartup);
|
|
printf("HotKey: %08x\n", hdr->wHotKey);
|
|
printf("Unknown5: %08x\n", hdr->Unknown5);
|
|
printf("Unknown6: %08x\n", hdr->Unknown6);
|
|
|
|
/* dump out all the flags */
|
|
printf("Flags: %04x ( ", hdr->dwFlags);
|
|
dwFlags=hdr->dwFlags;
|
|
#define FLAG(x) do \
|
|
{ \
|
|
if (dwFlags & SLDF_##x) \
|
|
{ \
|
|
printf("%s ", #x); \
|
|
dwFlags&=~SLDF_##x; \
|
|
} \
|
|
} while (0)
|
|
FLAG(HAS_ID_LIST);
|
|
FLAG(HAS_LINK_INFO);
|
|
FLAG(HAS_NAME);
|
|
FLAG(HAS_RELPATH);
|
|
FLAG(HAS_WORKINGDIR);
|
|
FLAG(HAS_ARGS);
|
|
FLAG(HAS_ICONLOCATION);
|
|
FLAG(UNICODE);
|
|
FLAG(FORCE_NO_LINKINFO);
|
|
FLAG(HAS_EXP_SZ);
|
|
FLAG(RUN_IN_SEPARATE);
|
|
FLAG(HAS_LOGO3ID);
|
|
FLAG(HAS_DARWINID);
|
|
FLAG(RUNAS_USER);
|
|
FLAG(HAS_EXP_ICON_SZ);
|
|
FLAG(NO_PIDL_ALIAS);
|
|
FLAG(FORCE_UNCNAME);
|
|
FLAG(RUN_WITH_SHIMLAYER);
|
|
FLAG(FORCE_NO_LINKTRACK);
|
|
FLAG(ENABLE_TARGET_METADATA);
|
|
FLAG(DISABLE_KNOWNFOLDER_RELATIVE_TRACKING);
|
|
FLAG(RESERVED);
|
|
#undef FLAG
|
|
if (dwFlags)
|
|
printf("+%04x", dwFlags);
|
|
printf(")\n");
|
|
|
|
printf("Length: %04x\n", hdr->dwFileLength);
|
|
printf("\n");
|
|
|
|
if (hdr->dwFlags & SLDF_HAS_ID_LIST)
|
|
dump_pidl();
|
|
if (hdr->dwFlags & SLDF_HAS_LINK_INFO)
|
|
dump_location();
|
|
if (hdr->dwFlags & SLDF_HAS_NAME)
|
|
dump_string("Description", hdr->dwFlags & SLDF_UNICODE);
|
|
if (hdr->dwFlags & SLDF_HAS_RELPATH)
|
|
dump_string("Relative path", hdr->dwFlags & SLDF_UNICODE);
|
|
if (hdr->dwFlags & SLDF_HAS_WORKINGDIR)
|
|
dump_string("Working directory", hdr->dwFlags & SLDF_UNICODE);
|
|
if (hdr->dwFlags & SLDF_HAS_ARGS)
|
|
dump_string("Arguments", hdr->dwFlags & SLDF_UNICODE);
|
|
if (hdr->dwFlags & SLDF_HAS_ICONLOCATION)
|
|
dump_string("Icon path", hdr->dwFlags & SLDF_UNICODE);
|
|
|
|
bhdr=fetch_block();
|
|
while (bhdr)
|
|
{
|
|
if (!bhdr->cbSize)
|
|
break;
|
|
switch (bhdr->dwSignature)
|
|
{
|
|
case EXP_SZ_LINK_SIG:
|
|
dump_sz_block(bhdr, "exp.link");
|
|
break;
|
|
case EXP_SPECIAL_FOLDER_SIG:
|
|
dump_special_folder_block(bhdr);
|
|
break;
|
|
case EXP_SZ_ICON_SIG:
|
|
dump_sz_block(bhdr, "icon");
|
|
break;
|
|
case EXP_DARWIN_ID_SIG:
|
|
dump_darwin_id(bhdr);
|
|
break;
|
|
default:
|
|
dump_raw_block(bhdr);
|
|
}
|
|
bhdr=fetch_block();
|
|
}
|
|
}
|