// This header is part of CrossCall: the sandbox inter-process communication. // This header defines the basic types used both in the client IPC and in the // server IPC code. CrossCallParams and ActualCallParams model the input // parameters of an IPC call and CrossCallReturn models the output params and // the return value. // // An IPC call is defined by its 'tag' which is a (uint32_t) unique identifier // that is used to route the IPC call to the proper server. Every tag implies // a complete call signature including the order and type of each parameter. // // Like most IPC systems. CrossCall is designed to take as inputs 'simple' // types such as integers and strings. Classes, generic arrays or pointers to // them are not supported. // // Another limitation of CrossCall is that the return value and output // parameters can only be uint32_t integers. Returning complex structures or // strings is not supported.
// max number of extended return parameters. See CrossCallReturn // 输出型参数最多只能有8个,不清楚是否包括返回值,等到看CrossCallReturn时再说 constsize_t kExtendedReturnCount = 8;
// Union of multiple types to be used as extended results // in the CrossCallReturn. // 前面已经看到了,返回值和输出型参数只能是uint32_t // 实际上这种说法不严谨,编码意义上的本质在于只能使用32位的值。 // 32位的值可以表示一个任意类型x86指针,可以表示句柄等等。 // 下面的union就整理了CrossCallReturn可能会用到的几种类型 unionMultiType { uint32_t unsigned_int; void* pointer; HANDLE handle; ULONG_PTR ulong_ptr; };
// Maximum number of IPC parameters currently supported. // To increase this value, we have to: // - Add another Callback typedef to Dispatcher. // - Add another case to the switch on SharedMemIPCServer::InvokeCallback. // - Add another case to the switch in GetActualAndMaxBufferSize // 这个应该是IPC参数的最大数量 // 看起来如果扩展参数数量会相当麻烦: // - Dispatcher的Callback需要增加 // - SharedMemIPCServer::InvokeCallback的switch需要加个case // - GetActualAndMaxBufferSize的switch需要加个case constint kMaxIpcParams = 9;
// Contains the information about a parameter in the ipc buffer. // IPC通信无论如何折腾,最本质的client用buffer承载数据,将其发给server // ParamInfo则抽象了buffer中的某个参数 structParamInfo { ArgType type_; // 看下面 uint32_t offset_; // 真实数据偏移 uint32_t size_; // 真实数据尺寸 }; // 这种offset+size的组合通常来说都是真实数据游离于ParamInfo结构之外 // 而真实数据append到ParamInfo后面的某个地址处,offset和size用于定位真实数据的起始
// Defines the supported C++ types encoding to numeric id. Like a simplified // RTTI. Note that true C++ RTTI will not work because the types are not // polymorphic anyway. // 这个就表示参数(上面提到的真实数据)是哪一种C++类型,用枚举来表示 // 这就很像一个简化的RTTI(runtime type identify),当然了这些类型没有多态所以RTTI是没戏的。 enumArgType { INVALID_TYPE = 0, WCHAR_TYPE, UINT32_TYPE, UNISTR_TYPE, VOIDPTR_TYPE, INPTR_TYPE, INOUTPTR_TYPE, LAST_TYPE };
// Models the return value and the return parameters of an IPC call // currently limited to one status code and eight generic return values // which cannot be pointers to other data. For x64 ports this structure // might have to use other integer types. // 封装了IPC调用的输出型参数和返回值 structCrossCallReturn { // the IPC tag. It should match the original IPC tag. uint32_t tag; // 每种IPC调用独有的tag // The result of the IPC operation itself. ResultCode call_outcome; // 保存IPC操作本身的状态码结果 // the result of the IPC call as executed in the server. The interpretation // of this value depends on the specific service. // 这个就是server上处理IPC call的结果状态值,具体意义取决于特定的服务 union { NTSTATUS nt_status; DWORD win32_result; }; // Number of extended return values. uint32_t extended_count; // 应该是输出型参数的个数,暂不清楚是否包含返回值 // for calls that should return a windows handle. It is found here. HANDLE handle; // 如果有需要返回windows句柄的,可以存在这里 // The array of extended values. // extended values数组,每一种都是MultiType这个union MultiType extended[kExtendedReturnCount];//kExtendedReturnCount是8 };
// CrossCallParams base class that models the input params all packed in a // single compact memory blob. The representation can vary but in general a // given child of this class is meant to represent all input parameters // necessary to make a IPC call. // 把输入型参数紧凑的捏成一团,用于发起IPC调用 // // This class cannot have virtual members because its assumed the IPC // parameters start from the 'this' pointer to the end, which is defined by // one of the subclasses // 该类无法拥有虚成员函数,因为设计上会假定从this指针起始到结尾(并不是对象内存空间尾)这部分内存空间 // 要作为IPC参数的buffer,这由它的子类定义 //(含有虚函数的对象会有虚表,虚表在this处即对象头部位置) // // Objects of this class cannot be constructed directly. Only derived // classes have the proper knowledge to construct it. // 限于这种复杂的使用方式,该类不能简单的直接构造,而是必须通过子类以特殊方式构造。 classCrossCallParams { public: // Returns the tag (ipc unique id) associated with this IPC. // 获取该IPC调用的tag,实际上存储在tag_成员 uint32_tGetTag()const{ return tag_; }
// Returns the beggining of the buffer where the IPC params can be stored. // prior to an IPC call // 这里就看出类头注释的意义了,该对象实体的整个内存空间都是IPC参数存储的buffer constvoid* GetBuffer()const{ returnthis; }
// Returns how many parameter this IPC call should have. // 返回该IPC call有多少个参数 uint32_tGetParamsCount()const{ return params_count_; }
// Returns a pointer to the CrossCallReturn structure. // 返回CrossCallReturn结构指针,这里可以看出该对象内部封装了一个用于承载返回值和 // 输出型参数的结构 CrossCallReturn* GetCallReturn(){ return &call_return; }
// Returns true if this call contains InOut parameters. // 是否有InOut型即输入输出型参数 boolIsInOut()const{ return (1 == is_in_out_); }
// Tells the CrossCall object if it contains InOut parameters. voidSetIsInOut(bool value){ if (value) is_in_out_ = 1; else is_in_out_ = 0; }
protected: // constructs the IPC call params. Called only from the derived classes // 构造器是protected,表明该对象的实例化需要借助派生类。 CrossCallParams(uint32_t tag, uint32_t params_count) : tag_(tag), is_in_out_(0), params_count_(params_count) {}
// ActualCallParams models an specific IPC call parameters with respect to the // storage allocation that the packed parameters should need. // NUMBER_PARAMS: the number of parameters, valid from 1 to N // BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take, // typically the block size is defined by the channel size of the underlying // ipc mechanism. // In practice this class is used to levergage C++ capacity to properly // calculate sizes and displacements given the possibility of the packed params // blob to be complex. // // As is, this class assumes that the layout of the blob is as follows. Assume // that NUMBER_PARAMS = 2 and a 32-bit build: // 这就是关键之处了,刻画了2个参数情形下的buffer布局 // // [ tag 4 bytes] // [ IsOnOut 4 bytes] // 这个应该是IsInOut吧。。. // [ call return 52 bytes] // [ params count 4 bytes] // 上面这些就是父类CrossCallParam的64B内存空间 // [ parameter 0 type 4 bytes] // 三元组就是struct ParamInfo,2表示有3个 // [ parameter 0 offset 4 bytes] ---delta to ---\ // [ parameter 0 size 4 bytes] | // [ parameter 1 type 4 bytes] | // [ parameter 1 offset 4 bytes] ---------------|--\ // [ parameter 1 size 4 bytes] | | // [ parameter 2 type 4 bytes] | | // [ parameter 2 offset 4 bytes] ----------------------\ // [ parameter 2 size 4 bytes] | | | // |---------------------------| | | | // | value 0 (x bytes) | <--------------/ | |// 这部分对应真实数据 // | value 1 (y bytes) | <-----------------/ | // | | | // | end of buffer | <---------------------/// 最后一个offset表示结尾 // |---------------------------| // // Note that the actual number of params is NUMBER_PARAMS + 1 // so that the size of each actual param can be computed from the difference // between one parameter and the next down. The offset of the last param // points to the end of the buffer and the type and size are undefined. // 描述了最后一个ParamInfo的说明 // // 类模板,常数型,用以在定义ActualCallParams<xxx,xxx>类时指定参数个数与buffer尺寸 template <size_t NUMBER_PARAMS, size_t BLOCK_SIZE> classActualCallParams : public CrossCallParams { public: // constructor. Pass the ipc unique tag as input // 以传入的tag和模板常量NUMBER_PARAMS调用父类构造器 // 像这种单基本型参数的构造器都要声明explicit,防止编译器自作聪明的在某些情况把uint32_t自动转化成ActualCallParams<xxx,xxx>对象 explicitActualCallParams(uint32_t tag) : CrossCallParams(tag, NUMBER_PARAMS) { // 第一个ParamInfo的offset是已知的 // parameters_数组存储真实参数,它的首地址减去param_info_数组地址就是param_info_[0]的offset param_info_[0].offset_ = static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this)); }
// Testing-only constructor. Allows setting the |number_params| to a // wrong value. // 测试用的构造器,这里没有用模板常量而是使用了传入的参数个数 ActualCallParams(uint32_t tag, uint32_t number_params) : CrossCallParams(tag, number_params) { param_info_[0].offset_ = static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this)); }
// Testing-only method. Allows setting the apparent size to a wrong value. // returns the previous size. // 测试用,修改一个param_info_[someone]的offset uint32_tOverrideSize(uint32_t new_size){ uint32_t previous_size = param_info_[NUMBER_PARAMS].offset_; param_info_[NUMBER_PARAMS].offset_ = new_size; return previous_size; }
// Copies each paramter into the internal buffer. For each you must supply: // index: 0 for the first param, 1 for the next an so on // 把参数拷贝到正确的buffer位置,index表示第几个参数 boolCopyParamIn(uint32_t index, constvoid* parameter_address, uint32_t size, bool is_in_out, ArgType type){ if (index >= NUMBER_PARAMS) { // sanity check returnfalse; }
if (UINT32_MAX == size) { // Memory error while getting the size. returnfalse; }
if (size && !parameter_address) { returnfalse; }
if ((size > sizeof(*this)) || (param_info_[index].offset_ > (sizeof(*this) - size))) { // It does not fit, abort copy. returnfalse; }
// 安检通过,找到坑位 char* dest = reinterpret_cast<char*>(this) + param_info_[index].offset_;
// We might be touching user memory, this has to be done from inside a try // except. // copy参数过去 __try { memcpy(dest, parameter_address, size); } __except (EXCEPTION_EXECUTE_HANDLER) { returnfalse; }
// Set the flag to tell the broker to update the buffer once the call is // made. // 如果这个参数是输入输出型参数,enable is_in_out_ if (is_in_out) SetIsInOut(true);
// Returns a pointer to a parameter in the memory section. // get特定参数,返回的是buffer中真实参数的指针 void* GetParamPtr(size_t index){ returnreinterpret_cast<char*>(this) + param_info_[index].offset_; }
// Returns the total size of the buffer. Only valid once all the paramters // have been copied in with CopyParamIn. // 只有当所有参数都通过CopyParamIn拷贝过来后,这个函数返回的才是正确的值。 // 所以如果我并不copy NUMBER_PARAMS个参数的话,是否会引入某些漏洞? uint32_tGetSize()const{ return param_info_[NUMBER_PARAMS].offset_; }
// This header defines the CrossCall(..) family of templated functions // Their purpose is to simulate the syntax of regular call but to generate // and IPC from the client-side. // // The basic pattern is to // 1) use template argument deduction to compute the size of each // parameter and the appropriate copy method // 2) pack the parameters in the appropriate ActualCallParams< > object // 3) call the IPC interface IPCProvider::DoCall( ) // // The general interface of CrossCall is: // ResultCode CrossCall(IPCProvider& ipc_provider, // uint32_t tag, // const Par1& p1, const Par2& p2,...pn // CrossCallReturn* answer) // // where: // ipc_provider: is a specific implementation of the ipc transport see // sharedmem_ipc_server.h for an example. // tag : is the unique id for this IPC call. Is used to route the call to // the appropriate service. // p1, p2,.. pn : The input parameters of the IPC. Use only simple types // and wide strings (can add support for others). // answer : If the IPC was successful. The server-side answer is here. The // interpretation of the answer is private to client and server. // // The return value is ALL_OK if the IPC was delivered to the server, other // return codes indicate that the IPC transport failed to deliver it.
// The copy helper uses templates to deduce the appropriate copy function to // copy the input parameters in the buffer that is going to be send across the // IPC. These template facility can be made more sophisticated as need arises.
// The default copy helper. It catches the general case where no other // specialized template matches better. We set the type to UINT32_TYPE, so this // only works with objects whose size is 32 bits. // 这个是default模板,除了下面对明确类型的模板类定义以外,其他的都匹配到这里 template <typename T> classCopyHelper { public: CopyHelper(const T& t) : t_(t) {}
// Returns the pointer to the start of the input. constvoid* GetStart()const{ return &t_; }
// Update the stored value with the value in the buffer. This is not // supported for this type. boolUpdate(void* buffer){ // Not supported; // 这个显然对未知类型很危险,不能瞎j8赋值 returntrue; }
// Returns the size of the input in bytes. uint32_tGetSize()const{ returnsizeof(T); }
// Returns true if the current type is used as an In or InOut parameter. boolIsInOut(){ returnfalse; }
// This copy helper template specialization if for the void pointer // case both 32 and 64 bit. // 这个是T为void*的情景,实际上没做什么实际内容 template <> classCopyHelper<void*> { public: CopyHelper(void* t) : t_(t) {}
// Returns the pointer to the start of the input. constvoid* GetStart()const{ return &t_; }
// Update the stored value with the value in the buffer. This is not // supported for this type. boolUpdate(void* buffer){ // Not supported; returntrue; }
// Returns the size of the input in bytes. uint32_tGetSize()const{ returnsizeof(t_); }
// Returns true if the current type is used as an In or InOut parameter. boolIsInOut(){ returnfalse; }
// Returns this object's type. ArgType GetType(){ return VOIDPTR_TYPE; }
private: constvoid* t_; };
// This copy helper template specialization catches the cases where the // parameter is a pointer to a string. // 这个是const宽字符指针,实际上是字符串 template <> classCopyHelper<constwchar_t*> { public: CopyHelper(constwchar_t* t) : t_(t) {}
// Returns the pointer to the start of the string. constvoid* GetStart()const{ return t_; }
// Update the stored value with the value in the buffer. This is not // supported for this type. // 都const了,改个毛线 boolUpdate(void* buffer){ // Not supported; returntrue; }
// Returns the size of the string in bytes. We define a nullptr string to // be of zero length. // 获取尺寸,这里并不是类型的尺寸,而是宽字符串的整体大小 uint32_tGetSize()const{ __try { return (!t_) ? 0 : static_cast<uint32_t>(StringLength(t_) * sizeof(t_[0])); } __except (EXCEPTION_EXECUTE_HANDLER) { return UINT32_MAX; } }
// Returns true if the current type is used as an In or InOut parameter. boolIsInOut(){ returnfalse; }
ArgType GetType(){ return WCHAR_TYPE; }
private: // We provide our not very optimized version of wcslen(), since we don't // want to risk having the linker use the version in the CRT since the CRT // might not be present when we do an early IPC call. // 起始就是简单的count,\0结束,包含了\0 staticsize_t __cdecl StringLength(constwchar_t* wcs){ constwchar_t* eos = wcs; while (*eos++) ; returnstatic_cast<size_t>(eos - wcs - 1); }
constwchar_t* t_; };
// Specialization for non-const strings. We just reuse the implementation of the // const string specialization. // 这个是non-const字符串 template <> classCopyHelper<wchar_t*> : public CopyHelper<constwchar_t*> { public: typedef CopyHelper<constwchar_t*> Base;//定义这货是怕编译器混淆,用了自生成类模板类吗? CopyHelper(wchar_t* t) : Base(t) {}
// This copy helper template specialization catches the cases where the // parameter is a an input/output buffer. // 输入输出型参数比较特殊,用InOutCountedBuffer结构,这个结构一会儿再分析 // 对应ArgType为INOUTPTR_TYPE template <> classCopyHelper<InOutCountedBuffer> { public: CopyHelper(const InOutCountedBuffer t) : t_(t) {}
// Returns the pointer to the start of the string. constvoid* GetStart()const{ return t_.Buffer(); }
// Updates the buffer with the value from the new buffer in parameter. boolUpdate(void* buffer){ // We are touching user memory, this has to be done from inside a try // except. __try { memcpy(t_.Buffer(), buffer, t_.Size()); } __except (EXCEPTION_EXECUTE_HANDLER) { returnfalse; } returntrue; }
// Returns the size of the string in bytes. We define a nullptr string to // be of zero length. uint32_tGetSize()const{ return t_.Size(); }
// Returns true if the current type is used as an In or InOut parameter. boolIsInOut(){ returntrue; }
// Generic encapsulation class containing a pointer to a buffer and the // size of the buffer. It is used by the IPC to be able to pass in/out // parameters. classInOutCountedBuffer : public CountedBuffer { public: InOutCountedBuffer(void* buffer, uint32_t size) : CountedBuffer(buffer, size) {} };
// Encapsulates a pointer to a buffer and the size of the buffer. classCountedBuffer { public: CountedBuffer(void* buffer, uint32_t size) : size_(size), buffer_(buffer) {}
// This is the IPC server interface for CrossCall: The IPC for the Sandbox // On the server, CrossCall needs two things: // 1) threads: Or better said, someone to provide them, that is what the // ThreadProvider interface is defined for. These thread(s) are // the ones that will actually execute the IPC data retrieval. // // 2) a dispatcher: This interface represents the way to route and process // an IPC call given the IPC tag. // // The other class included here CrossCallParamsEx is the server side version // of the CrossCallParams class of /sandbox/crosscall_params.h The difference // is that the sever version is paranoid about the correctness of the IPC // message and will do all sorts of verifications. // // A general diagram of the interaction is as follows: // // ------------ // | | // ThreadProvider <--(1)Register--| IPC | // | | Implemen | // | | -tation | // (2) | | OnMessage // IPC fired --callback ------>| |--(3)---> Dispatcher // | | // ------------ // // The IPC implementation sits as a middleman between the handling of the // specifics of scheduling a thread to service the IPC and the multiple // entities that can potentially serve each particular IPC.
// ThreadProvider models a thread factory. The idea is to decouple thread // creation and lifetime from the inner guts of the IPC. The contract is // simple: // - the IPC implementation calls RegisterWait with a waitable object that // becomes signaled when an IPC arrives and needs to be serviced. // - when the waitable object becomes signaled, the thread provider conjures // a thread that calls the callback (CrossCallIPCCallback) function // - the callback function tries its best not to block and return quickly // and should not assume that the next callback will use the same thread // - when the callback returns the ThreadProvider owns again the thread // and can destroy it or keep it around. // simple已经描述的很清楚了,IPC实现体会利用RegisterWait来注册一个可等待对象,一旦IPC // 请求到来,那么该对象signaled,ThreadProvider这个线程工厂会生成一个线程来调用CrossCallIPCCallback // CrossCallIPCCallback很快回来,ThreadProvider再次控制该线程,可以销毁也可以保持 // 前后两次执行callback的不一定是同一个线程 // 那么CrossCallIPCCallback是个什么样的callback呢?实际上是个函数指针类型定义:
// This function signature is required as the callback when an IPC call fires. // context: a user-defined pointer that was set using ThreadProvider // reason: 0 if the callback was fired because of a timeout. // 1 if the callback was fired because of an event. typedefvoid(__stdcall* CrossCallIPCCallback)(void* context, unsignedchar reason); classThreadProvider { public: // Registers a waitable object with the thread provider. // client: A number to associate with all the RegisterWait calls, typically // this is the address of the caller object. This parameter cannot // be zero. // waitable_object : a kernel object that can be waited on // callback: a function pointer which is the function that will be called // when the waitable object fires // context: a user-provider pointer that is passed back to the callback // when its called // client作为标志把waitable_object与callback绑定,当waitable_object signaled(IPC arrive),调用callback virtualboolRegisterWait(constvoid* client, HANDLE waitable_object, CrossCallIPCCallback callback, void* context)= 0;
// Removes all the registrations done with the same cookie parameter. // This frees internal thread pool resources. virtualboolUnRegisterWaits(void* cookie)= 0; virtual ~ThreadProvider() {} };
// Models the server-side of the original input parameters. // Provides IPC buffer validation and it is capable of reading the parameters // out of the IPC buffer. // CrossCallParams的另一个子类,用在server // 模拟server端原始输入参数的处理,提供了IPC buffer的检查并copy到另一个对象结构 classCrossCallParamsEx : public CrossCallParams { public: // Factory constructor. Pass an IPCbuffer (and buffer size) that contains a // pending IPCcall. This constructor will: // 1) validate the IPC buffer. returns nullptr is the IPCbuffer is malformed. // 2) make a copy of the IPCbuffer (parameter capture) // 颇为关键的static工厂方法,验证IPC buffer并拷贝参数 static CrossCallParamsEx* CreateFromBuffer(void* buffer_base, uint32_t buffer_size, uint32_t* output_size);
// Provides IPCinput parameter raw access: // index : the parameter to read; 0 is the first parameter // returns nullptr if the parameter is non-existent. If it exists it also // returns the size in *size // IPCinput参数的各种形态原生访问方法 void* GetRawParameter(uint32_t index, uint32_t* size, ArgType* type);
// Gets a parameter that is four bytes in size. // Returns false if the parameter does not exist or is not 32 bits wide. boolGetParameter32(uint32_t index, uint32_t* param);
// Gets a parameter that is void pointer in size. // Returns false if the parameter does not exist or is not void pointer sized. boolGetParameterVoidPtr(uint32_t index, void** param);
// Gets a parameter that is a string. Returns false if the parameter does not // exist. boolGetParameterStr(uint32_t index, base::string16* string);
// Gets a parameter that is an in/out buffer. Returns false is the parameter // does not exist or if the size of the actual parameter is not equal to the // expected size. boolGetParameterPtr(uint32_t index, uint32_t expected_size, void** pointer);
// Frees the memory associated with the IPC parameters. staticvoidoperatordelete(void* raw_memory)throw();
private: // Only the factory method CreateFromBuffer can construct these objects. CrossCallParamsEx();//CrossCallParamsEx对象必须从static工厂方法中make
// This function uses a SEH try block so cannot use C++ objects that // have destructors or else you get Compiler Error C2712. So no DCHECKs // inside this function. CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base, uint32_t buffer_size, uint32_t* output_size){ // IMPORTANT: Everything inside buffer_base and derived from it such // as param_count and declared_size is untrusted. // 心智检查 if (!buffer_base) returnnullptr; if (buffer_size < sizeof(CrossCallParams)) returnnullptr; if (buffer_size > kMaxBufferSize) //就是1024,IPC Channel实现体目前的硬编码最大尺寸 returnnullptr;
// Touching the untrusted buffer is done under a SEH try block. This // will catch memory access violations so we don't crash. __try { CrossCallParams* call_params = reinterpret_cast<CrossCallParams*>(buffer_base); //传入的应该是client的ActualCallParams,call_params父类指针指向子类对象
// 过安检,CrossCallParams+((param_count + 1) * sizeof(ParamInfo))是除了真实参数数据以外至少需要的空间 // Check against the minimum size given the number of stated params // if too small we bail out. param_count = call_params->GetParamsCount(); min_declared_size = sizeof(CrossCallParams) + ((param_count + 1) * sizeof(ParamInfo));
// Initial check for the buffer being big enough to determine the actual // buffer size. // 如果buffer_size比min_declared_size还小,说明这段数据是有问题的,显然不能继续解析了 if (buffer_size < min_declared_size) returnnullptr;
// Retrieve the declared size which if it fails returns 0. // 不管参数个数有几个,参数总尺寸是可以计算的 // 参数总尺寸的计算其实就是ParamInfo[]的parser,一会儿展开看 declared_size = GetActualBufferSize(param_count, buffer_base);
// 判断一下buffer_size,buffer_size理应>=declared_size而declared_size理应>=min_declared_size // 这个函数很简单 if (!IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) returnnullptr;
// 这里就进行解包了,移花接木给copied_params,CrossCallParamsEx对象是在这里new的 // Now we copy the actual amount of the message. *output_size = declared_size; backing_mem = newchar[declared_size]; // 依然是这种间接new操作,因为buffer尺寸是已知的,但CrossCallParamsEx并没有包含所有的 // buffer数据(真实数据由ParamInfo定位,附加在类的正后方),所以要用间接的方式把这段 // 内存空间看成CrossCallParamsEx + 额外的真实参数数据 copied_params = reinterpret_cast<CrossCallParamsEx*>(backing_mem); // 这个memcpy实际上非常讲究,把ActualCallParams + 额外真实参数数据直接copy到了另外 // 一个对象CrossCallParamsEx+额外真实参数数据 // 这意味着CrossCallParamsEx和ActualCallParams有着相同的内存布局,事实上也确实如此 // 都是CrossCallParams基类+ParamInfo[] flexible数据的结构 // 在server和client各封装这样一个成员变量布局相同的类,主要是因为布局理应相同,但 // 要用到的接口函数是互逆的 memcpy(backing_mem, call_params, declared_size);
// Avoid compiler optimizations across this point. Any value stored in // memory should be stored for real, and values previously read from memory // should be actually read. // 内存屏障,防止此处的编译优化,所有内存的值必须用存储的真实值,此前从memory中读出的高速缓存不能在寄存器中直接复用 base::subtle::MemoryBarrier();
// Check that the copied buffer is still valid. // 可以看出server对input param的检查近乎严苛 // 还要检查copy过去后buffer是否符合预期 if (copied_params->GetParamsCount() != param_count || GetActualBufferSize(param_count, backing_mem) != declared_size || !IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) { delete[] backing_mem; returnnullptr; }
} __except (EXCEPTION_EXECUTE_HANDLER) { // In case of a windows exception we know it occurred while touching the // untrusted buffer so we bail out as is. delete[] backing_mem; returnnullptr; } // 内存的访问很可能因畸形或恶意构造IPCInput出现access violations,这里套上了try块,windows对应SEH
// Returns the actual size for the parameters in an IPC buffer. Returns // zero if the |param_count| is zero or too big. // 又见呆逼操作,不定参数个数的匹配处理在这里完成,不管是几个参数,返回的就是参数总尺寸 // 这里又变成1-9个了,怕是client端少写了两个,然后一直用不到? uint32_tGetActualBufferSize(uint32_t param_count, void* buffer_base){ // The template types are used to calculate the maximum expected size. // kMaxBufferSize是硬编码的sandbox::kIPCChannelSize,也就是1024 // 把9种模板类都做一下typedef typedef ActualCallParams<1, kMaxBufferSize> ActualCP1; typedef ActualCallParams<2, kMaxBufferSize> ActualCP2; typedef ActualCallParams<3, kMaxBufferSize> ActualCP3; typedef ActualCallParams<4, kMaxBufferSize> ActualCP4; typedef ActualCallParams<5, kMaxBufferSize> ActualCP5; typedef ActualCallParams<6, kMaxBufferSize> ActualCP6; typedef ActualCallParams<7, kMaxBufferSize> ActualCP7; typedef ActualCallParams<8, kMaxBufferSize> ActualCP8; typedef ActualCallParams<9, kMaxBufferSize> ActualCP9;
// Retrieve the actual size and the maximum size of the params buffer. // 根据参数个数,可以判断出是哪一个模板类 switch (param_count) { case0: return0; case1: returnreinterpret_cast<ActualCP1*>(buffer_base)->GetSize(); case2: returnreinterpret_cast<ActualCP2*>(buffer_base)->GetSize(); case3: returnreinterpret_cast<ActualCP3*>(buffer_base)->GetSize(); case4: returnreinterpret_cast<ActualCP4*>(buffer_base)->GetSize(); case5: returnreinterpret_cast<ActualCP5*>(buffer_base)->GetSize(); case6: returnreinterpret_cast<ActualCP6*>(buffer_base)->GetSize(); case7: returnreinterpret_cast<ActualCP7*>(buffer_base)->GetSize(); case8: returnreinterpret_cast<ActualCP8*>(buffer_base)->GetSize(); case9: returnreinterpret_cast<ActualCP9*>(buffer_base)->GetSize(); default: return0; } }
// Verifies that the declared sizes of an IPC buffer are within range. boolIsSizeWithinRange(uint32_t buffer_size, uint32_t min_declared_size, uint32_t declared_size){ if ((buffer_size < min_declared_size) || (sizeof(CrossCallParamsEx) > min_declared_size)) { // Minimal computed size bigger than existing buffer or param_count // integer overflow. returnfalse; }
if ((declared_size > buffer_size) || (declared_size < min_declared_size)) { // Declared size is bigger than buffer or smaller than computed size // or param_count is equal to 0 or bigger than 9. returnfalse; }
returntrue; }
最后一个关键的GetRawParameter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Accessors to the parameters in the raw buffer. void* CrossCallParamsEx::GetRawParameter(uint32_t index, uint32_t* size, ArgType* type){ // 不能越界 if (index >= GetParamsCount()) returnnullptr; // The size is always computed from the parameter minus the next // parameter, this works because the message has an extra parameter slot // 拿到该参数的尺寸和类型 *size = param_info_[index].size_; *type = param_info_[index].type_;
// Models an entity that can process an IPC message or it can route to another // one that could handle it. When an IPC arrives the IPC implementation will: // 1) call OnMessageReady() with the tag of the pending IPC. If the dispatcher // returns nullptr it means that it cannot handle this IPC but if it returns // non-null, it must be the pointer to a dispatcher that can handle it. // 2) When the IPC finally obtains a valid Dispatcher the IPC // implementation creates a CrossCallParamsEx from the raw IPC buffer. // 3) It calls the returned callback, with the IPC info and arguments. // 所以IPC请求给到IPC实现体时,会先使用tag调用Dispatcher::OnMessageReady()。 // Dispatcher维护了一组callback,如果该IPC与其中某一个匹配的话,就表示该Dispatcher // 可以处理该IPC调用 // 找到以后,IPC实现体会创建一个CrossCallParamsEx,从buffer拷贝数据 // 然后,以IPC info和args调用对应的callback classDispatcher { public: // Called from the IPC implementation to handle a specific IPC message. // 又是这种笨拙的函数指针定义,根据参数个数的多少,定义callback多种形态 typedefbool(Dispatcher::*CallbackGeneric)(); typedefbool(Dispatcher::*Callback0)(IPCInfo* ipc); typedefbool(Dispatcher::*Callback1)(IPCInfo* ipc, void* p1); typedefbool(Dispatcher::*Callback2)(IPCInfo* ipc, void* p1, void* p2); typedefbool(Dispatcher::*Callback3)(IPCInfo* ipc, void* p1, void* p2, void* p3); typedefbool(Dispatcher::*Callback4)(IPCInfo* ipc, void* p1, void* p2, void* p3, void* p4); typedefbool(Dispatcher::*Callback5)(IPCInfo* ipc, void* p1, void* p2, void* p3, void* p4, void* p5); typedefbool(Dispatcher::*Callback6)(IPCInfo* ipc, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6); typedefbool(Dispatcher::*Callback7)(IPCInfo* ipc, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7); typedefbool(Dispatcher::*Callback8)(IPCInfo* ipc, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8); typedefbool(Dispatcher::*Callback9)(IPCInfo* ipc, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8, void* p9);
// Called from the IPC implementation when an IPC message is ready override // on a derived class to handle a set of IPC messages. Return nullptr if your // subclass does not handle the message or return the pointer to the subclass // that can handle it. // IPC消息到来时,IPC实现体先调用这个MessageReady事件响应,看看是否有能力handle virtual Dispatcher* OnMessageReady(IPCParams* ipc, CallbackGeneric* callback);
// Called when a target proces is created, to setup the interceptions related // with the given service (IPC). // 当target进程创建时,部署与给定服务(IPC)相关的interceptions // 这个暂时不关心,涉及到它的上层组件Interception机制 virtualboolSetupService(InterceptionManager* manager, int service)= 0;
Dispatcher(); virtual ~Dispatcher();
protected: // Structure that defines an IPC Call with all the parameters and the handler. structIPCCall { IPCParams params; CallbackGeneric callback; };
// List of IPC Calls supported by the class. // 一个Dispatcher所支持的IPC调用列表,这个结构在内部定义使用 // 封装了IPCParams和CallbackGeneric std::vector<IPCCall> ipc_calls_; };
// Represents the client process that initiated the IPC which boils down to the // process handle and the job object handle that contains the client process. structClientInfo { HANDLE process; DWORD process_id; };
// All IPC-related information to be passed to the IPC handler. // 一组callback的第一个参数,分组了tag,进程相关信息以及一个CrossCallReturn structIPCInfo { int ipc_tag; const ClientInfo* client_info; CrossCallReturn return_info; };
// This structure identifies IPC signatures. structIPCParams { int ipc_tag; ArgType args[kMaxIpcParams];