00:00
好,那就开始今天的部分啊,今天呢,我们来讲一下,我先开一个项目啊。嗯,咱们今天呢,来讲一下什么呢,就是两种反调啊,再讲两种反调式啊,一种呢是对前面的一个补充啊,一种呢是一种新的,呃,补中这种呢是什么呢?就是我们在前面说过用异常这种方式啊,然后用这个关闭无效句柄的形式来这个进行探测,那么这种形式呢,还有一种写法啊,那么我们现在呢,来给它补充一下啊,首先呢,我们还是要给它创建一个线程出来。拿出它的原形啊。给他改一下名字。
01:21
填上回调,然后类型强转。呃,开一个等待等待啊,不用这个debug,这个用这个for single,第一个是等待的句柄,第二个是时间啊,这个我们永久等待好,然后呢,我们现在呢,开始实现我们刚才说的这个功能啊,就是我们今天要说的这种。
02:24
首先我们写一个这个异啊,上边是异常保护块啊,下边是处理。好,然后呢,我们这回呢,我们要了解的是一个这样的API啊。
03:02
L。啊,我们直接F1啊。看到这个函数了吗?它的功能呢,就是什么?就是复制一个对象的一个句柄,然后有这么多这个呃,这么多参数啊,那我们这里边关比较关键的参数呢,是什么呢?是我们首先第四个。二三嗯,对,应该是这个第四个啊,第四个呢,我们看一下它是什么,它是指向这个接收重复句柄的这个变量的一个指针啊,然后第一个是什么呢?是要复制的这个句柄,然后最后一个呢,是一个这个标记啊,可以是零,也可以是其他的啊,那我们在这儿呢,我们就给他选一个这个忽略我们前面的一个参数啊呃,这个权限参数啊,然后呢,具有同样的这个访问权限,那我们用这个东西是干什么的?我们这个其实有两种操作啊,你可以看到我们用它可以复制这个句柄,而用这个呢,可以关闭句柄,那我们可以干嘛呢?先用这个红啊,把这个句柄复制出来啊,复制一个无效的句柄,然后呢,再用干嘛呢?用这个close关掉它啊,但是关之前呢,我们在前面我们用过一种叫做set handle information的一种方式啊,让它禁止关闭啊,然后这两种操作之后呢,如果是在调试器的状态下呢,它就会触发异常啊,如果正常情况下呢,就会正常运行啊那。
04:26
根据这个原理啊,它就会干嘛呢,就会这个满足我们的一个条件啊,那首先我们在里边呢,我们要声明一个handle。啊,这憨豆是一个。这应该叫叫啥呢。啊,它第一个是要被复制的啊,那我们想要复制,肯定是想要复制一个这个无效的,对不对啊,那所以说呢,我们直接给他复制一个负一啊,然后后边这几个呢,我们要被复制这几个呢,我们都给它一啊第四个呢,是要复制到哪去啊,这个呢,我把我们这个地址给它传进去,好剩下两个呢,是这个一个是权限啊,一个是这个我看一下那是什么。
05:14
啊,这个是访问权限,这个是是否可以继继承啊,那这个呢,就是否继承呢,我们就可以给他传一个false,然后前面这个权限呢,我们用now啊给它忽略掉。最后这个呢,我们选择用这个红啊,把前面这个参数忽略好,这就是复制了,复制完事之后呢,我们现在呢,要用什么,要用这个set handle。啊information啊来进行设置,设置的呢,就是我们刚才啊创造出来的这个无效句柄,然后呢,这个无效句柄呢,我们现在呢,要给它设置成禁止关闭的啊,我直接F1进去看看它有没有现成的红,可以复制一下。
06:02
啊,就是这个啊,如果设置了此标志啊,调用close handle函数就不会关闭这个对象距比啊,所以说呢,我们直接用这个啊,然后它的下一个参数呢,和它是一样的啊,那我们两个就设置成一样的就行了,好,现在呢,在这一局设置完事之后呢,它就没有办法被关闭了啊,那所以说呢,我们现在呢,如果关闭它的时候就会触发异常啊,当然调器状态下触发异常普通下情下不会啊,那么接再接下来呢,我们要用什么?用我们这个东西啊,把它呢给它这个掉啊,这次呢,我们把我们刚才复制的这个东西呢,放到第二个参数上啊,然后呢,我们后边呢给它来一个。嗯,再来一个,再来一个憨的。234原憨豆设置成原来的啊,然后这个是一个新憨豆,然后呢,后边这个红呢,我们给它替换一下啊,它有一个红呢,是只是要要你把它关掉的,你看关闭原句柄啊,那我们刚才放在这个位置的第二个参数,这个就是什么,这就是圆句柄啊,那我们现在呢,把它这个关掉。
07:13
有这个close,那因为我们刚才设置的不可以关啊,所以这个位置呢,它就会触发一个异常,那我们在这个位置呢,如果正常走下来啊,我们就给它返回一个false啊,因为它走到这儿肯定是没有问题的,如果它触发异常呢,就会进入到这边来,那进入到这边来,我们直接return一个处就行了,好,那这样之后呢,我们就可以在这个我们这边啊,给它进行一个调用啊,我们判断一下啊if啊,我们如果是这个checkbug。那这边呢,就是发现条是脆了啊,我们直接给他输出一下。Bug啊,如果不是呢,我们在这边呢,给他输出什么,输出一个run。
08:09
好,每次运行之前呢,我们给它设一点间隔。好,那么现在我们直接运行。啊,直接运行它就是一个debug的一个状态啊,然后呢,我们把它关掉,关掉之后啊,我们再给它来干嘛呢?再给它用这个没有调试器的情况下来运行,也可以看到它就是一个软的一个情形。然后我们还可以用这个调试,这个第三方调试啊,X64DBUG这种啊,来给他试一下。
09:03
也可以看到啊,也是可以识别出来的啊,因为它在跳水器状态下会触发这个异常啊好,那这个呢,就是咱们今天的这个第一种啊,第一种然后呢,今天呢,咱们还有一种啊,还有一种呢,就相对复杂一点啊,那这个呢,我们就要来了解一下什么是硬段啊。好,我们给它粘在这儿啊,然后呢,我们硬件断点这个东西,首先我们要来看一下我们的这个调试器啊,在调试器而言呢,我们随便拖一个东西进去啊,然后给它运行到我们的这个主拈下我们右键啊,然后你可以看到这有断点啊,断点呢,你可以设置设置各种各样的这个断点啊,我们可以看到上边啊。呃。这这有啊,这不是那个不是不是样了,这个调试啊,然后。哎,他这个直接下那个什么在哪啊。啊算了,就直接说吧,就这儿啊断点你可以看到这有设置这个硬件断点,然后你在内存那儿呢,你可以右键是怎么样,你可以设置这个我们的内存断点啊,然后这也可以是硬件断点,然后你普通的时候,你直接F2这下载是什么是软件断点,也就是说呢,我们在这个我们的调试器里起码是有三种断点,第一种呢,就是软件断点,第二种呢,就是这个内存断点啊,第三种呢,是这个硬件断点啊,那前两种呢,就是什么?第一种软件断点,它其实呢,就是基什么,基于我们的这个,呃,INT3的也就是一种中断啊,然后呢,它这个里边呢,在内存里改的呢,是0CC啊,它是汇编的就是INT3啊,所以说呢,它其实说触发一个三号熔断,然后呢,进行什么进行的软件断点,然后内存断点是什么意思呢?内存断点也是异常啊,也是异常,只不过不是直接通过这种方式触发异常。
10:58
异呢是置了啊内存属性啊,比如啊比如不可。
11:07
啊,他这个东西正常情况下是可读可写可执行啊,有三种权限对不对啊,那如果说什么权限都没有呢。那如果是这样呢,那这样的情况下啊,你无论干什么啊,读写还是执行它都会产生异常啊,那这种情况下呢,就可以干嘛呢,把它当做一种断点啊,这个这个,然后直接拦到自己那边去进行处理啊,就是用这内存属性,而硬件断点呢,它呢更为复杂一些啊,硬件断点呢,是基于什么,基于我们CPU给我们提供的这个呃,寄存器硬件的,呃,我们可以来看一下这张图啊,这张图呢,就是我们的一个这个硬件的一个断点的一个相关的一个寄存器啊,从DR0到DR7啊,都是这个硬件调试计算器,那这些东西都是什么样的功能的,那首先呢,我们正常情况下啊,有这个要使用的啊,其实最多的其实是DRR0啊到DRR3,然后呢,是DRR6和DRR7啊,其实DR6都不怎么用啊,我们自己设置的时候都不怎么用啊,DRR7和这个前四个比较常用啊,但是它有这么七个,呃八个啊,那这些都是干什么的呢?首先呢,DR。
12:17
零啊到第二三之间啊,这些东西呢,都是用来保存硬件断点的一个地址的啊,你无论是32位还是64位,你要下一个断点,你像软件断点的情况下,你直接在那改了一个东西触发就走了,不用放地址啊,内存断点呢,你设置的这个内存属性啊,那那但是呢,实际上我们在管理的时候呢,我们软件断点和内存断点也有这个地址保存起来,但是呢,我们硬件断点呢,是直接采用了一种寄存器的形式啊,来给它保存的啊,那我们这个地址呢,它是线性地址啊,它不是物理地址啊,因为我们如果是这个呃物理地址的话,其实是有一定问题的,因为我们的这个CPU呢,在线性地址被翻译成这个呃,物理地址之前呢,就会去处理断点啊,所以说呢,我们在这个保护模式内啊,就不能用这个调试均衡器啊,对你的这个物理内存进行设置,我们的DR4和DR5你。
13:17
个操作的时候啊,需要一些特别的操作啊,就是如果说啊,它有一个东西叫做调试拓展啊,如果说调试拓展这个东西没有开的情况下,你的DR4和D其实就是呃,DR6和DR的别名啊,其实他们俩是互相映射的,那如果说是这个调试拓展开了啊,也就是你CR4里边有一个叫de的一个啊,你把它设置成啊,或者说叫一啊就开启了啊,任何这个你如果它开启了任何对这个DR4和D5的这个调用啊,它会。它会产生一个异常叫sharp UD啊,也就是一个非FA指令异常啊啊然后呢,我们这上面呢,就是DRR6和DRR7啊,那我们首先呢看一下DR7啊,DR7的它比较多,而且没那么多保留项啊,但是DR6的保留项比较多,那我们首先呢看一下啊,它这些东西呢,你可以看到都有什么零至三啊,这零至三都是什么关系呢?其实就是和底下这个硬件断点均算器呢,进行什么进行一个一一对应的,那首先呢,你这个L0啊G0啊,还有这个RW0,以及你的这个LN0啊,这些东西呢,都是和底下DR0对应,那以此以此就可以知道啊,你的L1G1啊,然后呢,还有个这个LN1RW1啊,这些东西呢,都是什么和DR1对应的啊,所以说呢,他们呢,这个DR7实际上是对前四个啊进行一个管理啊,进行一个一一对应的,你像是它的这个RW域啊,RW域这个东西是最好最好理解的就是什么呢?就是这个。
14:48
啊,也就是读写啊,读写域那四个读写域分别对对应的就是这四个这个地址计行器啊然后呢,这个东西呢,主要是为了来这监控你这个断点的一个访问类型啊,你像是它这个东西你可以看到啊r we占十六十七啊也就是说呢,它能放俩数啊,也其实也不是说俩数吧,就是两位啊,也就是说有有这么四种状态啊,就是000110和幺幺啊然后如果它是零零的情况下呢,就是只有在执行的时,执行对应断点的时候呢,才会进行中断啊,零幺呢是你如果是写数据的中断,幺零呢是这个就是IO的时候中断啊,然后111111呢是读写数据都中断啊,但是读指令不不不处在其中,那然后呢就是LLN就是长度域啊,那从零到三呢,也是一一对应啊,那如果说是这个,它也是什么能放两位啊,那这两位放的什么呢?如果是零零就一字结肠啊,零一就两字结肠,但是啊,幺零不是四节幺零。
15:48
八字节,而幺幺是四字节啊啊所以说呢,这这块呢,就是什么这块呢,就是用来这个设置断点长度的啊断点长度的,然后就是我们的这个L0啊到L3这一堆啊,这一堆呢是局部断点启用啊,就是说呢,我们如果说要设置成一,就把这个断点打开了,如果是零呢,就是关闭了,也就是说呢,我们用这个东西其实可以监视到你这个东西到底有没有采用的啊,采用这个断点,然后G0和G3到G3呢,它主要是这个全局的啊,区别呢,就是我们的这个呃为零为零关闭之后啊,这个CPU呢,不会主主动的对你的地址进行一个清除啊,然后这边呢,还有什么呢,还有这个这个G和GD啊这几个东西啊,剩下这些就保留和死位啊,你看这一就是死位,就是一定要设置成一啊这些东西。
16:38
然后比如说我们这个Le这个位啊,Le和GE这两位啊,你其实呢是可以不不管他的,因为这个东西呢,是在这个486之前使用的一个位啊,到现在呢,保留它主要是为了兼容啊,但实际上已经没有人真实的去使用它了,而GD这个位呢,GD位是有用的啊,GD位呢,它是一个访问检测位啊,如果说GD为一为一的情况下啊,那么GCPU啊,遇到修改DRR计算器的一个指令啊,它就会产生一种异常啊,然后呢,我们再来看一下什么,再看一下我们的这个DR26寄存器啊,DR26寄存器呢,是调试状态寄算器啊,上面的管理啊这些状态啊,那么像是我们这个B0到B3啊,很明显它也是对应着我们DR20到DR23之间的东西啊,如果说我们的B0啊,比如说B0啊被质疑了啊,那就说明什么啊,就说明我们的这个RW0 L0 DR0的条件啊,是都被满足的啊,也就是上边我们这些东西啊是都被满足的,所以它的状态呢,就被质疑了啊,然后呢,我们后边呢,还有BDBSBT啊,BD这个东西呢,是和我们上G。
17:38
地位是这个相关联的啊,就是当你的CPU呢,发现了需要这个修改这个电压计存器的指令啊,那就会停止这个执行,然后呢,把这个BD位设置成一,然后呢,交给一个叫沙DB的一个这个处理程序啊,那你像这个BS位呢,BS位是单部位啊,这个单部位呢,是和你的状态这个呃,Flag寄存器啊,就是e flag啊这个寄存器里的TS位相关联啊,如果是这一位啊,是一的情况下,那就表示啊这个是什么?是单步触发的,然后这个BT呢,这个T呢,实际上是TSS的T啊,就是这个任务段相关的啊,然后呢,我们当这个任务切换的时候啊,如果发现这个下一个TSS的这个是一的情况下啊,T标志是一的情况下啊,那就会这个中断到我们这个调试中断程序里边啊,那我们说了这么一大堆呢,主要是给大家介绍一下这个硬件调试啊,它是怎么样一个原理上的啊,那现在呢,我们如果要进行反调试啊,那现在呢,也要进行对他们的一个这个检测,那我们这个检测呢,其实呢,很多方式都可以检测。
18:38
到啊,最简单的方式呢,其实啊,首先呢,你可以检测它有没有开启这个断点啊,但是呢,它开启断点呢,有一定误差,万一它开启了,但是没有下对不对啊啊所以说呢,我们最简单的方式呢,就是对你的几个这个硬件地址计算器啊进行一个这个测试啊,如果说这几个断点计算器开了啊有值啊不为零啊,那这种情况下,一般情况下就是被下断点了啊啊我们可以判断它是有这个调的。
19:05
好啊,那如果说这个没关注的啊,关心关注一下啊,然后每周六周日都有直播啊,然后呢,如果有需要领取往期录播课程的,可以联系咱们的这个老啊,在公屏上可以联系到他啊好,那我们说完了它这个原理啊,那现在呢,我们来开始写这个代码啊,如何去检测他的这个硬件断点。我们的调试寄存器呢,它都是和这个硬件相,不是和这个线程相关的啊,这这这些器都是每一每一个线程有一套啊,就是从零到啊,这是线程相关的,所以说你要的时候,你要把所有线程都一遍,因为你不知道它调试的时候,它的调试器这个断点是下在哪一个,这个硬件断点是下在哪一个线程里的,所以说你要干嘛呢?你就要给它来这个呃遍历一下。
20:14
首先呢,我们来给他搞一个这个,嗯,搞一个这个快照,我们用来便利一下线程这H32C s s NP three的。啊,下边呢,是他的这个进程ID,进程ID呢,我直接获取一下本地的。好获取完事之后呢,接下来呢,我们搞一个这个结构啊,用来存它的线程信息啊,就是我们的这个three的ENTRY32。
21:02
然后对它进行初始化啊,他需要初始化它的这个size。好,说完赛制之后呢,接下来呢,我们就要开始对它这个一个循环便利了啊,在循环便利之前呢,我们在上边呢,给它放一个这个现成句柄啊,一会要用到。好,那现在呢,我们开始给他便利啊,我们直接用一个Y。While呢?我们直接开始查找下一个啊,Three的32。N,诶。第一个参数呢,就是你便利的这个句柄,第二个呢就是结构地址。好了之后呢,我们现在呢,就开始来对它里边的东西啊,进行一个这个循环查找。
22:03
那首先呢,我们来一个这个判断。判断一下我们当前获取的一个进程ID啊,是不是我们想要的这个进程ID。嗯。啊,少报的货号。如果是的情况下啊,我们对这个线程啊进行一个打开,等于open three,然后以全权限进行打开的。哦,然后啊不继程句柄最后呢,是它的线程ID,线程ID呢,在我们的线程结构里,TT32里边有一个这个three的IDTH32THREE的ID啊直接打开,打开之后呢,我们现在呢,要获取线程的上下文,从上下文里拿到你当前的这个调试计算器的状态,那这个东西呢,它是一个叫做contest的一个结构。
23:20
啊,我们对直接对它进行初始化,初始化成零,然后呢,我们设置一下我们都需要获取什么啊,它这里边呢,是有一个成员啊,我们来F12看一下,首先呢,它可以获取的东西非常多啊,比如寄准器啊,这个这个占机准器啊,通用准器啊啊,然后还有这个我们的这个呃,调试器啊,这些东西啊,但是呢,它是通过这个context f flagx啊来决定你到底要获取什么,你可以给它设成啊,但是呢,如果说你只需要获取到调试的时候呢,你可以也可以不设置成,而是什么呢?而是直接来找它这个调试器。也就是contest啊,你看这有contest all嘛,我们不用all,我们用这个debug啊,这样呢,获取到的呢,就都是这个调试相关的这个净音器,然后现在呢,我们只是要获取,不用设置,所以说呢,我们就给它用一个这个get。
24:16
瑞瑞的con。然后啊,把地址传进去,用来进行获取,接下来呢,我们现在呢,就可以来这个进行判断了啊if啊if什么if,你的contest里如果说DR0。不等于零,然后或者啊,把这个复制一下。或者什么DR1DRR2DRR3啊不等于零啊,三个地址进去,如果不等于零的情况下,那我们就直接啊return一个处,否则呢return一个false,好,现在呢,对它呢进行一个调用。
25:24
好,现在呢,即使你用什么用这个VS进行调试也是没有问题的,它只会显示乱啊,因为VS它不下什么,不下这个硬件断点啊,我们这次呢,它检测的只是检测硬件断点的功能,所以说比如说你用这个什么,用这个调试器啊,用调试器才可以啊,你把它啊进行什么进行启动。启动之后呢,你可以先给它放起来,放起来。现在它不是run起了吗?Run起来之后呢,我们现在呢,直接右键啊,我们转到啊,不是搜索吧。我们找一下这个主模块里的一个字符串啊,我们直接跳过去。
26:04
啊,在这附近啊,随便吧,随便一个地址吧,然后我们直接给他这个右键啊,然后断点啊,然后设置一个硬件断点。设置完事之后呢,我们现在呢,你可以看到它直接掉到掉到这了,然后运行,诶你看它是不是就第八个了,对不对啊。啊,这个呢,就是检测什么,检测硬件断点的一个功能啊。啊,然后呢,今天的这个技术部分啊,就这么多了啊,有没有什么问题啊,没什么问题的话,我们就进入下一步啊。
我来说两句