写这篇文章,还是为了分析我写加壳程序的经历,大家一同进步.

给dll加壳在kanxu老大的书里面有很仔细的论述,不过都是汇编的,我看了有点晕,飞天诚信的书写的又是欲盖弥彰.所以我还是更多的参考了bigboot的文章.

dll理论上和exe是一样的,但dll多了几个东西.

1.export表,exe是没有这个东西的(borland编译的exe似乎也有这个东西).所有的函数要在export表中输出.这个东西在我们自己的loader(stub)加载前就加载了,所以我们要维持这些字符串的原装,对每一个地址加上我们的偏移.当然也可以自己构造新的export结构. 总之处理方法和iat基本一致.

2. reloc表,这个东西exe绝对没有了.exe都是40000加载的,但dll默认是100000,而且可能变化,所以一定要处理reloc.我刚开始做的时候有点复杂了,后来想了一下,没有必要把我们自己stub的reloc和原来dll的reloc合并,因为我们如果不用线程什么的,reloc就是原来的样子,不用合并.处理的方法还是遍历,把所有的thunk都走一遍,计算一下偏移,每个thunk都加上这个偏移.

3.tls,这个基本不用考虑,如果想做,就用tls代理技术吧,bigboot和我前面的文章都有写.注意别把地址和数据搞混了.

4. 一个全局变量,我们可以设为bFirstRun.一定我们的stub加载过一次,就让bFirstRun=false,以后就不在进行stub的操作,直接跳转.

5. 跳转! 这个比较关键,因为和exe不一样,dll还要回到其他程序中去,所以栈绝对不能出错,我自己就在这里走了不少弯路(C++就是这点不好,要是asm自己可以控制栈了). 当dll被加载时,过程时 exe->dllmain, dll中函数被调用是 exe -> dllmain-> dll function. 那系统是怎么判断那个函数dllmain执行完毕的呢?就是栈.所以在跳转到dllmain(其实是entrypoint)前,栈中的数据一定要保证和刚刚开始一直. 这里就说一下c++的实现方法. 我们自己的oep中,主函数一定要用void __declspec(naked), 这样就没有prolog/epilog, 也就是在进入函数时,栈没有变化. 当然里面的函数可以不用这样说明(事实上,就不能嵌套). 在stub处理过所有操作后,我们必须使用汇编跳转, __asm jmp oep;

代码应该是
void __declspec(naked) StubEntryPoint()
{
  if(bFirstRun)
    {
     bFirstRun=false;
     // stub operation
    }
   
   __asm jmp oep;

}


总结,C++做壳原理和asm完全一样,但细节由于语言的限制还是很不一样的.c的代码可读性很不错,如果和asm配合,应该可以写出很不错的壳来.

下面的代码是处理reloc的
void PerformRelocations () 
{
  //see if no relocation records
  if ( gev.dwOriginalRelocVA == 0 )
    return;

  PIMAGE_DOS_HEADER pDosHdr;
  PIMAGE_NT_HEADERS pNtHdr;
  PIMAGE_SECTION_HEADER pSecHdr;
  DWORD dwSecStart;
  DWORD dwKatSup;
  LONG lJmp;
  WORD wNumSections;
  WORD wSizeO;

  pDosHdr = (PIMAGE_DOS_HEADER)dwLoadAddress;
  lJmp = pDosHdr->e_lfanew;
  dwKatSup = (DWORD)pDosHdr;
  dwKatSup += lJmp;

  pNtHdr = (PIMAGE_NT_HEADERS)dwKatSup;
  
  //compute offset
  DWORD reloc_offset = dwLoadAddress - pNtHdr->OptionalHeader.ImageBase;

  //if we're where we want to be, nothing further to do
  if ( reloc_offset == 0 )
    return;

  //gotta do it, compute the start
  IMAGE_BASE_RELOCATION* ibr_current = (IMAGE_BASE_RELOCATION*)(gev.dwOriginalRelocVA + dwLoadAddress );

  //compute the end
  IMAGE_BASE_RELOCATION* ibr_end = (IMAGE_BASE_RELOCATION*)(&((unsigned char*)ibr_current)[gev.dwOriginalRelocSize]);

  //loop through the chunks
  while ( ibr_current < ibr_end && ibr_current->VirtualAddress ) 
  {
    DWORD RVA_page = ibr_current->VirtualAddress;
    int nCountReloc = ( ibr_current->SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION ) / sizeof(WORD);
    WORD awRelTypenIdx = (WORD)((unsigned char*)ibr_current + IMAGE_SIZEOF_BASE_RELOCATION);//???
    
    for ( int i = 0; i < nCountReloc; ++i ) 
    {
      WORD wType = awRelTypenIdx >> 12;
      WORD wValue = awRelTypenIdx & 0x0fff;
      
      if ( wType == IMAGE_REL_BASED_HIGHLOW ) 
      { //do it
        *((DWORD*)(RVA_page + wValue + dwLoadAddress)) += reloc_offset;
      }
    }
    ibr_current = (IMAGE_BASE_RELOCATION*)(&((unsigned char*)ibr_current)[ibr_current->SizeOfBlock]);
  }
}


下一个题目就是anti了,这个都是crack的高手,我得多花些时间了.
文章内容太简单,大家不要笑话