1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2009-2014, Mario Vilas
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are met:
9#
10#     * Redistributions of source code must retain the above copyright notice,
11#       this list of conditions and the following disclaimer.
12#     * Redistributions in binary form must reproduce the above copyright
13#       notice,this list of conditions and the following disclaimer in the
14#       documentation and/or other materials provided with the distribution.
15#     * Neither the name of the copyright holder nor the names of its
16#       contributors may be used to endorse or promote products derived from
17#       this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29# POSSIBILITY OF SUCH DAMAGE.
30
31"""
32Wrapper for shell32.dll in ctypes.
33"""
34
35# TODO
36# * Add a class wrapper to SHELLEXECUTEINFO
37# * More logic into ShellExecuteEx
38
39__revision__ = "$Id$"
40
41from winappdbg.win32.defines import *
42from winappdbg.win32.kernel32 import LocalFree
43
44#==============================================================================
45# This is used later on to calculate the list of exported symbols.
46_all = None
47_all = set(vars().keys())
48#==============================================================================
49
50#--- Constants ----------------------------------------------------------------
51
52SEE_MASK_DEFAULT            = 0x00000000
53SEE_MASK_CLASSNAME          = 0x00000001
54SEE_MASK_CLASSKEY           = 0x00000003
55SEE_MASK_IDLIST             = 0x00000004
56SEE_MASK_INVOKEIDLIST       = 0x0000000C
57SEE_MASK_ICON               = 0x00000010
58SEE_MASK_HOTKEY             = 0x00000020
59SEE_MASK_NOCLOSEPROCESS     = 0x00000040
60SEE_MASK_CONNECTNETDRV      = 0x00000080
61SEE_MASK_NOASYNC            = 0x00000100
62SEE_MASK_DOENVSUBST         = 0x00000200
63SEE_MASK_FLAG_NO_UI         = 0x00000400
64SEE_MASK_UNICODE            = 0x00004000
65SEE_MASK_NO_CONSOLE         = 0x00008000
66SEE_MASK_ASYNCOK            = 0x00100000
67SEE_MASK_HMONITOR           = 0x00200000
68SEE_MASK_NOZONECHECKS       = 0x00800000
69SEE_MASK_WAITFORINPUTIDLE   = 0x02000000
70SEE_MASK_FLAG_LOG_USAGE     = 0x04000000
71
72SE_ERR_FNF              = 2
73SE_ERR_PNF              = 3
74SE_ERR_ACCESSDENIED     = 5
75SE_ERR_OOM              = 8
76SE_ERR_DLLNOTFOUND      = 32
77SE_ERR_SHARE            = 26
78SE_ERR_ASSOCINCOMPLETE  = 27
79SE_ERR_DDETIMEOUT       = 28
80SE_ERR_DDEFAIL          = 29
81SE_ERR_DDEBUSY          = 30
82SE_ERR_NOASSOC          = 31
83
84SHGFP_TYPE_CURRENT = 0
85SHGFP_TYPE_DEFAULT = 1
86
87CSIDL_DESKTOP                   = 0x0000
88CSIDL_INTERNET                  = 0x0001
89CSIDL_PROGRAMS                  = 0x0002
90CSIDL_CONTROLS                  = 0x0003
91CSIDL_PRINTERS                  = 0x0004
92CSIDL_PERSONAL                  = 0x0005
93CSIDL_FAVORITES                 = 0x0006
94CSIDL_STARTUP                   = 0x0007
95CSIDL_RECENT                    = 0x0008
96CSIDL_SENDTO                    = 0x0009
97CSIDL_BITBUCKET                 = 0x000a
98CSIDL_STARTMENU                 = 0x000b
99CSIDL_MYDOCUMENTS               = CSIDL_PERSONAL
100CSIDL_MYMUSIC                   = 0x000d
101CSIDL_MYVIDEO                   = 0x000e
102CSIDL_DESKTOPDIRECTORY          = 0x0010
103CSIDL_DRIVES                    = 0x0011
104CSIDL_NETWORK                   = 0x0012
105CSIDL_NETHOOD                   = 0x0013
106CSIDL_FONTS                     = 0x0014
107CSIDL_TEMPLATES                 = 0x0015
108CSIDL_COMMON_STARTMENU          = 0x0016
109CSIDL_COMMON_PROGRAMS           = 0x0017
110CSIDL_COMMON_STARTUP            = 0x0018
111CSIDL_COMMON_DESKTOPDIRECTORY   = 0x0019
112CSIDL_APPDATA                   = 0x001a
113CSIDL_PRINTHOOD                 = 0x001b
114CSIDL_LOCAL_APPDATA             = 0x001c
115CSIDL_ALTSTARTUP                = 0x001d
116CSIDL_COMMON_ALTSTARTUP         = 0x001e
117CSIDL_COMMON_FAVORITES          = 0x001f
118CSIDL_INTERNET_CACHE            = 0x0020
119CSIDL_COOKIES                   = 0x0021
120CSIDL_HISTORY                   = 0x0022
121CSIDL_COMMON_APPDATA            = 0x0023
122CSIDL_WINDOWS                   = 0x0024
123CSIDL_SYSTEM                    = 0x0025
124CSIDL_PROGRAM_FILES             = 0x0026
125CSIDL_MYPICTURES                = 0x0027
126CSIDL_PROFILE                   = 0x0028
127CSIDL_SYSTEMX86                 = 0x0029
128CSIDL_PROGRAM_FILESX86          = 0x002a
129CSIDL_PROGRAM_FILES_COMMON      = 0x002b
130CSIDL_PROGRAM_FILES_COMMONX86   = 0x002c
131CSIDL_COMMON_TEMPLATES          = 0x002d
132CSIDL_COMMON_DOCUMENTS          = 0x002e
133CSIDL_COMMON_ADMINTOOLS         = 0x002f
134CSIDL_ADMINTOOLS                = 0x0030
135CSIDL_CONNECTIONS               = 0x0031
136CSIDL_COMMON_MUSIC              = 0x0035
137CSIDL_COMMON_PICTURES           = 0x0036
138CSIDL_COMMON_VIDEO              = 0x0037
139CSIDL_RESOURCES                 = 0x0038
140CSIDL_RESOURCES_LOCALIZED       = 0x0039
141CSIDL_COMMON_OEM_LINKS          = 0x003a
142CSIDL_CDBURN_AREA               = 0x003b
143CSIDL_COMPUTERSNEARME           = 0x003d
144CSIDL_PROFILES                  = 0x003e
145
146CSIDL_FOLDER_MASK               = 0x00ff
147
148CSIDL_FLAG_PER_USER_INIT        = 0x0800
149CSIDL_FLAG_NO_ALIAS             = 0x1000
150CSIDL_FLAG_DONT_VERIFY          = 0x4000
151CSIDL_FLAG_CREATE               = 0x8000
152
153CSIDL_FLAG_MASK                 = 0xff00
154
155#--- Structures ---------------------------------------------------------------
156
157# typedef struct _SHELLEXECUTEINFO {
158#   DWORD     cbSize;
159#   ULONG     fMask;
160#   HWND      hwnd;
161#   LPCTSTR   lpVerb;
162#   LPCTSTR   lpFile;
163#   LPCTSTR   lpParameters;
164#   LPCTSTR   lpDirectory;
165#   int       nShow;
166#   HINSTANCE hInstApp;
167#   LPVOID    lpIDList;
168#   LPCTSTR   lpClass;
169#   HKEY      hkeyClass;
170#   DWORD     dwHotKey;
171#   union {
172#     HANDLE hIcon;
173#     HANDLE hMonitor;
174#   } DUMMYUNIONNAME;
175#   HANDLE    hProcess;
176# } SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO;
177
178class SHELLEXECUTEINFO(Structure):
179    _fields_ = [
180        ("cbSize",       DWORD),
181        ("fMask",        ULONG),
182        ("hwnd",         HWND),
183        ("lpVerb",       LPSTR),
184        ("lpFile",       LPSTR),
185        ("lpParameters", LPSTR),
186        ("lpDirectory",  LPSTR),
187        ("nShow",        ctypes.c_int),
188        ("hInstApp",     HINSTANCE),
189        ("lpIDList",     LPVOID),
190        ("lpClass",      LPSTR),
191        ("hkeyClass",    HKEY),
192        ("dwHotKey",     DWORD),
193        ("hIcon",        HANDLE),
194        ("hProcess",     HANDLE),
195    ]
196
197    def __get_hMonitor(self):
198        return self.hIcon
199    def __set_hMonitor(self, hMonitor):
200        self.hIcon = hMonitor
201    hMonitor = property(__get_hMonitor, __set_hMonitor)
202
203LPSHELLEXECUTEINFO = POINTER(SHELLEXECUTEINFO)
204
205#--- shell32.dll --------------------------------------------------------------
206
207# LPWSTR *CommandLineToArgvW(
208#     LPCWSTR lpCmdLine,
209#     int *pNumArgs
210# );
211def CommandLineToArgvW(lpCmdLine):
212    _CommandLineToArgvW = windll.shell32.CommandLineToArgvW
213    _CommandLineToArgvW.argtypes = [LPVOID, POINTER(ctypes.c_int)]
214    _CommandLineToArgvW.restype  = LPVOID
215
216    if not lpCmdLine:
217        lpCmdLine = None
218    argc = ctypes.c_int(0)
219    vptr = ctypes.windll.shell32.CommandLineToArgvW(lpCmdLine, byref(argc))
220    if vptr == NULL:
221        raise ctypes.WinError()
222    argv = vptr
223    try:
224        argc = argc.value
225        if argc <= 0:
226            raise ctypes.WinError()
227        argv = ctypes.cast(argv, ctypes.POINTER(LPWSTR * argc) )
228        argv = [ argv.contents[i] for i in compat.xrange(0, argc) ]
229    finally:
230        if vptr is not None:
231            LocalFree(vptr)
232    return argv
233
234def CommandLineToArgvA(lpCmdLine):
235    t_ansi = GuessStringType.t_ansi
236    t_unicode = GuessStringType.t_unicode
237    if isinstance(lpCmdLine, t_ansi):
238        cmdline = t_unicode(lpCmdLine)
239    else:
240        cmdline = lpCmdLine
241    return [t_ansi(x) for x in CommandLineToArgvW(cmdline)]
242
243CommandLineToArgv = GuessStringType(CommandLineToArgvA, CommandLineToArgvW)
244
245# HINSTANCE ShellExecute(
246#     HWND hwnd,
247#     LPCTSTR lpOperation,
248#     LPCTSTR lpFile,
249#     LPCTSTR lpParameters,
250#     LPCTSTR lpDirectory,
251#     INT nShowCmd
252# );
253def ShellExecuteA(hwnd = None, lpOperation = None, lpFile = None, lpParameters = None, lpDirectory = None, nShowCmd = None):
254    _ShellExecuteA = windll.shell32.ShellExecuteA
255    _ShellExecuteA.argtypes = [HWND, LPSTR, LPSTR, LPSTR, LPSTR, INT]
256    _ShellExecuteA.restype  = HINSTANCE
257
258    if not nShowCmd:
259        nShowCmd = 0
260    success = _ShellExecuteA(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd)
261    success = ctypes.cast(success, c_int)
262    success = success.value
263    if not success > 32:    # weird! isn't it?
264        raise ctypes.WinError(success)
265
266def ShellExecuteW(hwnd = None, lpOperation = None, lpFile = None, lpParameters = None, lpDirectory = None, nShowCmd = None):
267    _ShellExecuteW = windll.shell32.ShellExecuteW
268    _ShellExecuteW.argtypes = [HWND, LPWSTR, LPWSTR, LPWSTR, LPWSTR, INT]
269    _ShellExecuteW.restype  = HINSTANCE
270
271    if not nShowCmd:
272        nShowCmd = 0
273    success = _ShellExecuteW(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd)
274    success = ctypes.cast(success, c_int)
275    success = success.value
276    if not success > 32:    # weird! isn't it?
277        raise ctypes.WinError(success)
278
279ShellExecute = GuessStringType(ShellExecuteA, ShellExecuteW)
280
281# BOOL ShellExecuteEx(
282#   __inout  LPSHELLEXECUTEINFO lpExecInfo
283# );
284def ShellExecuteEx(lpExecInfo):
285    if isinstance(lpExecInfo, SHELLEXECUTEINFOA):
286        ShellExecuteExA(lpExecInfo)
287    elif isinstance(lpExecInfo, SHELLEXECUTEINFOW):
288        ShellExecuteExW(lpExecInfo)
289    else:
290        raise TypeError("Expected SHELLEXECUTEINFOA or SHELLEXECUTEINFOW, got %s instead" % type(lpExecInfo))
291
292def ShellExecuteExA(lpExecInfo):
293    _ShellExecuteExA = windll.shell32.ShellExecuteExA
294    _ShellExecuteExA.argtypes = [LPSHELLEXECUTEINFOA]
295    _ShellExecuteExA.restype  = BOOL
296    _ShellExecuteExA.errcheck = RaiseIfZero
297    _ShellExecuteExA(byref(lpExecInfo))
298
299def ShellExecuteExW(lpExecInfo):
300    _ShellExecuteExW = windll.shell32.ShellExecuteExW
301    _ShellExecuteExW.argtypes = [LPSHELLEXECUTEINFOW]
302    _ShellExecuteExW.restype  = BOOL
303    _ShellExecuteExW.errcheck = RaiseIfZero
304    _ShellExecuteExW(byref(lpExecInfo))
305
306# HINSTANCE FindExecutable(
307#   __in      LPCTSTR lpFile,
308#   __in_opt  LPCTSTR lpDirectory,
309#   __out     LPTSTR lpResult
310# );
311def FindExecutableA(lpFile, lpDirectory = None):
312    _FindExecutableA = windll.shell32.FindExecutableA
313    _FindExecutableA.argtypes = [LPSTR, LPSTR, LPSTR]
314    _FindExecutableA.restype  = HINSTANCE
315
316    lpResult = ctypes.create_string_buffer(MAX_PATH)
317    success = _FindExecutableA(lpFile, lpDirectory, lpResult)
318    success = ctypes.cast(success, ctypes.c_void_p)
319    success = success.value
320    if not success > 32:    # weird! isn't it?
321        raise ctypes.WinError(success)
322    return lpResult.value
323
324def FindExecutableW(lpFile, lpDirectory = None):
325    _FindExecutableW = windll.shell32.FindExecutableW
326    _FindExecutableW.argtypes = [LPWSTR, LPWSTR, LPWSTR]
327    _FindExecutableW.restype  = HINSTANCE
328
329    lpResult = ctypes.create_unicode_buffer(MAX_PATH)
330    success = _FindExecutableW(lpFile, lpDirectory, lpResult)
331    success = ctypes.cast(success, ctypes.c_void_p)
332    success = success.value
333    if not success > 32:    # weird! isn't it?
334        raise ctypes.WinError(success)
335    return lpResult.value
336
337FindExecutable = GuessStringType(FindExecutableA, FindExecutableW)
338
339# HRESULT SHGetFolderPath(
340#   __in   HWND hwndOwner,
341#   __in   int nFolder,
342#   __in   HANDLE hToken,
343#   __in   DWORD dwFlags,
344#   __out  LPTSTR pszPath
345# );
346def SHGetFolderPathA(nFolder, hToken = None, dwFlags = SHGFP_TYPE_CURRENT):
347    _SHGetFolderPathA = windll.shell32.SHGetFolderPathA     # shfolder.dll in older win versions
348    _SHGetFolderPathA.argtypes = [HWND, ctypes.c_int, HANDLE, DWORD, LPSTR]
349    _SHGetFolderPathA.restype  = HRESULT
350    _SHGetFolderPathA.errcheck = RaiseIfNotZero # S_OK == 0
351
352    pszPath = ctypes.create_string_buffer(MAX_PATH + 1)
353    _SHGetFolderPathA(None, nFolder, hToken, dwFlags, pszPath)
354    return pszPath.value
355
356def SHGetFolderPathW(nFolder, hToken = None, dwFlags = SHGFP_TYPE_CURRENT):
357    _SHGetFolderPathW = windll.shell32.SHGetFolderPathW     # shfolder.dll in older win versions
358    _SHGetFolderPathW.argtypes = [HWND, ctypes.c_int, HANDLE, DWORD, LPWSTR]
359    _SHGetFolderPathW.restype  = HRESULT
360    _SHGetFolderPathW.errcheck = RaiseIfNotZero # S_OK == 0
361
362    pszPath = ctypes.create_unicode_buffer(MAX_PATH + 1)
363    _SHGetFolderPathW(None, nFolder, hToken, dwFlags, pszPath)
364    return pszPath.value
365
366SHGetFolderPath = DefaultStringType(SHGetFolderPathA, SHGetFolderPathW)
367
368# BOOL IsUserAnAdmin(void);
369def IsUserAnAdmin():
370    # Supposedly, IsUserAnAdmin() is deprecated in Vista.
371    # But I tried it on Windows 7 and it works just fine.
372    _IsUserAnAdmin = windll.shell32.IsUserAnAdmin
373    _IsUserAnAdmin.argtypes = []
374    _IsUserAnAdmin.restype  = bool
375    return _IsUserAnAdmin()
376
377#==============================================================================
378# This calculates the list of exported symbols.
379_all = set(vars().keys()).difference(_all)
380__all__ = [_x for _x in _all if not _x.startswith('_')]
381__all__.sort()
382#==============================================================================
383