看雪论坛
发新帖
3

[原创][开源]用C++实现的壳(扩展版)

cyxvc 2016-1-1 13:47 19319
        前几天我写了篇【用C++实现的壳(基础版)】http://bbs.pediy.com/showthread.php?t=206804 完成了一个非常纯洁的C++壳基础框架。现在这个【扩展版】的壳在之前的基础上增加了两个功能,一个是IAT加密,在一个就是机器码绑定。并修复了一些bug,静态编译了Shell.dll,使得在Win 7 x86和Win 7 x64位下都可以正常加壳了,如果加壳后的程序无法运行,请取消“IAT加密”选项,原因是我在IAT加密中使用了Hook-API,而Hook以后全当Call来处理了,实际有的程序IAT位置所填写的地址指向的并不全是Call指令,有时候是Mov指令,所以“IAT加密”功能的兼容性不足。
        本来想增加反调试功能,可我实在水平有限,无法干掉StrongOD的反反调试功能,所以就没在拓展版中假如反调试功能,如果大家有什么好的反调试技术可以添加到这个壳子里,我在这就不献丑了...
        拓展版在界面上有了变化,增加了一些可选选项:

   

        下面就分别介绍一下“IAT加密”和“机器码绑定”这两个功能的实现。

【IAT加密】        

        我对IAT进行加密的思路是:
        ①首先定义一个自己的导入表数据结构,在Pack部分加壳的时候,读取被加壳程序的导入表信息并保存到我自己的导入表数据结构中,然后抹去被加壳程序的导入表数据;
        ②在Shell部分对IAT进行解密的时候,直接从我自己定义的导入表数据结构中获取修复IAT所用的信息,当通过这些信息获取到真正的函数地址后,将该地址填入一个new出来的堆空间1内保存,在这个堆空间1内加入一些花指令,最终再调用真正的函数地址。
        如果直接将堆空间1的地址写入IAT,就完成了一个简单的IAT-Hook,但这样操作的话,真正的函数地址和要写入的IAT地址会同时出现,在反汇编中修改代码,直接将函数地址写入IAT地址的话,就会使得IAT-Hook失效,所以我没有直接将堆空间1写入IAT地址。
        ③而是将堆空间1写入了一段又new出来的堆空间2,然后再在别的函数中,将堆空间2的地址写入IAT地址,这就能保证真正的函数地址和IAT地址不同时出现,这样的话,如果脱壳者在不了解我自己定义的导入表数据结构的情况下,是很难修复IAT并成功脱壳的。
        我自己定义的IAT数据结构如下:
typedef struct _MYIMPORT
{
  DWORD  m_dwIATAddr;      //IAT地址
  DWORD  m_dwModNameRVA;      //模块名偏移
  DWORD  m_dwFunNameRVA;      //函数名偏移
  BOOL  m_bIsOrdinal;      //是否为序号导出函数
  DWORD  m_Ordinal;        //序号
}MYIMPORT, *PMYIMPORT;


        示意图如下:
   

        经过这样处理后的IAT,在你停在原程序OEP的情况下,用IAT修复工具是很难进行自动修复的,修复失败,也就意味着不能脱壳成功。
        
        如果大家感兴趣可以用这个壳对一个程序进行“IAT加密”,然后忘掉这个导入表数据结构,再脱脱壳感受一下,嘿嘿...

【机器码绑定】
        这个其实没有什么技术含量,只是在逻辑上很难破解(在你不知道这个程序所绑定的机器码的情况下)。
        原理就是,用机器码1同代码段进行亦或操作,这样加过壳的程序就只能在机器码1的电脑上运行了,而如果在其他机器上运行,Shell部分在对机器码绑定进行解密的时候,获取的机器码同机器码1不同,这样解密出来的代码就是垃圾代码,根本无法运行,这样双击的效果为:
   

        大家在测试的时候,通过我打包的文件中的“查看机器码”查看本机的机器码,然后将这个机器码输入到加壳选项中,所加壳后的程序就只能在你的机器上运行了,如果改动一位,加壳后的程序运行时就会出错。
        我这个获取机器码的代码是从网上找的,但貌似并不是真正的机器码,不过也无所谓,只要能保证每台机器所获取的信息都是不同且唯一的就行,原理一样。
        
【总结】
        给程序加壳其实就是拖慢破解者的进度,或者从数量上击垮对方,很难有脱不了的壳,除非是经过VM保护的代码才会很难分析。这就是一场加壳者和破解者之间的较量,再简单的壳也会有小菜脱不了,再难的壳也会有大牛能搞定。
        注:测试环境为win7系统,32位和64位都可以,但在xp上不行,原因是Shell部分获取Kernel32.dll基址的时候的代码只适用于win7,xp的话需要删除一句“mov eax, [eax]”。如果你想要兼容xp的话,请从网上自行搜索获取Kernel32.dll基址的其他方法。

        源码下载: CyxvcProtect(扩展版).rar
上传的附件:
本主题帖已收到 0 次赞赏,累计¥0.00
最新回复 (68)
11
Netfairy 2016-1-1 14:23
2
感谢分享,之前我也研究过写壳
smurf 2016-1-1 14:49
3
支持一下,顶起
ID蝴蝶 2016-1-1 15:13
4
支持一下,顶起。
2
pxhb 2016-1-1 17:25
5
非常感谢分享
yunshouhu 2016-1-1 17:26
6
不错,支持楼主,等放假回来再看看。
2
pxhb 2016-1-1 17:34
7
方便传个vs2010版本的吗
3
cyxvc 2016-1-1 18:00
8
我电脑上只有VS2013啊

不过我给你百度了一下如何解决问题

希望能帮到你:
http://blog.csdn.net/civilman/article/details/40109483
1
zfdyq 2016-1-2 00:01
9
good job
2
pxhb 2016-1-2 10:22
10
非常感谢回复
要是以后加入虚拟机引擎就爽了
2
czcqq 2016-1-3 01:00
11
感谢分享!挑战一下自己,明天开始研究如何破解
2
czcqq 2016-1-3 03:19
12
看了一下你的加密程序,有思路了,嘿嘿代码加密的找到入口点,等你自己解密了之后,就行,至于IAT加密,嘿嘿,也不难啊!可以采用,已知明文破解的办法,就是用VC编写一个静态加载自己DLL的程序,编写之后,用PE编辑工具打开这个文件,打开之后,查看导出表在文件地址,根据对齐值,计算一下加载到内存时的位置,记下这个位置,接着用楼主的程序加壳,加壳之后用OD加载被加壳的程序,拿着刚刚已经计算好的IAT地址加上模块的加载地址,就可以知道IAT地址,然后在IAT地址上下内存断点,在LoadLibraryW GetProcAddress GetModuleHandleW等函数上下执行断点,接着就调试,嘿嘿,看是什么指令写入了IAT,什么位置调用了GetProcAddress  以及加载了什么模块,等加载自己的DLL的时候,就自己手动通过EAT获取自己DLL的函数的地址,然后填入刚刚加壳程序填入的IAT地址,看是否会直接调用你的程序,如果直接调用,而没有经过加壳程序的话,就说明你获取的IAT写入地址正确了,嘿嘿,用这个方法手工恢复吧!要是获取的IAT地址不正确,就继续调试,直到能获取正确的IAT地址为止!
3
cyxvc 2016-1-3 10:10
13
你这个思路实现了吗? 我获取到真正的函数地址以后,不是直接填到对应的IAT地址的啊。。而是直接填到一块new出来的堆空间中的啊,而这个堆空间中填的地址也不是直接就是函数地址,是一段ShellCode的地址,经过一些花指令以后才会call真正的函数地址。
你dump以后成功修复IAT了? (加壳的时候别忘了选择IAT加密)
2
czcqq 2016-1-3 10:22
14
部分实现,通过一个已知的DLL的API,我已经定位到了解码IAT的程序!已经找到了IAT原地址,你即使申请了new的地址,你也必须向原IAT地址填写一个HOOK,不是么?跟踪到后面的结果是,如果没有进行IAT加密的话,会发现惊喜,不过经过测试,加密之后也没事,你同样会发现惊喜!
2
czcqq 2016-1-3 10:25
15
没有完全实现的原因,只是因为我跟踪到IAT之后,累了,就先休息了,嘿嘿,等过两天,我有空了,继续挑战,嘿嘿!不过表扬一下,花指令还是不错,不过,不是很强大,花了我多一点事件读取而已
3
cyxvc 2016-1-3 10:28
16
哇,多谢大牛赐教!

调试方面我还是太弱啊,回头研究研究你说的这种方法!

感谢感谢!
yunshouhu 2016-1-3 14:32
17
1、本人环境为vs2010,附件中的源码存在问题,开始直接编译不通过。
需修改shell.cpp 245行279行添加等号。
2、修改完各种错误后静态编译出来的shell.dll只有38kb,而楼主所给的shell.dll为78kb不知是什么原因。(基本的环境配置我没有修改,只修改编译错误信息。)
3、使用楼主的shell.dll加壳在win7 x64系统可以正常运行,但是使用楼主源码编译出来的exe和dll加壳后运行弹出对话框后运行出错。第二次输出信息为:
runtime error R6009
- not enough space for environment
4、猜想附件中的源码是不是最终版本。
3
cyxvc 2016-1-3 15:52
18
我用的是VS2013开发的。
我给出的那个78kb的Shell.dll是我在VS2013下静态编译的( 运行库:多线程 MT )Release版。


你试一下你自己编译exe文件配上压缩包里自带的Shell.dll(78kb)看能不能正常加壳。如果能的话就说明是Shell编译的问题。

确定一下你的dll项目是否是Release(之前为了调试方便,我把shell项目Release的生成目录改成了Debug目录),再确定一下Shell项目是不是按照上图中的方式设置。

在我电脑上下载下来源码然后直接编译出来,加壳是没问题的。

看来VS低版本对高版本的兼容性不太好吧,你也可以看看这个迁移方案,希望能够帮到你:
http://blog.csdn.net/civilman/article/details/40109483
上传的附件:
2
czcqq 2016-1-3 17:17
19
已经可以破解了,嘿嘿,多谢你的加壳程序了,我已经弄好了,等我整理好了,吃了晚饭,我就上传破解办法以及资料,先上传截图,等我晚上弄好以后,再上传教程,嘿嘿,肚子饿了,晚上才弄
上传的附件:
Promisejhy 2016-1-3 17:50
20
留个记号,持续关注楼主的更新。
2
czcqq 2016-1-3 18:14
21
楼主,你的加密程序,我已经破解好了,但是我现在在考虑是不是要公布做法,来教一教大家呢?等我公布了,我怕楼主又不高兴了!
3
cyxvc 2016-1-3 18:32
22
啊...暂时没有更新的打算了,我就是提供个框架,整点简单的加密而已,
主要是抛砖引玉哈哈...
3
cyxvc 2016-1-3 18:33
23
没啥不高兴的,我这壳本来就是供大家学习的,我自己水平也有限,有大牛来指点当然是极好的了!
2
czcqq 2016-1-3 18:38
24
楼主犯了一个小错误,导致我可以直接在你的程序中,直接截取你的IAT填写以及复制过程,在这个过程中,可以在堆栈中记录到导出模块已经导出函数名,楼主在申请新的IAT地址前,使用了LoadLibrary和GetModuleHandle,这两个函数调用时,在堆栈上可以看到我们想要的模块,赶紧的,记录下来呗!,使用了GetProAddress,没有清空EAX寄存器的值,而是,将EAX与15151515h进行异或之后填写入局部变量中,然后就申请了新的内存,新内存地址就保存在EAX中,然后以EAX作为返回值,新的内存用来填写ShllCode,而ShllCode中对原函数的回调用的地址就直接写入SHELLCODE,同时把ShllCode地址存在一个变量中,这个变量,后来又被取出,用来填写真正的IAT地址!我只需截取 ShellCode,让ShellCode的地址指向原函数,并且取消对ShellCode的填写操作,然后用OD将对应的代码回填到可执行文件中,并且在你的程序回填IAT HOOK的时候,记录你的IAT地址就行,有了IAT地址,又有正确的模块加载列表,函数列表,以及IAT的函数地址的链表,恢复IAT不好办么?
2
czcqq 2016-1-3 18:49
25
楼主,你想破解的话,先准备工具,需要的工具ollydbg
Cheat Engine和VS2010,以及楼主的加壳程序!
楼主,我先编辑资料,写上破解过程,等一下我发上来
3
cyxvc 2016-1-3 18:51
26
OK,那麻烦您了!贡献资料供大家学习!感谢感谢!
2
czcqq 2016-1-3 19:08
27
对了,还需要一个PE文件编辑器,我这里选择的是PE Exolorer
雅鸦歌 2016-1-3 21:29
28
顶下学员。。。。
2
czcqq 2016-1-3 22:15
29
楼主,你想破解的话,先准备工具,需要的工具ollydbg, PE Exolorer
Cheat Engine和VS2010,以及楼主的加壳程序!
先用VS2010建立一个控制台工程,
工程代码如下
#include "stdafx.h"
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
        _asm int 3//这个代码一定需要,这个代码调用时会加载调试器,用来在不同调试器中使程序断在我想要的位置
        while(1)
        {
        while(::MessageBox(0,L"是否记录到导出表?",L"Test",MB_YESNO)==IDYES)
        {
                return 0;
        }
        }
        return 0;
}
然后打开工程设置,将工程设置为固定基地址,以及取消地址虚拟化,取消VC代码的折叠优化,不做以上设置,就会影响破解!
解释一下为什么要设置死循环,就是需要减少程序的启动次数!
好,将工程编译为Release版本,我们调试一下,没问题,正常调用!
用加密程序加密吧!加密时选择IAT加密,以及代码段加密,选择出现提示,以便我们附加需要的调试器,而程序中的INT 3指令,是帮助我们的调试器定位在我们的代码的,其余的我们都不

选,以免影响我们的破解!
//我们准备好已经加密过的程序以后,用PE Exolorer打开未加密的文件,查看一下导出表地址,我这里是00402224h 大小50h ,导出函数链表地址为00402000h 记录下来,备用
然后直接运行加密后的程序,如果加密无误,会弹出崩溃提示,这时我们附加VC的调试器上去,会看到VC弹出提示,遇到我们先前设置的断点!
断点后面的代码如下
00401003  int         3  //这里就是我们设置的断点,VC附加无误后会断在这里!
00401004  mov         eax,1  
00401009  test        eax,eax  
0040100B  je          0040102E  
0040100D  push        4  
0040100F  push        4020F4h  
00401014  push        402100h  
00401019  push        0  
0040101B  call        dword ptr ds:[4020A4h]//当运行到这里时,记录一下括号里面的值,我这里是4020A4h,然后找到导出表开始的地
记录值之后,停止VC调试,
打开Cheat Engine 61,然后运行加密过的程序,等弹出提示的时候,用CE附加它,然后选择手动添加指针,然后填入00402000
,添加指针之后,在指针列表上单击右键,选择找出是谁改写了这个指针,然后继续运行程序,之后CE会记录到,到底是什么地方改写了这个指针,也就是IAT的指针
接着打开CE的记录,单击第一个记录(一般是第一个的),然后点击 显示反汇编工具,就会自动找到改写这个IAT的地址,我这里是0040AFC8,
0040AFB0 - mov eax,[00407140] : [0041B000]
0040AFB5 - lea ecx,[esi+esi*4]
0040AFB8 - mov edx,[eax+ecx*4]
0040AFBB - mov eax,[004169C0] : [00260000]
0040AFC0 - mov ecx,[eax+esi*4]
0040AFC3 - mov eax,[004169D0] : [00400000]
0040AFC8 - mov [eax+edx],ecx
这时我们观察一下寄存器的值,

EAX=00400000
EBX=00270000
ECX=006B0000
EDX=00002000
ESI=0000000F
EDI=00000000
ESP=0018FF74
EBP=0018FF88
发现什么情况没有?
eax+edx=00402000 EAX就是模块基地址,那么EDX就是相对偏移 这么说这里的EDX保存的是原导入表地址啦?赶紧保存
什么情况00402000不是原导入表地址么?这里怎么进行了赋值操作?
ECX的值哪里来的?
mov ecx,[eax+esi*4]这里来的!
这句代码一看就知道类似 Addr[i]=值
那EAX的值就应该等于Addr的地址啦!
mov eax,[004169C0] : [00260000]
这里赋值EAX
看004169C0指向的地址,这像一个变量(经验判断的)
接着,在CE中添加地址,勾选指针类型,偏移量,我们写0
然后重启被加密的程序,然后附加CE,找到刚才添加的指针,右键,选择找出是谁访问了这个指针,弹出的对话框中选择第一个
然后,回到被加密程序中,单击确定,这时CE就记录了是谁访问了这个变量!
在弹出的代码列表中,选择第一个(一般都是第一个的)
然后按旁边的反汇编按钮,定位到改写这个变量的地址
我这里是0040B3BD
得到获取IAT地址的指令在那之后,
接下来就只能靠OD了,打开OD,用OD加载被加密程序之后,定位到0040B3BD
然后下断点,接下来,凭经验,找到填写这个地址的函数头,在那里下断点,
然后按F9运行,接着被加密程序会弹出消息框,我们单击确定,然后会陷入我们OD下的断点中,
接着,仔细了哦!别漏了,这里要有重要的事情发生了,仔细看寄存器,以及OD的显示
运行到0040b213处时,发生了什么事情?ECX寄存器里,居然得到了解密之后的模块文件名,

留一个题,你们自己分析,在上面已经运行过的代码中,可以分析出,加密的密钥是什么!
你们自己去分析吧!我就不去分析了直接修改代码,让解密以后,再进行加密混淆的代码无法运行!

模块文件名,我们得到了,接下来,继续运行
运行到0040B369处时候,我们得到了什么?居然是调用GetProceAddress哈哈,这就好办了,第一个参数是模块地址,第二个就是函数名!这就好办了,不用说,记录!
我们继续调试,当GetProceAddress返回时,居然看见了一个让我惊喜的东西,在0040B378处,看到了xor eax,15151515,哈哈,加密混淆指令,明显是使用GetProceAddress返回的地址与

15151515进行异或,当然不允许了,直接用NOP填充,接下来
函数调用了VirtualAlloc申请了内存,用来干嘛呢?接下来,有一大堆的指令,如果没有EAX寄存器的指令,我们直接忽略,接下来到了
0040B3A4 指令处,注意啊!注意看ECX的值,以及指令,
MOV DWORD PTR DS:[EDX*4+ECX],EAX这个指令很熟悉吧?不就是和前面赋值IAT的指令相似么?
再看看作为基地址的ECX的内容,ECX居然等于00260000,这不是前面的用于赋值给IAT的变量么?原来在这里,嘿嘿,不用说的,直接吧正确的地址给写回去,也就是GetProceAddress返回的

地址,写回去,把0040B396到0040B39F用NOP填充掉,把0040B36F到0040B388全部用NOP填充掉,下面,就是一些清除原导入表的操作,不用说的,果断NOP掉,别忘了保存一下函数与导入表的

关系,以及需要写入导出表的地址,否则,破解就白做了,
然后就用OD保存修改,
再用OD加载修改之后的程序,定位到OEP的最后的RET指令,这时在堆栈顶端,就可以看见原来的OEP,果断保存!
到此,我们得到了,模块名,函数名,以及对应的需要写入的IAT的地址:第一个IAT函数地址就是 00402000+0*4,第二个就是00402000+1*4,第三个就是00402000+2*4,以此类推,就能填写

IAT了,有了模块名,和函数名,自己写一个模块名与函数名的链表,然后就将IAT指向原IAT就行!

破解与获取IAT,以及获取OEP的方法,大家可以看看,哈哈有了原程序的IAT信息,我们自己构建IAT了,就不用麻烦楼主的壳给我恢复IAT了,也就直接过了你的老师的花指令!

好累啊!接下来的自己构建IAT的事,嘿嘿留给楼主自己了,看电脑久了,头大了!
2
czcqq 2016-1-3 22:20
30
以上的破解做了之后,记录下之后,还要减去基地址0x0040000,才是正确的RVA,在下一次,调试时需要加上模块的基地址,才能得到真正的内存数据!别搞错,不要直接引用我的数据哦!我的数据只针对我的机器,不同机器的基地址是不同的,只有RVA是一样的哦!因此,得到相应的地址之后需要做计算的哦!
2
czcqq 2016-1-3 22:32
31
我写错了,我改为

以上的破解做了之后,记录下之后,还要减去OEP所在节的基地址(我这里是0x00405000),才是正确的RVA,在下一次,调试时需要加上OEP所在节的基地址,才能得到真正的内存数据!别搞错,不要直接引用我的数据哦!我的数据只针对我的机器,不同机器的基地址是不同的,只有RVA是一样的哦!因此,得到相应的地址之后需要做计算的哦!

OEP所在节的基地址可以用PE Explorer,读取PE文件,查询到va,进而查询到rva,或者也可以使用debug相关的函数查询到RVA,相关知识可以去看PE文件结构了解
2
czcqq 2016-1-3 22:41
32
我以上的破解方法,可以说是已知明文破解,也就是说,我知道解密后的程序,以及解密前的程序,然后调试解密的程序本身,通过对解密程序本身的调试,知道了它是如何工作的,以及工作需要的资源是存储在哪!然后,既然我知道了,加密程序的相关的资源在哪,以及是如何工作的,那么破解这个壳加密的别的程序,我就依样画葫芦,直接读取资源,然后破解就行了,这就是我的思路,说起来,这方法我是在红盟的教程里面学的,然而,这个教程里面却说,这种破解方法源自日本,是日本甲午战争的时候破解我国莫尔斯电码时用的方法!汗颜啊!
2
czcqq 2016-1-4 00:13
33
建议楼主
OEP处先保存一下各个寄存器的值,备用!对IAT加密解密程序,代码进行加密,
然后申请一段内存,存入加密过的IAT,然后设置权限为无法读取,以及写入,然后注册一个异常回调函数,在准备进入原程序的OEP前,写一个有缓冲区溢出缺陷的函数,然后故意触发缓冲区溢出,在溢出的堆栈指向的地址中,我们写一个具有除0错误的汇编程序,然后这个汇编程序就修正堆栈,让其返回正常的函数,嘿嘿,这样除0错误,会被异常回调函数处理,异常回调函数会取得先前保存的OEP的各个寄存器的值,然后恢复寄存器,恢复寄存器后,我们就可以恢复IAT了,存入加密过的IAT的那段新的内存给予读取权限,然后恢复IAT,恢复以后,将模块名,全部指向一个系统存在,但是并没有加载的DLL,然后,将OEP地址恢复到EIP中,最后结束异常处理,然后卸载异常处理函数,当异常处理函数返回时,就跳到了原程序的OEP中了,而如果,在程序运行开始时有调试器试图附加,来调试你的IAT解密过程的话,由于调试器处理了缓冲区溢出,以及除0错误,所以不会导致异常处理代码的执行,那么IAT也就没有机会解密,这样就会导致程序无法完全解密了,同时破解难度也会提升的!
3
cyxvc 2016-1-4 00:52
34
大牛,你这个对OEP的处理很棒的说!学习了学习了!!!
sdnyzjzx 2016-1-4 08:27
35
从头开始学起。感谢分享!
jxlmz 2016-1-4 09:26
36
我先去看看基础版的,回头再来深入研究这个
蚯蚓降龙 2016-1-4 10:01
37
这个应该是加密壳类型的吧,根本就没压缩性...
选了代码段加密,然而代码段还是有一堆原指令出现,
iat加密比较简单,find #EB02FF15C3000000#几下就能修复了
StrongOD好久没更新了,bug也是有不少的
2
czcqq 2016-1-4 18:54
38
这只是用来学习的,可以用来练手,不要纠结这么多
2
czcqq 2016-1-5 22:00
39
楼主,刚刚写了一个隐藏OEP地址的壳代码的例子,要不要看一下?采用了异常处理链的方式调用了OEP,我想你如果想加强你的壳,你可以看一下嘿嘿,OEP中绝对不会出现原OEP地址,而且附带了部分的调试器检测功能!
3
cyxvc 2016-1-5 22:55
40


我怕我真心看不懂了,我觉得我把这个壳的代码给你拓展,然后你再发个帖子普罗大众一下吧嘿嘿

我们现在开始学内核了,时间比较紧,壳我想就先放一放了...
2
czcqq 2016-1-5 23:38
41
内核不好玩的,玩内核你小心了,给你个忠告,玩内核编程千万要注意,你得先准备几块硬盘!我玩内核编程时,报废了三块硬盘,以及一块显卡,BOSS被我弄坏了一次,系统嘛就不知道弄坏了多少次,嘿嘿,还是RING3好,绝对不会弄坏硬件!另外,你的壳我的VC 不支持,我还是贴出来吧!你自己能看懂多少就多少了,另外壳程序,我早就写过了,而且是半汇编的

代码如下// SetHardWare.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "SetHardWare.h"
typedef
ULONG
(NTAPI
*PRtlNtStatusToDosError)(
   ULONG Status
   );
//#include "../../testbase/Memory/Memory.h"
//PRtlNtStatusToDosError RtlNtStatusToDosError=(PRtlNtStatusToDosError)::GetProcAddress(::GetModuleHandle(L"Ntdll.dll"),"RtlNtStatusToDosError");
#include "../../BPHookFunction/BPHookFunction/BPHookFunction.h"
//#include "../../testbase/Memory/Memory.h"
#define MAX_LOADSTRING 100
typedef ULONG (WINAPI *pfnRtlDispatchException)(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext);
pfnRtlDispatchException m_fnRtlDispatchException=(pfnRtlDispatchException)::GetProcAddress(::GetModuleHandleA("ntdll"),"KiUserExceptionDispatcher");;
// 全局变量:
HINSTANCE hInst;                                                                // 当前实例
TCHAR szTitle[MAX_LOADSTRING];                                        // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING];                        // 主窗口类名
int APIENTRY _tWinMain2(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow);
// 此代码模块中包含的函数的前向声明:
ATOM                                MyRegisterClass(HINSTANCE hInstance);
BOOL                                InitInstance(HINSTANCE, int);
LRESULT CALLBACK        WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK        About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain2(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow);
LONG WINAPI MyTopUnhandledExceptionFilter(
         struct _EXCEPTION_POINTERS *ExceptionInfo
);
                //  extern "C" __declspec(dllimport) ULONG NTAPI  RtlNtStatusToDosError(
//  _In_ LONG Status
//);
BOOL RtlDispatchException(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext)
{
  // 返回TRUE,这个异常我已经处理好了,继续运行程序
  // 返回FALSE,这个异常不是我的,找别人处理去
        MessageBox(0,L"DDDD",L"DDSADS",0);
        return 1;
}
ULONG WINAPI _RtlDispatchException( PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext )
{
  if(RtlDispatchException(pExcptRec,pContext)) return 1;
  return m_fnRtlDispatchException(pExcptRec,pContext);
}
BOOL HookSystemSEH()
{
        m_fnRtlDispatchException=(pfnRtlDispatchException)::GetProcAddress(::GetModuleHandleA("ntdll"),"KiUserExceptionDispatcher");
        return 0;
//        return SetInlineHook((PVOID*)&m_fnRtlDispatchException,_RtlDispatchException);
}
DWORD Address = 0,EndAddress = 0;
LPVOID Data = 0;
DWORD IsCall = 0;
HANDLE VectoredHandler = 0;
VOID WINAPI RegFilter()
{
        PVOID Data = (PVOID)Address;
        if(RtlNtStatusToDosError==0) exit(0);
#ifndef _DEBUG
        if(::IsDebuggerPresent()) return ;
#else

#endif
        VectoredHandler=AddVectoredExceptionHandler(1,MyTopUnhandledExceptionFilter);
        if(VectoredHandler==0) exit(0);
}
int WINAPI Show()
{
        return ::MessageBox(0,L"我差,居然有调试器,请结束调试器!",L"调试器",16);
}
__declspec(naked)int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
        _asm
        {
                mov edi,edi//WINAPI 约定调用方式 这里的一大堆指令,其实就是一个意思,就是jmp _tWinMain2
                push ebp
                        mov ebp, esp
         push eax
         lea eax,dword ptr[Error]
         mov Address,eax
         lea eax,dword ptr[End]
         mov EndAddress,eax
         mov eax,110
         xor ebx ,ebx
         pop eax
        mov esp,ebp
                pop ebp                               
                 mov eax,110
        push eax
                        push dword ptr[Next]
                push RegFilter
                ret
       
Next:                pop eax
        Error:        _asm idiv ebx
                        call Show
                        ret 16
End:
               
                #ifdef _DEBUG
                ret 16
        #else
                call dword ptr[DebugBreak]
                push Error+2
                #endif
                ret
                        ret 16
        }
}
LONG WINAPI MyTopUnhandledExceptionFilter(
         struct _EXCEPTION_POINTERS *ExceptionInfo
)
                 {
                         PVECTORED_EXCEPTION_HANDLER Handler=(PVECTORED_EXCEPTION_HANDLER)VectoredHandler;
                //         return Handler(ExceptionInfo);
                         WCHAR Buffer[32]={0};
                        DWORD  ExceptionCode=RtlNtStatusToDosError(ExceptionInfo->ExceptionRecord->ExceptionCode);
                //        ExceptionCode=::BaseNtStatusToDosError(ExceptionCode);
                        //_asm int 3 c0000094
                        //wsprintf(Buffer,L"%x",ExceptionCode);
                        //MessageBox(0,Buffer,L"Text",0);
                        //if(IsCall==1)
                        if(IsCall==2) return 0;
                        if(ExceptionInfo->ExceptionRecord->ExceptionCode==0x80000003)
                        {
                                if(ExceptionInfo->ContextRecord->Eax==120)
                                //if(EndAddress==(DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress)
                                {
                                                        ExceptionInfo->ContextRecord->Eax=119;
                                                        ExceptionInfo->ContextRecord->Esp=ExceptionInfo->ContextRecord->Esp+4;
                                                        ExceptionInfo->ContextRecord->Eip=(DWORD)Data;
                                                        return EXCEPTION_CONTINUE_EXECUTION;
                                }
                                return 0;
                        }
                        if(ExceptionCode==998)
                        {
                                //if(ExceptionInfo->ExceptionRecord->ExceptionAddress==(PVOID)Data)
                        //        {
                        //if(IsCall==2) return 0;

                        if(ExceptionInfo->ExceptionRecord->ExceptionAddress==Data)
                        {
                                memset(Data,0,16);
                                VirtualFree(Data,16,MEM_DECOMMIT);
                                        if(ExceptionInfo->ContextRecord->Eax==119)
                                        {
                                                IsCall=2;
                                                ExceptionInfo->ContextRecord->Eip=(DWORD)_tWinMain2;
                                                return EXCEPTION_CONTINUE_EXECUTION;
                                        }
                        }
                        //        }
                        }
                        if(ExceptionInfo->ExceptionRecord->ExceptionCode==0xc0000094)
                        {
                                //ExceptionInfo->ExceptionRecord->
                                if(ExceptionInfo->ExceptionRecord->ExceptionAddress==(PVOID)Address)
                                {
                                        if(ExceptionInfo->ContextRecord->Eax==110)
                                        {
                                                 Data=VirtualAlloc(0,16,MEM_COMMIT, PAGE_READWRITE);
                                                 //::IsDebuggerPresent()
                                                 BOOL IsDebug= ::IsDebuggerPresent();
                                                #ifndef _DEBUG
                           //  if(::IsDebuggerPresent()) return ;
                          #else
                                                 IsDebug=FALSE;
                           #endif
                                                 if(IsDebug)
                                                 {
                                                         #ifndef _DEBUG
                                                ExceptionInfo->ContextRecord->Eip=ExceptionInfo->ContextRecord->Eip+2;
                                                #else
                                                         ExceptionInfo->ContextRecord->Eip=(DWORD)_tWinMain2;
                                                         #endif
                                                //return EXCEPTION_CONTINUE_EXECUTION;
                                                         RemoveVectoredExceptionHandler(VectoredHandler);
                                                        return EXCEPTION_CONTINUE_EXECUTION;
                                                 }
                                                 #ifdef _DEBUG
                                                 ExceptionInfo->ContextRecord->Eip=(DWORD)_tWinMain2;
                                                 RemoveVectoredExceptionHandler(VectoredHandler);
                                                 return EXCEPTION_CONTINUE_EXECUTION;
                                                 #endif
                                                if(Data==0)
                                                {
                                                        //exit(0);
                                                        ExceptionInfo->ContextRecord->Eip=ExceptionInfo->ContextRecord->Eip+2;
                                                }else
                                                {
                                                        CONTEXT lpContext={0};
                                                        IsCall=1;
                                                        memset(Data,0xcc,16);
                                                        ExceptionInfo->ContextRecord->Eax=120;
                                                        ExceptionInfo->ContextRecord->Eip=EndAddress;
                                                }
                                        }
                                }
                                return EXCEPTION_CONTINUE_EXECUTION;
                        }
                         return 0;
                 }
int APIENTRY _tWinMain2(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);

        // TODO: 在此放置代码。
//        _asm int  3
        MSG msg;
        HACCEL hAccelTable;

        // 初始化全局字符串
        LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadString(hInstance, IDC_SETHARDWARE, szWindowClass, MAX_LOADSTRING);
        MyRegisterClass(hInstance);
       
        // 执行应用程序初始化:
        if (!InitInstance (hInstance, nCmdShow))
        {
                return FALSE;
        }

        hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SETHARDWARE));

        // 主消息循环:
        while (GetMessage(&msg, NULL, 0, 0))
        {
                if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
                {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }
        }

        return (int) msg.wParam;
}

//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
//  注释:
//
//    仅当希望
//    此代码与添加到 Windows 95 中的“RegisterClassEx”
//    函数之前的 Win32 系统兼容时,才需要此函数及其用法。调用此函数十分重要,
//    这样应用程序就可以获得关联的
//    “格式正确的”小图标。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
        WNDCLASSEX wcex;

        wcex.cbSize = sizeof(WNDCLASSEX);

        wcex.style                        = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc        = WndProc;
        wcex.cbClsExtra                = 0;
        wcex.cbWndExtra                = 0;
        wcex.hInstance                = hInstance;
        wcex.hIcon                        = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SETHARDWARE));
        wcex.hCursor                = LoadCursor(NULL, IDC_ARROW);
        wcex.hbrBackground        = (HBRUSH)(COLOR_WINDOW+1);
        wcex.lpszMenuName        = MAKEINTRESOURCE(IDC_SETHARDWARE);
        wcex.lpszClassName        = szWindowClass;
        wcex.hIconSm                = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

        return RegisterClassEx(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // 将实例句柄存储在全局变量中

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: 处理主窗口的消息。
//
//  WM_COMMAND        - 处理应用程序菜单
//  WM_PAINT        - 绘制主窗口
//  WM_DESTROY        - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        int wmId, wmEvent;
        PAINTSTRUCT ps;
        HDC hdc;

        switch (message)
        {
        case WM_COMMAND:
                wmId    = LOWORD(wParam);
                wmEvent = HIWORD(wParam);
                // 分析菜单选择:
                switch (wmId)
                {
                case IDM_ABOUT:
                        DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                        break;
                case IDM_EXIT:
                        DestroyWindow(hWnd);
                        break;
                default:
                        return DefWindowProc(hWnd, message, wParam, lParam);
                }
                break;
        case WM_PAINT:
                hdc = BeginPaint(hWnd, &ps);
                // TODO: 在此添加任意绘图代码...
                EndPaint(hWnd, &ps);
                break;
        case WM_DESTROY:
                PostQuitMessage(0);
                break;
        default:
                return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
        UNREFERENCED_PARAMETER(lParam);
        switch (message)
        {
        case WM_INITDIALOG:
                return (INT_PTR)TRUE;

        case WM_COMMAND:
                if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
                {
                        EndDialog(hDlg, LOWORD(wParam));
                        return (INT_PTR)TRUE;
                }
                break;
        }
        return (INT_PTR)FALSE;
}

实际入口点为_tWinMain2,VC,为我们连接的入口点是_tWinMain,至于为什么入口点是_tWinMain,因为这是一个例子,不用更改mainCRTStartup的,这里做演示就已经够了!
3
cyxvc 2016-1-6 07:56
42
ok!这个我收藏了

十分感谢大神的赐教!

我还处在学习阶段,勉励!
cooldogpp 2016-1-8 09:04
43
这个 真是不错
jerryroxet 2016-1-8 16:12
44
学习了,支持楼主
sbfkpyud 2016-1-11 15:19
45
这个版块是不是大牛最多的,怎么发现了N多精华和关注,研究壳的都是大牛啊。膜拜
zxfpt 2016-1-12 18:17
46
太高深了,看不懂,我小菜一个写的程序没人感兴趣unpack
inheqqq 2016-1-14 08:34
47
楼主写的不错,支持一下
casc 2016-1-15 14:22
48
楼主大神啊,这个神器能脱阿里加固吗?
kading 2016-2-3 14:52
49
多谢楼主分享
伪装的伤痛 2016-3-3 14:20
50
我擦,居然有调试器
返回



©2000-2017 看雪学院 | Based on Xiuno BBS | 域名 加速乐 保护 | SSL证书 又拍云 提供 | 微信公众号:ikanxue
Time: 0.016, SQL: 13 / 京ICP备10040895号-17