Thunk 技术的一个改进(转)

<script type="text/javascript"> /*<![CDATA[*/ if(top.location != self.location){ top.location = self.location; } var myref = encodeURIComponent("http://hi.baidu.com/%D3%A3%C1%E8%D7%BC/blog/item/abe5f724a974f92cd5074203%2Ehtml"); /*]]>*/ </script> /*<![CDATA[*/ #usrbar{padding:4px 10px 3px 0;font-size:12px;height:19px;line-height:19px;color:#000000;font-family:Arial;text-align:right;background:#ffffff;filter:alpha(opacity=65);-moz-opacity:0.5;width:auto !important;width:100%;letter-spacing:normal} #usrbar a,#usrbar a:link,#usrbar a:visited{color:#0000CC;text-decoration:underline} #ft{clear:both;height:20px;line-height:20px;color:#666666;font-size:12px;font-family:Arial;text-align:center} #ft a,#ft a:link,#ft a:visited{color:#7777CC;text-decoration:underline} #usrbar,#usrbar a,#usrbar a:link,#usrbar a:visited,#ft,#ft a,#ft a:link,#ft a:visited{letter-spacing:normal} /*]]>*/

百度空间 |  百度首页  <script type="text/javascript"> document.write('| <a href="https://passport.baidu.com/?login&tpl=sp&tpl_reg=sp&u=http://hi.baidu.com' + encodeURIComponent('/%D3%A3%C1%E8%D7%BC/blog/item/abe5f724a974f92cd5074203%2Ehtml') + '">登录</a>'); </script> | 登录
<script> <!-- function newUserTipShow(f) { if(f=="0"){//close G("newUserTipSwf").style.width="40px"; G("newUserTip").style.width="40px"; G("newUserTipShadow").style.display="none"; }else{//show G("newUserTip").style.width="225px"; G("newUserTipSwf").style.width="225px"; G("newUserTipShadow").style.display="block"; } } function newUserRegLog(){ var now=new Date(); now.setTime(now.getTime()+5*60*1000); document.cookie="BDSP_REGFLAG=1;expires="+now.toGMTString()+";path=/"; new Image().src="http://hi.baidu.com/sys/statlog/1.gif?m=blog_newer_pro2_click&v=/%D3%A3%C1%E8%D7%BC&t="+Math.random(); } //--> </script> <script type="text/javascript"> function set_cookie_4_bdtip(index/* start from one */, value){ var bdtip = document.cookie.match(/(^| )BDTIP=([^;]*)(;|$)/); if(!bdtip){ bdtip=new Array(index); for(var i=0,n=bdtip.length;i<n;i++) { if(bdtip[i]=="" || bdtip[i]==null) bdtip[i]=0; if(i == index - 1){ bdtip[i] = value; } } }else{ bdtip = bdtip[2].split('-'); if(index > bdtip.length) bdtip.length= index; for(var i = 0, j = bdtip.length; i < j; i ++){ if(bdtip[i]=="" || bdtip[i]==null) bdtip[i]=0; if(i == index - 1){ bdtip[i] = value; } } } bdtip = bdtip.join('-'); document.cookie = "BDTIP=" + bdtip+ ";expires=Wed, 28-Nov-37 01:45:46 GMT;path=/;"; } </script>
 
查看文章
  
Thunk 技术的一个改进(转)
2008-12-20 00:55

Thunk 技术的一个改进

作者:南风

下载源代码

摘要:介绍了 thunk 技术中如何避免直接写机器码。

关键字:Thunk 机器码 this指针

  Thunk技术,一般认为是在程序中直接构造出可执行代码的技术(在正常情况下,这是编译器的任务)。《深度探索C++对象模型》中对这个词的 来源有过考证(在中文版的162页),说thunk是knuth的倒拼字。knuth就是大名鼎鼎的计算机经典名著《The Art of Computer Programming》的作者,该书被程序员们称为“编程圣经”,与牛顿的“自然哲学的数学原理”等一起,被评为“世界历史上最伟大的十种科学著作”之 一(也不知是谁评的,我没查到,不过反正这本书很牛就是了)。
  一般情况下,使用thunk技术都是事先查好指令的机器码,然后将数组或结构体赋值为这些机器码的二进制值,最后再跳转到数组或结构体的首地址。比如在参考文献[1]中的代码:

void foo(int a)
{ printf ("In foo, a = %d/n", a); }

unsigned char code[9];
* ((DWORD *) &code[0]) = 0x042444FF; /* inc dword ptr [esp+4] */
              code[4]  = 0xe9;       /* JMP */
* ((DWORD *) &code[5]) = (DWORD) &foo - (DWORD) &code[0] - 9; /* 跳转偏移量 */

void (*pf)(int/* a*/) = (void (*)(int)) &code[0];
pf (6);

  这是一段典型的thunk代码,其执行结果是“In foo, a = 7”。
可以看到,它定义了一个数组code[9],然后将事先查好的各汇编指令的机器码直接赋值给数组。然后定义一个函数指针等于数组的首地址,最后通过该函数 指针调用thunk代码。这里使用了函数指针完成调用,好处是代码比较清晰易读。也可以使用汇编代码jmp或call来完成,这样就不必额外定义一个函数 指针。
  网络上的thunk代码,基本上都是这个思路。如果你实际写一段这样的代码,一定会发现很麻烦。对着教科书查找每一个汇编指令的机器码,相信不会是一件愉快的事情。其实我们回过头来想想,这件事计算机来做不是最合适吗,编译器不就是做这个事情的吗?
  以上面的代码为例,让我们重新考虑一下整个过程。我们的目的是在调用函数foo之前将参数增加1。一般而言,这样做肯定是没有foo函数的源代码或者 不允许修改源代码,否则直接改foo函数的代码就好了,何必这么麻烦。为了调用时候的简单化,定义一个函数指针是比较合适的,否则每次调用都写汇编代码 jmp或call太麻烦。这样一来,函数指针必须指向一个代码段的地址。但是这个代码段必须用机器码来构造吗,直接写汇编代码也同样可以做到。
  当然,这里有一个问题。我们写汇编指令的时候,必须是一条指令一条指令的写,不能说指令写一半,然后让汇编程序去处理。上面的代码中,第一条指令 inc直接写汇编语句当然没问题。但下面的jmp语句,就不能直接写。因为我们写汇编语句的时候,jmp跳转偏移量是未知的,必须编译后才知道。并且我们 不能只写jmp而不写偏移量,那是通不过编译的。
  这个问题可以这样解决,写jmp语句的时候,我们写一个占位的DWORD,其值设为一个特殊的值,比如0xffff(原理是这样,实际处理还要迂回一 下,后面有说明)。只要在这段thunk代码中不出现这个值就好。然后执行的时候,在第一次调用之前,在thunk代码中查找该值,将其替换为计算出来的 动态值。经过这样的处理,就可以彻底在thunk代码中消除机器码的直接操作。
  更一般化,为了生成正确的机器码,我们用两个函数。一个用于生成机器码的模板,另一个函数用于在机器码的模板中填入需要动态计算产生的值。下面是一个例子:

void ThunkTemplate(DWORD& addr1,DWORD& addr2)//生成机器码
{
 int flag = 0;
 DWORD x1,x2;

 if(flag)
 {
          //注意,这个括号中的代码无法直接执行,因为其中可能含有无意义的占位数。
  __asm
  {
thunk_begin:
                   ;//这里写thunk代码的汇编语句. 
                   ...
 
thunk_end:   ;
  }
 }
 __asm
 {
  mov   x1,offset thunk_begin; //取 Thunk代码段 的地址范围.
  mov   x2,offset thunk_end;
 }

    addr1 = x1;
    addr2 = x2;
}

  上面的函数用于生成thunk的机器码模板,之所以称为模板,是因为其中包含了无意义的占位数,必须将这些占位数替换为有意义的值之后,才可以 执行这些代码。因此,在函数中thunk代码模板放在一个if(0)语句中,就是避免调用该函数的时候执行thunk代码。另外,为了能方便的得到 thunk代码模板的地址,这里采用一个函数传出thunk代码的首尾地址。

至于替换占位数的功能是很简单的,直接替换就好。

void ReplaceCodeBuf(BYTE *code,int len, DWORD old,DWORD x)//完成动态值的替换.
{
 int i=0;

 for(i=0;i<len-4;++i)
 {
  if(*((DWORD *)&code[i])==old)
  {
   *((DWORD *)&code[i]) = x;

   return ;
  }
 }
}
这样使用两个函数:
DWORD addr1,addr2;
 ThunkTemplate(addr1,addr2);

 memset(m_thunk,0,100);//m_thunk是一个数组: char m_thunk[100];
 memcpy(m_thunk,(void*)addr1,addr2-addr1);//将代码拷贝到m_thunk中。

 ReplaceCodeBuf(m_thunk,addr2-addr1,-1,(DWORD)((void*)this));//将m_thunk中的-1替换为this指针的值。

  原理部分到此为止。下面举一个完整的,有实际意义的例子。在windows中,回调函数的使用是很常见的。比如窗口过程,又比如定时器回调函 数。这些函数,你写好代码,但是却从不直接调用。相反,你把函数地址传递给系统,当系统检测到某些事件发生的时候,系统来调用这些函数。这样当然很好,不 过如果你想做一个封装,将所有相关部分写成一个类,那问题就来了。
  问题是,这些回调函数的形式事先已经定义好了,你无法让一个类的成员函数成为一个回调函数,因为类型不可能匹配。这不能怪微软,微软不可能将回调函数 定义为一个类成员函数(该定义为什么类?),而只能将回调函数定义为一个全局的函数。并且微软其实很多时候也提供了补救措施,在回调函数中增加了一个 void *的参数。这个参数一般都用来传递类的this指针。这样一来,可以这样解决:给系统提供一个全局函数作为回调函数,在该函数中通过额外的那个void *参数访问到类的对象,从而直接调用到类成员函数。如此,你的封装一样可以完成,不过多了一次函数调用而已。

但是,不是所有的回调函数都这么幸运,微软都给它们提供了一个额外的参数。比如,定时器的回调函数就没有。

VOID CALLBACK TimerProc(
  HWND hwnd,         // handle to window
  UINT uMsg,         // WM_TIMER message
  UINT_PTR idEvent,  // timer identifier
  DWORD dwTime       // current system time
);

四个参数,个个都有用途。没有地方可以让你传递那个this指针。当然了,你实在要传也可以做到,比如将hwnd设置为一个结构体的指针,其中包含 原来的hwnd和一个this指针。在定时器回调函数中取出hwnd后强制转化为结构体指针,取出原来的hwnd,取出this指针。现在就可以通过 this指针自由的调用类成员函数了。不过这种方法不是我想要的,我要的是一个通用,统一的解决方法。通过在参数里面加塞夹带的方法,一般也是没有问题 的,不过如果碰到一个回调函数没有参数怎么办?另外,本来是封装为一个类的,结果还是要带着一个全局函数,你难道不觉得有些不爽吗?
  这正是thunk技术大显身手的地方了。我们知道,所谓类成员函数,和对应的全局函数,其实就差一个this指针。如果我们在系统调用函数之前正确处理好this指针,那系统就可以正确的调用类成员函数。
  具体的思路是这样的:当系统需要一个回调函数地址的时候,我们传递一个thunk代码段的地址。这个代码段做两件事:

1、准备好this指针
2、调用成员函数

关键的代码如下(完整的工程在附件中):

void ThunkTemplate(DWORD& addr1,DWORD& addr2,int calltype=0)
{
 int flag = 0;
 DWORD x1,x2;

 if(flag)
 {
  __asm //__thiscall
  {
thiscall_1:     mov   ecx,-1;   //-1占位符,运行时将被替换为this指针.
      mov   eax,-2;   //-2占位符,运行时将被替换为CTimer::CallBcak的地址.
      jmp   eax;
thiscall_2:  ;
  }

  __asm //__stdcall
  {
stdcall_1: push  dword ptr [esp]        ; //保存(复制)返回地址到当前栈中
   mov   dword ptr [esp+4], -1  ; //将this指针送入栈中,即原来的返回地址处
   mov   eax,  -2;
         jmp   eax                    ; //跳转至目标消息处理函数(类成员函数)
stdcall_2: ;
  }
 }

 if(calltype==0)//this_call
 {
  __asm
  {
   mov   x1,offset thiscall_1;  //取 Thunk代码段 的地址范围.
   mov   x2,offset thiscall_2 ;
  }
 }
 else
 {
  __asm
  {
   mov   x1,offset stdcall_1;   
   mov   x2,offset stdcall_2 ;
  }
 }

 addr1 = x1;
 addr2 = x2;
}

上面的函数有几个地方需要说明:

1、为了能适应两种不同的成员函数调用约定,这里写了两份代码。通过参数calltype决定拷贝哪一份代码到缓冲区。
2、本来一条jmp xxxx;指令这里分解为两条指令:

mov eax,-2;
jmp eax;

  这是由汇编语言的特点决定的。直接写jmp -2是通不过的(根据地址的不同,jmp汇编后可能出现好几种形式。这里必须出现一个真实的地址以便汇编器决定jmp类型)。
3、如果对this指针的知识不清楚,请参考我在vc知识库的另外一篇文章《直接调用类成员函数地址》。

设置thunk代码的完整代码如下:

DWORD FuncAddr;
 GetMemberFuncAddr_VC6(FuncAddr,&CTimer::CallBcak);

 DWORD addr1,addr2;
 ThunkTemplate(addr1,addr2,0);

 memset(m_thunk,0,100);
 memcpy(m_thunk,(void*)addr1,addr2-addr1);

 ReplaceCodeBuf(m_thunk,addr2-addr1,-1,(DWORD)((void*)this)); //将-1替换为this指针.
    ReplaceCodeBuf(m_thunk,addr2-addr1,-2,FuncAddr); //将-2替换为成员函数的指针.

  如果你还想和以前一样直接在数组中赋值机器码(毕竟这样看起来很酷,我完全理解)。那也可以这样,调用ThunkTemplate生成 m_thunk后,打印出该数组的值,而后在程序中直接给m_thunk数组赋值,就象网上大部分thunk代码那样,当然在调用前要多一个步骤就是替换 掉占位数。不过无论如何,调用这两个函数生成机器码应该比手工查找方便多了,如果你也这样认为,那就算我这篇文章没白写。

参考文献:基于 Thunk 实现的类成员消息处理函数


类别:参考 | 添加到搜藏 | 浏览( 27) | 评论 (1) <script> /*<![CDATA[*/ var pre = [true,'Thunk::在类中封装回调函数(转)', 'Thunk::在类中封装回调函数(转)','/%D3%A3%C1%E8%D7%BC/blog/item/ead069dd2e2ec3335882dd02.html']; var post = [true,'死机','死机', '/%D3%A3%C1%E8%D7%BC/blog/item/f1e09d272e378604908f9d87.html']; if(pre[0] || post[0]){ document.write('<div style="height:5px;line-height:5px;">&nbsp;</div><div id="in_nav">'); if(pre[0]){ document.write('上一篇:<a href="' + pre[3] + '" title="' + pre[1] + '">' + pre[2] + '</a>&nbsp;&nbsp;&nbsp;&nbsp;'); } if(post[0]){ document.write('下一篇:<a href="' + post[3] + '" title="' + post[1] + '">' + post[2] + '</a>'); } document.write('</div>'); } /*]]>*/ </script>
 
上一篇: Thunk::在类中封装回调函数(转)    下一篇: 死机
 
/*<![CDATA[*/ #in_related_doc a { text-decoration:none; } /*]]>*/
 
<script type="text/javascript"> /*<![CDATA[*/ function HI_MOD_IN_RELATED_DOC_CALLBACK(arg){ if(arg.length <= 1) return false; var hasMore = arg[0]; var D=function(A,B){A[A.length]=B;} if(arg.length % 2 == 0) D(arg, ["","","",""]); var html = ['<div id="in_related_doc"><div class="tit">相关文章:</div>']; D(html, '<table cellpadding="0" cellspacing="3" border="0">'); for(var i = 1, j = arg.length; i < j; i += 2){ D(html, '<tr>'); D(html, '<td width="15px"><a style="font-size:25px" >&#8226;</a></td><td><a href="http://hi.baidu.com/' + arg[i][3] + '/blog/item/' + arg[i][2] + '.html" target="_blank" title="' + arg[i][0] + '">' + arg[i][1] + '</a>'); D(html, new Array(10).join('/u3000')); D(html, '</td>'); if(arg[i + 1][0] != "") D(html, '<td width="15px"><a style="font-size:25px" >&#8226;</a></td><td><a href="http://hi.baidu.com/' + arg[i + 1][3] + '/blog/item/' + arg[i + 1][2] + '.html" target="_blank" title="' + arg[i + 1][0] + '">' + arg[i + 1][1] + '</a></td>'); else D(html, '<td>&nbsp;</td><td>&nbsp;</td>'); D(html, '</tr>'); } if(hasMore) D(html, '<tr><td colspan="4"><a target="_blank" href="/sys/search?pageno=1&type=7&sort=1&word=Thunk%20%BC%BC%CA%F5%B5%C4%D2%BB%B8%F6%B8%C4%BD%F8%28%D7%AA%29&item=abe5f724a974f92cd5074203">更多&gt;&gt;</a></td></tr>'); D(html, '</table></div><div class="line">&nbsp;</div>'); var div = document.getElementById('in_related_tmp'); if(div){ div.innerHTML = html.join(''); while(div.firstChild){ div.parentNode.insertBefore(div.firstChild, div); } div.parentNode.removeChild(div); } window.setTimeout("tracker_init('in_related_doc')",100); } if(RelatedDocData == -1){ // not supported xhr var script = document.createElement('script'); script.type = 'text/javascript'; script.src = '/sys/search?type=8&word=Thunk%20%BC%BC%CA%F5%B5%C4%D2%BB%B8%F6%B8%C4%BD%F8%28%D7%AA%29&item=abe5f724a974f92cd5074203&t=' + new Date().getTime(); document.getElementsByTagName('HEAD')[0].appendChild(script); }else if(RelatedDocData == null){ GetAndEval = true; }else{ eval(RelatedDocData); } /*]]>*/ </script>
最近读者:
<script> var g_spAnnony=true; var g_read=[ ["wizyco","fbaf77697a79636f0601","wizyco"], ["scholesyc","bdaa7363686f6c657379638c03","scholesyc"], ["confusion","3f3bb1e0c2ebb4edcef32300","编码错误"], ["%C2%B7%B9%ED%BC%D7","b4dfc2b7b9edbcd7ab04","路鬼甲"], ["pds%5Fsjz","f9cfbac5d7d3d4c2dcf8f103","号子月茗"], {} ]; g_read.length=g_read.length-1; var _rh1=""; var _rh2=""; function wrreader(){ _rh1 += '<table width="100%" ><tr>'; _rh2+='<tr>'; if(g_spAnnony){ _rh1+='<td align="center" width="10%" ><img border="0" width="55" height="55" src="https://i-blog.csdnimg.cn/blog_migrate/54406254f9eed4e545f11f701d50c07e.jpeg"></td>'; _rh2+='<td>&nbsp;</td>'; if(g_read.length>0){ _rh1+='<td align="left" width="12%">'; }else{ _rh1+='<td align="left" width="100%">'; } _rh1+='<a href="https://passport.baidu.com/?login&tpl=sp&tpl_reg=sp&u='+myref+'" target="_self">登录</a>后,您就出现在这里。</td>'; _rh2+='<td>&nbsp;</td>' } if(g_read.length==0){ if(!g_spAnnony){ _rh1+='<td align=left width="100%">最近还没有登录用户看过这篇文章……</td>'; _rh2+='<td>&nbsp;</td>'; } }else{ for(i=0,len=g_read.length;i<len;i++){ _rh1+='<td align="center" valign="bottom" width="10%" class="user"><a href="/'+g_read[i][0]+'" target="_blank"><img border="0" src="http://himg.baidu.com/sys/portraitn/item/'+g_read[i][1]+'.jpg"></a></td>'; _rh2+='<td align="center" valign="top" class="user"><a href="/'+g_read[i][0]+'" target="_blank">'+g_read[i][2]+'</a></td>'; } } _rh1+='<td width="100%"></td></tr>'; _rh2+='<td></td></tr></table>'; document.write(_rh1+_rh2); } wrreader(); </script>
登录后,您就出现在这里。 
  wizycoscholesyc编码错误路鬼甲号子月茗 
 
<script> allkey=allkey+"7440f12439542733c995598c_abe5f724a974f92cd5074203_"; </script>
网友评论:
<script> function writecmt(type,id,cmtname,cmturl,portraitId){ var html1=""; cmturl=cmturl.replace("https://","http://"); if(type==1){ html1="<a href='"+cmturl+"' target='_blank' title='"+cmturl+"'><img border='0' src='http://himg.baidu.com/sys/portraitn/item/"+portraitId+".jpg'><br>"+cmtname+"</a>"; }else{ if(cmtname=="" || cmtname=="匿名网友"){ if(cmturl==""){ html1="<a>匿名网友</a>"; }else{ html1="<a href='"+cmturl+"' target='_blank' title='"+cmturl+"'>"+cmtname+"</a>"; } }else{ if(cmturl==""){ html1="<div class='f14' style='display:inline'>网友:<a>"+cmtname+"</a></div>"; }else{ html1="<div class='f14' style='display:inline'>网友:<a href='"+cmturl+"' target='_blank' title='"+cmturl+"'>"+cmtname+"</a></div>"; } } } document.write(html1); } function filterCmtContent(n){ if(!BdBrowser.isIE){ var defaultfilter1='<span style="filter:glow(color=#000000,strength=2);height:0px;color:#000000">'; var defaultfilter2='<span style="height: 0px; color: rgb(0, 0, 0);">'; var commentDiv=document.getElementById(n); var divs=commentDiv.getElementsByTagName('div'); var d,tmp; for( var i=0,len=divs.length;i<len;i++){ d=divs[i]; if(d.getAttribute('name')=='cmtcontent'){ tmp=d.innerHTML; tmp=tmp.replace(/<span style="filter:glow/(color=#([0-9a-z]{3,6}),strength=2/);height:0px;color:#([0-9a-z]{3,6})">/ig,defaultfilter1); tmp=tmp.replace('<span style="height: 0px; color: rgb(255, 255, 255);">',defaultfilter2); d.innerHTML=tmp; } } } } </script>
1
<script> writecmt(1,"2cdfefdf60977b1b6227981d","路鬼甲","http://hi.baidu.com/路鬼甲","b4dfc2b7b9edbcd7ab04"); </script>
路鬼甲
2008-12-21 11:00 | 回复
神奇的路过 。。。 顺便说声 密码我给改了~~~ 哇哈哈哈哈~~~ 慢慢猜~~~~
 
<script>filterCmtContent('in_comment');</script>
<script> document.getElementById("spRefURL").value = window.location.href; </script>
发表评论:
姓 名: <script> document.write(" &amp;nbsp;&amp;nbsp; &lt;a href='http://hi.baidu.com/st/reg.html' target='_blank'&gt;注册&lt;/a&gt;"); document.write(' | &lt;a href="https://passport.baidu.com/?login&amp;tpl=sp&amp;tpl_reg=sp&amp;u='+myref+'"&gt;登录&lt;/a&gt;'); </script>    注册 | 登录
网址或邮箱: (选填)
<script> G("spBlogCmtor").value=""; G("spBlogCmtURL").value=""; </script>
内 容:
插入表情
<script> var editor=null; try{ editor=new BdEditor("spBlogCmtText",{width:"100%",height:"155px"}); editor.onfocus = function(){hidErr(3);} editor.render(); }catch(e){ var spBlogCmtText = document.getElementById("spBlogCmtText"); var p = spBlogCmtText.previousSibling; while(p && p.nodeType != 1) p = p.previousSibling; if(p && /bdeditor_container/.test(p.id)){ p.parentNode.removeChild(p); } spBlogCmtText.style.display = ''; editor=null; } </script> <script> G("spBlogCmtor").value=G("spBlogCmtor").defaultValue; G("spBlogCmtText").value=""; </script>
验证码: 请点击后输入四位验证码,字母不区分大小写
<script type="text/javascript"> /*<![CDATA[*/ function f_focus(){ getVcode(); } function getVcode(){ var url="http://hi.baidu.com/sys/file/getvcode?echoback=getVcodeDone&t="+(new Date().getTime()); BdAjax.loadJS(url) } function getVcodeDone(vcode){ document.form1.spVcode.value=vcode; var imgsrc="http://hiup.baidu.com/cgi-bin/genimg?"+vcode; G('verifypic').src=imgsrc; G('yanzheng').style.display="block"; } function newverifypic(){ getVcode(); return false; } /*]]>*/ </script>
     

   
<script> <!-- var hstr="/%D3%A3%C1%E8%D7%BC/brwstat?key1=1"; document.write("<script src='"+hstr+"&key2="+allkey+"'><//script>"); //--> </script> <script src="http://hi.baidu.com/%D3%A3%C1%E8%D7%BC/brwstat?key1=1&key2=7440f12439542733c995598c_abe5f724a974f92cd5074203_"></script>
©2009 Baidu
<script> if(document.getElementById("m_blog")) { var imgarray = document.getElementById("m_blog").getElementsByTagName('img'); var imgw = document.getElementById("m_blog").offsetWidth; imgw =imgw-40; for(var i=0; i<imgarray.length; i++){ if(imgarray[i].className=="blogimg" && imgarray[i].width>=imgw) imgarray[i].width=imgw; } } // Fix ff bugs var blog_text = document.getElementById('blog_text'); blog_text.innerHTML = blog_text.innerHTML.replace(/href/s*=/s*("|')?(/././//././/)/gi,"href=$1../$2"); gotoreply(); </script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值