CLR 中代码访问安全检测实现原理(3)
过于如何遍历调用对象,因为涉及到比较复杂的堆栈帧类型处理,这里就不详细解释,等有空专门写篇文章介绍。目前需要了解的是 CLR 将堆栈切分成各种不同类型的帧,每个帧代表一种状态的迁移。例如调用一个函数、从一个 Assembly 调用另一个 Assembly、从一个
过于如何遍历调用对象,因为涉及到比较复杂的堆栈帧类型处理,这里就不详细解释,等有空专门写篇文章介绍。目前需要了解的是 CLR 将堆栈切分成各种不同类型的帧,每个帧代表一种状态的迁移。例如调用一个函数、从一个 Assembly 调用另一个 Assembly、从一个 AppDomain 调用另一个 AppDomain、乃至从 Managed 代码调用 Unmanaged 代码等等。而这些帧又通过链表被串到一起。堆栈遍历的工作实际上就是从当前调用帧开始,反向遍历所有的堆栈帧,处理能够处理的,跳过不能处理的,最终到栈顶结束遍历。如果遍历过程中,堆栈帧的回调处理函数发现问题,则可以通过设置标志中断堆栈遍历操作,返回异常给上级程序进行处理。如 CodeAclearcase/" target="_blank" >ccessCheckStackWalkCB 发现某个调用链上的方法所在 Assembly 权限不够,则中断遍历操作抛出异常。
对检测某个组件是否拥有权限的 CodeAccessCheckStackWalkCB 函数 (ComCodeAccessSecurityEngine.cpp:449) 来说,其主要工作如下:
以下内容为程序代码:
enum StackWalkAction {
SWA_CONTINUE = 0, // continue walking
SWA_ABORT = 1, // stop walking, early out in "failure case"
SWA_FAILED = 2 // couldn't walk stack
};
// 堆栈帧的封装类
class CrawlFrame {
// ...
};
static StackWalkAction CodeAccessCheckStackWalkCB(CrawlFrame* pCf, VOID* pData)
{
// CheckInternal 填充的检测数据
CheckWalkHeader *pCBdata = (CheckWalkHeader*)pData;
// 获取当前帧的相关信息
MethodDesc * pFunc = pCf->GetFunction();
Assembly *pAssem = pFunc->GetModule()->GetAssembly();
AppDomain *pAppDomain = pCf->GetAppDomain();
// 遍历操作回调函数的返回动作,SWA_CONTINUE 继续;SWA_ABORT 中断;SWA_FAILED 失败。
StackWalkAction action ;
// 跳过特殊情况的帧
if (Security::SecWalkCommonProlog (&(pCBdata->prologData), pFunc, &action, pCf))
return action ;
if (pAssem != pCBdata->pPrevAssembly)
{
// 当 Assembly 变化时进行 CAS 检测
// ...
pCBdata->pPrevAssembly = pAssem;
}
if (pAppDomain != pCBdata->pPrevAppDomain)
{
// 当 AppDomain 变化时进行 CAS 检测
// ...
pCBdata->pPrevAppDomain = pAppDomain;
}
OBJECTREF *pFrameObjectSlot = pCf->GetAddrOfSecurityObject();
if (pFrameObjectSlot != NULL && *pFrameObjectSlot != NULL)
{
// 当帧保护安全对象时进行 CAS 检测
// ...
}
return SWA_CONTINUE;
}
从当前帧中可以获取各种需要检测的信息,如方法、Assembly和AppDomain。只有当 Assembly 或 AppDomain 发生变化时,对新的对象进行 CAS 检测,同时如果帧具有显式的安全对象,也要进行 CAS 检测。不过对于符合 Security::SecWalkCommonProlog 函数 (Security.cpp:406) 定义的特殊帧,将完全跳过 CAS 检测。
原文转自:http://www.ltesting.net
|