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"""
32CONTEXT structure for i386.
33"""
34
35__revision__ = "$Id$"
36
37from winappdbg.win32.defines import *
38from winappdbg.win32.version import ARCH_I386
39
40#==============================================================================
41# This is used later on to calculate the list of exported symbols.
42_all = None
43_all = set(vars().keys())
44#==============================================================================
45
46#--- CONTEXT structures and constants -----------------------------------------
47
48# The following values specify the type of access in the first parameter
49# of the exception record when the exception code specifies an access
50# violation.
51EXCEPTION_READ_FAULT        = 0     # exception caused by a read
52EXCEPTION_WRITE_FAULT       = 1     # exception caused by a write
53EXCEPTION_EXECUTE_FAULT     = 8     # exception caused by an instruction fetch
54
55CONTEXT_i386                = 0x00010000    # this assumes that i386 and
56CONTEXT_i486                = 0x00010000    # i486 have identical context records
57
58CONTEXT_CONTROL             = (CONTEXT_i386 | long(0x00000001)) # SS:SP, CS:IP, FLAGS, BP
59CONTEXT_INTEGER             = (CONTEXT_i386 | long(0x00000002)) # AX, BX, CX, DX, SI, DI
60CONTEXT_SEGMENTS            = (CONTEXT_i386 | long(0x00000004)) # DS, ES, FS, GS
61CONTEXT_FLOATING_POINT      = (CONTEXT_i386 | long(0x00000008)) # 387 state
62CONTEXT_DEBUG_REGISTERS     = (CONTEXT_i386 | long(0x00000010)) # DB 0-3,6,7
63CONTEXT_EXTENDED_REGISTERS  = (CONTEXT_i386 | long(0x00000020)) # cpu specific extensions
64
65CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)
66
67CONTEXT_ALL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | \
68                CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | \
69                CONTEXT_EXTENDED_REGISTERS)
70
71SIZE_OF_80387_REGISTERS     = 80
72MAXIMUM_SUPPORTED_EXTENSION = 512
73
74# typedef struct _FLOATING_SAVE_AREA {
75#     DWORD   ControlWord;
76#     DWORD   StatusWord;
77#     DWORD   TagWord;
78#     DWORD   ErrorOffset;
79#     DWORD   ErrorSelector;
80#     DWORD   DataOffset;
81#     DWORD   DataSelector;
82#     BYTE    RegisterArea[SIZE_OF_80387_REGISTERS];
83#     DWORD   Cr0NpxState;
84# } FLOATING_SAVE_AREA;
85class FLOATING_SAVE_AREA(Structure):
86    _pack_ = 1
87    _fields_ = [
88        ('ControlWord',     DWORD),
89        ('StatusWord',      DWORD),
90        ('TagWord',         DWORD),
91        ('ErrorOffset',     DWORD),
92        ('ErrorSelector',   DWORD),
93        ('DataOffset',      DWORD),
94        ('DataSelector',    DWORD),
95        ('RegisterArea',    BYTE * SIZE_OF_80387_REGISTERS),
96        ('Cr0NpxState',     DWORD),
97    ]
98
99    _integer_members = ('ControlWord', 'StatusWord', 'TagWord', 'ErrorOffset', 'ErrorSelector', 'DataOffset', 'DataSelector', 'Cr0NpxState')
100
101    @classmethod
102    def from_dict(cls, fsa):
103        'Instance a new structure from a Python dictionary.'
104        fsa = dict(fsa)
105        s = cls()
106        for key in cls._integer_members:
107            setattr(s, key, fsa.get(key))
108        ra = fsa.get('RegisterArea', None)
109        if ra is not None:
110            for index in compat.xrange(0, SIZE_OF_80387_REGISTERS):
111                s.RegisterArea[index] = ra[index]
112        return s
113
114    def to_dict(self):
115        'Convert a structure into a Python dictionary.'
116        fsa = dict()
117        for key in self._integer_members:
118            fsa[key] = getattr(self, key)
119        ra = [ self.RegisterArea[index] for index in compat.xrange(0, SIZE_OF_80387_REGISTERS) ]
120        ra = tuple(ra)
121        fsa['RegisterArea'] = ra
122        return fsa
123
124PFLOATING_SAVE_AREA = POINTER(FLOATING_SAVE_AREA)
125LPFLOATING_SAVE_AREA = PFLOATING_SAVE_AREA
126
127# typedef struct _CONTEXT {
128#     DWORD ContextFlags;
129#     DWORD   Dr0;
130#     DWORD   Dr1;
131#     DWORD   Dr2;
132#     DWORD   Dr3;
133#     DWORD   Dr6;
134#     DWORD   Dr7;
135#     FLOATING_SAVE_AREA FloatSave;
136#     DWORD   SegGs;
137#     DWORD   SegFs;
138#     DWORD   SegEs;
139#     DWORD   SegDs;
140#     DWORD   Edi;
141#     DWORD   Esi;
142#     DWORD   Ebx;
143#     DWORD   Edx;
144#     DWORD   Ecx;
145#     DWORD   Eax;
146#     DWORD   Ebp;
147#     DWORD   Eip;
148#     DWORD   SegCs;
149#     DWORD   EFlags;
150#     DWORD   Esp;
151#     DWORD   SegSs;
152#     BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
153# } CONTEXT;
154class CONTEXT(Structure):
155    arch = ARCH_I386
156
157    _pack_ = 1
158
159    # Context Frame
160    #
161    #  This frame has a several purposes: 1) it is used as an argument to
162    #  NtContinue, 2) is is used to constuct a call frame for APC delivery,
163    #  and 3) it is used in the user level thread creation routines.
164    #
165    #  The layout of the record conforms to a standard call frame.
166
167    _fields_ = [
168
169        # The flags values within this flag control the contents of
170        # a CONTEXT record.
171        #
172        # If the context record is used as an input parameter, then
173        # for each portion of the context record controlled by a flag
174        # whose value is set, it is assumed that that portion of the
175        # context record contains valid context. If the context record
176        # is being used to modify a threads context, then only that
177        # portion of the threads context will be modified.
178        #
179        # If the context record is used as an IN OUT parameter to capture
180        # the context of a thread, then only those portions of the thread's
181        # context corresponding to set flags will be returned.
182        #
183        # The context record is never used as an OUT only parameter.
184
185        ('ContextFlags',        DWORD),
186
187        # This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
188        # set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT
189        # included in CONTEXT_FULL.
190
191        ('Dr0',                 DWORD),
192        ('Dr1',                 DWORD),
193        ('Dr2',                 DWORD),
194        ('Dr3',                 DWORD),
195        ('Dr6',                 DWORD),
196        ('Dr7',                 DWORD),
197
198        # This section is specified/returned if the
199        # ContextFlags word contains the flag CONTEXT_FLOATING_POINT.
200
201        ('FloatSave',           FLOATING_SAVE_AREA),
202
203        # This section is specified/returned if the
204        # ContextFlags word contains the flag CONTEXT_SEGMENTS.
205
206        ('SegGs',               DWORD),
207        ('SegFs',               DWORD),
208        ('SegEs',               DWORD),
209        ('SegDs',               DWORD),
210
211        # This section is specified/returned if the
212        # ContextFlags word contains the flag CONTEXT_INTEGER.
213
214        ('Edi',                 DWORD),
215        ('Esi',                 DWORD),
216        ('Ebx',                 DWORD),
217        ('Edx',                 DWORD),
218        ('Ecx',                 DWORD),
219        ('Eax',                 DWORD),
220
221        # This section is specified/returned if the
222        # ContextFlags word contains the flag CONTEXT_CONTROL.
223
224        ('Ebp',                 DWORD),
225        ('Eip',                 DWORD),
226        ('SegCs',               DWORD),         # MUST BE SANITIZED
227        ('EFlags',              DWORD),         # MUST BE SANITIZED
228        ('Esp',                 DWORD),
229        ('SegSs',               DWORD),
230
231        # This section is specified/returned if the ContextFlags word
232        # contains the flag CONTEXT_EXTENDED_REGISTERS.
233        # The format and contexts are processor specific.
234
235        ('ExtendedRegisters',   BYTE * MAXIMUM_SUPPORTED_EXTENSION),
236    ]
237
238    _ctx_debug   = ('Dr0', 'Dr1', 'Dr2', 'Dr3', 'Dr6', 'Dr7')
239    _ctx_segs    = ('SegGs', 'SegFs', 'SegEs', 'SegDs', )
240    _ctx_int     = ('Edi', 'Esi', 'Ebx', 'Edx', 'Ecx', 'Eax')
241    _ctx_ctrl    = ('Ebp', 'Eip', 'SegCs', 'EFlags', 'Esp', 'SegSs')
242
243    @classmethod
244    def from_dict(cls, ctx):
245        'Instance a new structure from a Python dictionary.'
246        ctx = Context(ctx)
247        s = cls()
248        ContextFlags = ctx['ContextFlags']
249        setattr(s, 'ContextFlags', ContextFlags)
250        if (ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS:
251            for key in s._ctx_debug:
252                setattr(s, key, ctx[key])
253        if (ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT:
254            fsa = ctx['FloatSave']
255            s.FloatSave = FLOATING_SAVE_AREA.from_dict(fsa)
256        if (ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS:
257            for key in s._ctx_segs:
258                setattr(s, key, ctx[key])
259        if (ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER:
260            for key in s._ctx_int:
261                setattr(s, key, ctx[key])
262        if (ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL:
263            for key in s._ctx_ctrl:
264                setattr(s, key, ctx[key])
265        if (ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS:
266            er = ctx['ExtendedRegisters']
267            for index in compat.xrange(0, MAXIMUM_SUPPORTED_EXTENSION):
268                s.ExtendedRegisters[index] = er[index]
269        return s
270
271    def to_dict(self):
272        'Convert a structure into a Python native type.'
273        ctx = Context()
274        ContextFlags = self.ContextFlags
275        ctx['ContextFlags'] = ContextFlags
276        if (ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS:
277            for key in self._ctx_debug:
278                ctx[key] = getattr(self, key)
279        if (ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT:
280            ctx['FloatSave'] = self.FloatSave.to_dict()
281        if (ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS:
282            for key in self._ctx_segs:
283                ctx[key] = getattr(self, key)
284        if (ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER:
285            for key in self._ctx_int:
286                ctx[key] = getattr(self, key)
287        if (ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL:
288            for key in self._ctx_ctrl:
289                ctx[key] = getattr(self, key)
290        if (ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS:
291            er = [ self.ExtendedRegisters[index] for index in compat.xrange(0, MAXIMUM_SUPPORTED_EXTENSION) ]
292            er = tuple(er)
293            ctx['ExtendedRegisters'] = er
294        return ctx
295
296PCONTEXT = POINTER(CONTEXT)
297LPCONTEXT = PCONTEXT
298
299class Context(dict):
300    """
301    Register context dictionary for the i386 architecture.
302    """
303
304    arch = CONTEXT.arch
305
306    def __get_pc(self):
307        return self['Eip']
308    def __set_pc(self, value):
309        self['Eip'] = value
310    pc = property(__get_pc, __set_pc)
311
312    def __get_sp(self):
313        return self['Esp']
314    def __set_sp(self, value):
315        self['Esp'] = value
316    sp = property(__get_sp, __set_sp)
317
318    def __get_fp(self):
319        return self['Ebp']
320    def __set_fp(self, value):
321        self['Ebp'] = value
322    fp = property(__get_fp, __set_fp)
323
324#--- LDT_ENTRY structure ------------------------------------------------------
325
326# typedef struct _LDT_ENTRY {
327#   WORD LimitLow;
328#   WORD BaseLow;
329#   union {
330#     struct {
331#       BYTE BaseMid;
332#       BYTE Flags1;
333#       BYTE Flags2;
334#       BYTE BaseHi;
335#     } Bytes;
336#     struct {
337#       DWORD BaseMid  :8;
338#       DWORD Type  :5;
339#       DWORD Dpl  :2;
340#       DWORD Pres  :1;
341#       DWORD LimitHi  :4;
342#       DWORD Sys  :1;
343#       DWORD Reserved_0  :1;
344#       DWORD Default_Big  :1;
345#       DWORD Granularity  :1;
346#       DWORD BaseHi  :8;
347#     } Bits;
348#   } HighWord;
349# } LDT_ENTRY,
350#  *PLDT_ENTRY;
351
352class _LDT_ENTRY_BYTES_(Structure):
353    _pack_ = 1
354    _fields_ = [
355        ('BaseMid',         BYTE),
356        ('Flags1',          BYTE),
357        ('Flags2',          BYTE),
358        ('BaseHi',          BYTE),
359    ]
360
361class _LDT_ENTRY_BITS_(Structure):
362    _pack_ = 1
363    _fields_ = [
364        ('BaseMid',         DWORD,  8),
365        ('Type',            DWORD,  5),
366        ('Dpl',             DWORD,  2),
367        ('Pres',            DWORD,  1),
368        ('LimitHi',         DWORD,  4),
369        ('Sys',             DWORD,  1),
370        ('Reserved_0',      DWORD,  1),
371        ('Default_Big',     DWORD,  1),
372        ('Granularity',     DWORD,  1),
373        ('BaseHi',          DWORD,  8),
374    ]
375
376class _LDT_ENTRY_HIGHWORD_(Union):
377    _pack_ = 1
378    _fields_ = [
379        ('Bytes',           _LDT_ENTRY_BYTES_),
380        ('Bits',            _LDT_ENTRY_BITS_),
381    ]
382
383class LDT_ENTRY(Structure):
384    _pack_ = 1
385    _fields_ = [
386        ('LimitLow',        WORD),
387        ('BaseLow',         WORD),
388        ('HighWord',        _LDT_ENTRY_HIGHWORD_),
389    ]
390
391PLDT_ENTRY = POINTER(LDT_ENTRY)
392LPLDT_ENTRY = PLDT_ENTRY
393
394###############################################################################
395
396# BOOL WINAPI GetThreadSelectorEntry(
397#   __in   HANDLE hThread,
398#   __in   DWORD dwSelector,
399#   __out  LPLDT_ENTRY lpSelectorEntry
400# );
401def GetThreadSelectorEntry(hThread, dwSelector):
402    _GetThreadSelectorEntry = windll.kernel32.GetThreadSelectorEntry
403    _GetThreadSelectorEntry.argtypes = [HANDLE, DWORD, LPLDT_ENTRY]
404    _GetThreadSelectorEntry.restype  = bool
405    _GetThreadSelectorEntry.errcheck = RaiseIfZero
406
407    ldt = LDT_ENTRY()
408    _GetThreadSelectorEntry(hThread, dwSelector, byref(ldt))
409    return ldt
410
411# BOOL WINAPI GetThreadContext(
412#   __in     HANDLE hThread,
413#   __inout  LPCONTEXT lpContext
414# );
415def GetThreadContext(hThread, ContextFlags = None, raw = False):
416    _GetThreadContext = windll.kernel32.GetThreadContext
417    _GetThreadContext.argtypes = [HANDLE, LPCONTEXT]
418    _GetThreadContext.restype  = bool
419    _GetThreadContext.errcheck = RaiseIfZero
420
421    if ContextFlags is None:
422        ContextFlags = CONTEXT_ALL | CONTEXT_i386
423    Context = CONTEXT()
424    Context.ContextFlags = ContextFlags
425    _GetThreadContext(hThread, byref(Context))
426    if raw:
427        return Context
428    return Context.to_dict()
429
430# BOOL WINAPI SetThreadContext(
431#   __in  HANDLE hThread,
432#   __in  const CONTEXT* lpContext
433# );
434def SetThreadContext(hThread, lpContext):
435    _SetThreadContext = windll.kernel32.SetThreadContext
436    _SetThreadContext.argtypes = [HANDLE, LPCONTEXT]
437    _SetThreadContext.restype  = bool
438    _SetThreadContext.errcheck = RaiseIfZero
439
440    if isinstance(lpContext, dict):
441        lpContext = CONTEXT.from_dict(lpContext)
442    _SetThreadContext(hThread, byref(lpContext))
443
444#==============================================================================
445# This calculates the list of exported symbols.
446_all = set(vars().keys()).difference(_all)
447__all__ = [_x for _x in _all if not _x.startswith('_')]
448__all__.sort()
449#==============================================================================
450