首页
论坛
专栏
课程

论用C++给dll加壳[原创]

2006-5-25 18:00 14783

论用C++给dll加壳[原创]

2006-5-25 18:00
14783
写这篇文章,还是为了分析我写加壳程序的经历,大家一同进步.

给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的高手,我得多花些时间了.
文章内容太简单,大家不要笑话

[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (3)
kanxue 8 2006-5-25 18:10
2
0
文章写的不错,但是以下2点值得商榷:

最初由 dogwang 发布
1.export表,exe是没有这个东西的


看看OllyDbg.exe,OD的插件就是靠OllyDbg.exe的export函数工作的。

最初由 dogwang 发布
reloc表,这个东西exe绝对没有了


再看看OllyDbg.exe。
Delphi、BCB程序默认编译出来的EXE都有重定位表。
dogwang 4 2006-5-25 18:16
3
0
最初由 kanxue 发布
文章写的不错,但是以下2点值得商榷:
看看OllyDbg.exe,OD的插件就是靠OllyDbg.exe的export函数工作的。
........


不好意思,我应该加个限制,一般情况下. 呵呵,写的不严谨,不好意思.
另外vc的编译器把这些都省略了,borland编译器还是留着这些的.
softworm 30 2006-5-25 19:57
4
0
楼主能否贴出源码?
游客
登录 | 注册 方可回帖
返回