1// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "sandbox/win/src/sidestep_resolver.h"
6
7#include "base/win/pe_image.h"
8#include "sandbox/win/src/sandbox_nt_util.h"
9#include "sandbox/win/src/sidestep/preamble_patcher.h"
10
11namespace {
12
13const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize;
14
15struct SidestepThunk {
16  char sidestep[kSizeOfSidestepStub];  // Storage for the sidestep stub.
17  int internal_thunk;  // Dummy member to the beginning of the internal thunk.
18};
19
20struct SmartThunk {
21  const void* module_base;  // Target module's base.
22  const void* interceptor;  // Real interceptor.
23  SidestepThunk sidestep;  // Standard sidestep thunk.
24};
25
26}  // namespace
27
28namespace sandbox {
29
30NTSTATUS SidestepResolverThunk::Setup(const void* target_module,
31                                      const void* interceptor_module,
32                                      const char* target_name,
33                                      const char* interceptor_name,
34                                      const void* interceptor_entry_point,
35                                      void* thunk_storage,
36                                      size_t storage_bytes,
37                                      size_t* storage_used) {
38  NTSTATUS ret = Init(target_module, interceptor_module, target_name,
39                      interceptor_name, interceptor_entry_point,
40                      thunk_storage, storage_bytes);
41  if (!NT_SUCCESS(ret))
42    return ret;
43
44  SidestepThunk* thunk = reinterpret_cast<SidestepThunk*>(thunk_storage);
45
46  size_t internal_bytes = storage_bytes - kSizeOfSidestepStub;
47  if (!SetInternalThunk(&thunk->internal_thunk, internal_bytes, thunk_storage,
48                        interceptor_))
49    return STATUS_BUFFER_TOO_SMALL;
50
51  AutoProtectMemory memory;
52  ret = memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE);
53  if (!NT_SUCCESS(ret))
54    return ret;
55
56  sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch(
57      target_, reinterpret_cast<void*>(&thunk->internal_thunk), thunk_storage,
58      kSizeOfSidestepStub);
59
60  if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv)
61    return STATUS_BUFFER_TOO_SMALL;
62
63  if (sidestep::SIDESTEP_SUCCESS != rv)
64    return STATUS_UNSUCCESSFUL;
65
66  if (storage_used)
67    *storage_used = GetThunkSize();
68
69  return ret;
70}
71
72size_t SidestepResolverThunk::GetThunkSize() const {
73  return GetInternalThunkSize() + kSizeOfSidestepStub;
74}
75
76// This is basically a wrapper around the normal sidestep patch that extends
77// the thunk to use a chained interceptor. It uses the fact that
78// SetInternalThunk generates the code to pass as the first parameter whatever
79// it receives as original_function; we let SidestepResolverThunk set this value
80// to its saved code, and then we change it to our thunk data.
81NTSTATUS SmartSidestepResolverThunk::Setup(const void* target_module,
82                                           const void* interceptor_module,
83                                           const char* target_name,
84                                           const char* interceptor_name,
85                                           const void* interceptor_entry_point,
86                                           void* thunk_storage,
87                                           size_t storage_bytes,
88                                           size_t* storage_used) {
89  if (storage_bytes < GetThunkSize())
90    return STATUS_BUFFER_TOO_SMALL;
91
92  SmartThunk* thunk = reinterpret_cast<SmartThunk*>(thunk_storage);
93  thunk->module_base = target_module;
94
95  NTSTATUS ret;
96  if (interceptor_entry_point) {
97    thunk->interceptor = interceptor_entry_point;
98  } else {
99    ret = ResolveInterceptor(interceptor_module, interceptor_name,
100                             &thunk->interceptor);
101    if (!NT_SUCCESS(ret))
102      return ret;
103  }
104
105  // Perform a standard sidestep patch on the last part of the thunk, but point
106  // to our internal smart interceptor.
107  size_t standard_bytes = storage_bytes - offsetof(SmartThunk, sidestep);
108  ret = SidestepResolverThunk::Setup(target_module, interceptor_module,
109                                     target_name, NULL, &SmartStub,
110                                     &thunk->sidestep, standard_bytes, NULL);
111  if (!NT_SUCCESS(ret))
112    return ret;
113
114  // Fix the internal thunk to pass the whole buffer to the interceptor.
115  SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(),
116                   thunk_storage, &SmartStub);
117
118  if (storage_used)
119    *storage_used = GetThunkSize();
120
121  return ret;
122}
123
124size_t SmartSidestepResolverThunk::GetThunkSize() const {
125  return GetInternalThunkSize() + kSizeOfSidestepStub +
126         offsetof(SmartThunk, sidestep);
127}
128
129// This code must basically either call the intended interceptor or skip the
130// call and invoke instead the original function. In any case, we are saving
131// the registers that may be trashed by our c++ code.
132//
133// This function is called with a first parameter inserted by us, that points
134// to our SmartThunk. When we call the interceptor we have to replace this
135// parameter with the one expected by that function (stored inside our
136// structure); on the other hand, when we skip the interceptor we have to remove
137// that extra argument before calling the original function.
138//
139// When we skip the interceptor, the transformation of the stack looks like:
140//  On Entry:                         On Use:                     On Exit:
141//  [param 2] = first real argument   [param 2] (esp+1c)          [param 2]
142//  [param 1] = our SmartThunk        [param 1] (esp+18)          [ret address]
143//  [ret address] = real caller       [ret address] (esp+14)      [xxx]
144//  [xxx]                             [addr to jump to] (esp+10)  [xxx]
145//  [xxx]                             [saved eax]                 [xxx]
146//  [xxx]                             [saved ebx]                 [xxx]
147//  [xxx]                             [saved ecx]                 [xxx]
148//  [xxx]                             [saved edx]                 [xxx]
149__declspec(naked)
150void SmartSidestepResolverThunk::SmartStub() {
151  __asm {
152    push eax                  // Space for the jump.
153    push eax                  // Save registers.
154    push ebx
155    push ecx
156    push edx
157    mov ebx, [esp + 0x18]     // First parameter = SmartThunk.
158    mov edx, [esp + 0x14]     // Get the return address.
159    mov eax, [ebx]SmartThunk.module_base
160    push edx
161    push eax
162    call SmartSidestepResolverThunk::IsInternalCall
163    add esp, 8
164
165    test eax, eax
166    lea edx, [ebx]SmartThunk.sidestep   // The original function.
167    jz call_interceptor
168
169    // Skip this call
170    mov ecx, [esp + 0x14]               // Return address.
171    mov [esp + 0x18], ecx               // Remove first parameter.
172    mov [esp + 0x10], edx
173    pop edx                             // Restore registers.
174    pop ecx
175    pop ebx
176    pop eax
177    ret 4                               // Jump to original function.
178
179  call_interceptor:
180    mov ecx, [ebx]SmartThunk.interceptor
181    mov [esp + 0x18], edx               // Replace first parameter.
182    mov [esp + 0x10], ecx
183    pop edx                             // Restore registers.
184    pop ecx
185    pop ebx
186    pop eax
187    ret                                 // Jump to original function.
188  }
189}
190
191bool SmartSidestepResolverThunk::IsInternalCall(const void* base,
192                                                void* return_address) {
193  DCHECK_NT(base);
194  DCHECK_NT(return_address);
195
196  base::win::PEImage pe(base);
197  if (pe.GetImageSectionFromAddr(return_address))
198    return true;
199  return false;
200}
201
202}  // namespace sandbox
203