方案1
在C:\Qt\Qt 5.xx.x\Tools\mingw730_32\opt\bin(32位)
或者:
C:\Qt\Qt 5.xx.x\Tools\mingw730_32\opt\bin(64位)
找到 libeay32.dll ssleay32.dll 两个文件
1)将这两个文件复制到可执行文件目录
2)复制两个文件到:\Qt\Qt 5.xx.x\Qt 5.xx.x\mingwxxx_xx\bin 的目录下

我用这个方法并没有什么效果,经过一番搜寻找到了第二个
方案2
去https://slproweb.com/products/Win32OpenSSL.html 下载文件
我下载第二个Win64 OpenSSL v1.1.1e的文件
安装过程全部默认安装
并在Qt的Pro文件中添加
INCLUDEPATH += C:\Program Files\OpenSSL-Win64\include\openssl
问题成功解决!

调试分为用户层调试以及内核层调试
用户层调试是针对应用程序的
内核层调试是针对系统的
用户层调试使用API
DebugActiveProcess调试目标进程
WaitForDebugEvent等待调试信息,接受调试消息会暂停进程
ContinueDebugEvent继续调试进程
用户层调试内部机制
InitUserDebugSystem初始化调试系统信息
    获取函数地址
    判断是否为调试csrss.exe
DebugActiveProcess调试目标进程
    DbgUiConnectToDbg
        初始化Objeck_Attributes
        获取DbgSsReserved(TEB(线程)结构中)
            如果DbgSsReserved第二个成员(调试对象句柄)为空,则创建调试对象
                ZwCreateDebugObject创建调试对象
                     获取当前调试模式
                     判断句柄是否可写
                     PbCreateObject创建调试对象并初始化
                     将句柄插入句柄表(当前进程)
    GetTargProcessHandle获取目标进程句柄
        ZwOpenProcess打开进程
    DbgUiDebugActiveProcess调试目标进程
        GetThreadSsReserved获取DbgSsReserved成员
        ZwDebugActiveProcess(进程句柄,调试对象句柄)
             得到被调试进程的eprocess
             判断被调试进程是自己,或是系统进程,是的话退出
             判断保护模式,如果是用户模式且当前进程不是保护模式且被保护进程是保护模式则退出
             根据调试句柄获取调试对象
             使用ExaquireRundownProtection保护进程不退出
             发送虚假进程消息
                  收集所有线程创建消息
                       构造apimsg消息包
                       插入数据结构中
                            插入调试对象内
                       发送系统dll消息
                  切换目标进程KeStackAttachProcess
                  收集所有模块创建调戏并退出模块进程
                        收集最后一个线程后新创建的消息
                        收集垃圾消息
                        判断主线程的创建消息是否完成,如果未完成等待完成
                        处理垃圾消息
             设置调试对象给被调试的进程
        DbgUiIssueRemoteBreakin远程断点
            RtlCreateUserThread创建远程线程调用ntdll的DbgUiRemoteBreakin
            DbgUiRemoteBreakin在被调试线程中创建一个线程并执行int3断点触发异常使调试器接收并中断
WaitForDebugEvent
    DbguiWaitStateChange等待调试信息到来,在创建好的调试对象上等待
        ZwWaitForDebugEvent
    DbgUiConvertStateChangeStructure转换PDBGUI_WAIT_STATE_CHANGE到DEBUG_EVENT
    根据调试事件信息使用switch进行判断
        进程创建消息
            保存进程句柄
                PDBGSS_THREAD_DATA放到SsReserved数据结构第一个
            保存线程句柄
                PDBGSS_THREAD_DATA放到SsReserved数据结构第一个
ContinueDebugEvent
    DbgUiContinue
        ZwDebugContinue
    RemoveDbgssThreadData移除退出线程
        判断线程通过HandMarked句柄标记,是否是本线程以及本进程
        HandMarked句柄标记在WaitForDebugEvent中的退出线程以及退出进程事件中进行MarkThreadHandle

QueueMessage队列过程
将调试对象的等待设置,并等待调试事件的事件
由NtWaitForDebugEvent接收到调试对象的等待消息,包装apimsg发送给用户层,并由Necontinue设置调试时间的等待消息,促使同步消息机制正常运行
NtWaitForDebugEvent Handle Alertable TimeOUT PBDGUI WAIT STATE CHANGE
NtWaitForDebugEvent 调试对象句柄 Alertable 超时 接收调试事件PBDGUI WAIT STATE CHANGE
     判断用户层还是内核层,判断超时指针是否可读
    使用系统时间填充时间
    使用ObReferenceObjectByHandle获取调试对象
    使用KeWaitForSingleObject等待调试对象有信号
        如果是同步事件,在Queuemessage的KesetEvent设置有信号
        在SetProcessDebugObject会设置有信号
        int3断点硬件断点内存读写访问错误等会通过其他函数
        -------------------------------------
        判断调试对象是否存在
        遍历链表判断调试事件是否已读或者不活跃的
        再次遍历判断是否是第一个节点,如果不是由第一个到第一次遍历的节点都设置为不活跃(不理解)    
        转换结构
        标记为READ



第二种通过创建调试进程来进行调试的主要通过DbgMapActiveProcess进行收集模块信息,通过DbgkCreateThread收集线程信息

异常由程序生成异常,转给操作系统,操作系统发送给调试器

中断和异常是导致处理器转向正常控制流之外代码的两种操作系统条件。
中断是异步事件(可以在任何时候发生),并且与处理器当前正在执行的任务毫无关系。中断主要由I/O设备、处理器时钟,或者定时器产生的。
异常是一个同步条件,它是一个特殊指令 执行的结果,与当前处理器正在执行的任务有关。
系统为每一种中断或异常提供了一个陷阱处理器,各种陷阱处理器放到一起称之为idt(中断描述符表)
IDT中每一项都是一个IDTENTRY
IDTENTRY中低字节和高字节合并在一起是陷阱处理器的地址
执行陷阱处理器之前,需要把该异常发生时的现场保存下来,方便后续指令继续执行
将现场保存在ktrap_frame
进入后会自动填充
EIP
SegCs
Eflags
当前ESP
内核层fs代表处理器控制块kpcr
用户层fs代表teb
流程
进入异常-保存现场环境-处理异常-恢复现场环境-跳到异常处继续执行
陷阱处理器调用流程
CommonDispatchException
申请一个Exception_Record结构,构造一个异常记录
在函数内调用KidispatchException处理异常(可能不会调用KidispatchException处理异常,如果是虚拟86模式或参数问题)
     构造一个Context数据结构(context数据结构跟处理器有关)
     由陷阱帧转换为Context数据结构
     判断是用户模式异常还是内核模式异常
     用户模式异常
          第一次到来
               判断当前DebugPort是否存在
               如果没有调试器,则提交给内核调试器处理,如果处理就执行退出工作
               如果内核调试器不处理则调用DbgForwardException函数处理
                    DbgForwardException把异常调试信息封装成DebugEvent数据结构插入到调试对象中
               如果DbgForwardException执行失败
               用户层异常处理程序,KeUserDispatchException,进行二次异常分发,调用KiUserExceptionDispatcher
                    KiUserExceptionDispatcher
                    调用rtlCallVectorHandlers如果参数为0是VEH否则是VCH,VEH和VCH的数据结构统一存储在一个全局变量数组中
          第二次到来
               用户模式调试器,如果没处理就向异常端口发送消息
               如果两个都没处理则结束当前进程
     内核模式异常
          第一次到来
               寻找内核调试器,内核调试器处理了就返回,否则交给异常处理程序
               异常处理程序 给我们自己处理异常一次机会RtlDisPatchException
          第二次到来
               寻找内核调试器,内核调试器处理了就返回,否则交给异常处理程序
               蓝屏错误程序


     收尾,将context转变为陷阱帧
调用ExceptionExit退出函数

Windows异常处理方式
1.VEH向量化异常AddVectorExceptionHandler注册异常处理程序
2.VCH向量化异常AddVectoredContinueHandler
3.SEH结构化异常 Try—except
4.SUE结构化异常
执行顺序,VEH和VCH互不干扰 VEH和SEH有干扰,如果VEH为EXCEPTION——CONTINUE_SEARCH则会进入SEH,如果执行SEH不会执行VCH

_
VEH
SEH
VCH
SUE可以通过SetUnhandledExceptionFilter函数制作异常,注册全局系统异常回调


RtlDispatchException-VEH
                               -SEH/SEF
                               -VCH
SEH只针对当前线程
UEF只针对当前线程
VEH只针对当前进程
VCH只针对当前进程
用户层fs寄存器指向teb结构
xp环境下可以通过汇编构造直接插入SEH
win7开始存在保护
使用CallSEHandler 一次只调用一个
VEH可以实现只是在异常系统上编写一个调试器,完全不需要我们的调试系统
RtlDispatchException-
    RtlGetStackLimit获取内核栈顶栈底
    RtlGetRegistertionHead获取异常链表 kpcr
    RtlIsValidHandler判断异常回调函数是否非法
    RtlpExcecuteHandlerFroException执行SEH异常回调函数
调试寄存器 DR0-DR7介绍
DR0-DR3 调试地址寄存器
DR4-DR5保留
DR6调试状态寄存器
DR7调试控制寄存器
int3不能监控数据的可读可写
RW0-RW3
RW 指定监控地址的访问类型
00,仅当执行对应地址的指令中断
01,仅当写入对应地址中断
10,当CR4寄存器的调试器拓展位被设置时,其意义位当向对应地址进行io读写时发生中断,一般来说无意义
11,当读写对应地址时中断,读取指令指令除外
LEN0-3指定监控地址的长度
00 1字节长
01 2字节长
10 未定义或者表示8字节长,取决于cpu系列
11 4字节长
L0-3 与DR0-3相对应,表示只对当前任务有效,每次异常发生后L0-3会清除
G0-3与DR0-3相对应,表示只对所有任务有效,每次异常发生后L0-3不会清除
在用户层可以用WaitFordebug接收异常
再内核层可以使用HookRtlDispatchException

调试对象
附加调试进程,调试对象是由DbgSetProcessDEbugPbject设置的 由NtCreateDebugObject创建的
调试对象在Process->DebugPort,被调试进程
驱动保护通常直接清除被保护进程RPROCESS的DebugPort
处理方法
1.从根源处进行处理
     那里的代码清除DebugPort,我们就对哪里的代码进行打补丁
     优点:简单(已过期)
     缺点:驱动保护更新后必须从新找到清除DebugPort的代码,并且存在大量的检验
2.改调试对象位置的偏移
     例如转移到CreateTime,VdmTrapcHandler(推荐)
3.自建调试系统
     自己写调试系统替换原来的内核调试系统。然后修改操作DebugPort
4.通过硬件断点进行处理
     不适合DebugPort清零
硬件执行断点未执行指令
硬件读写断点会执行指令
TotalNumberOfObjects当前创建了几个调试对象
TotalNumberOfHandles当前创建了几个调试句柄
对两个值进行不断检测,只要有改变,就会重启
反调试方法
     1.hook掉更改这两个地方的函数,不让其更改
     2.用一个新的调试对象类型
1.更改函数
ba v4 85bd2768+0x18
ba v4 85bd2768+0x1c
调试对象都属于调试对象类型
2.创建新的调试对象
ObCreateObjectType创建对象类型

对ValidAccessMask清零绕过方法(控制访问权限,调试对象基于权限创建)
1.哪里访问处理哪里的指令
2.替换新调试对象类型
     重载内核获取地址
硬件断点是cpu直接支持的
内核层可以直接访问
用户层不可以
GetThreadContext对应NtGetContectThread会调用PsGetContextThread向目标插入APC  PspGetSetContextSpecialAPC
SetThreadContext对应NtSetContextThread会调用PsSetContextThread向目标插入APC  PspGetSetContextSpecialAPC
PspGetSetContextSpecialAPC
     获取和设置线程上下文最终是通过KeContextToKframes和KeContextFromKframe实现的

KeContextFromKframe
     内部是通过内核栈上保存的一个KTRAP_FRAME结构获取相关线程的上下文内容
     其中获取调试寄存器的内容后,通过KiUpdateDr7此函数更新一些东西
     线程对象的0x3偏移位置是DebugActive成员,这个成员是记录哪些调试寄存器被设置了
     反硬件断点,我们可以通过对某线程的DebugActive成员进行判断,如果存在值得话,就是存在硬件断点
     对抗方法对PsGetContextThread和PsSetContextThread进行hook

KeContextToKframes内部是通过设置内核栈上面保存的KTRAP_Frame结构来更新线程上下文的
其中并没有对我们的调试寄存器进行直接的设置工作


调用api原理
xxx-进入内核-KiFastCallEntry---xxx对应的函数---KiFastCallEntry---退出内核  uKiServiceExit-返回xxx
windows为了区分用户模式硬件断点和内核模式硬件的,他的设计思路如下
当时内核模式硬件断点时,可以直接设置调试寄存器
当是用户模式硬件断点时,因为具体的设置工作是要在内核中进行,如果直接在KeContextToKframes中进行设置调试寄存器的工作的话,那就等于设置内核模式的调试寄存器了,所以windows选择在目标线程退出内核的那一刻时,进行设置,那么退出内核后,硬件断点就生效了

通过对目标线程内核栈上的陷阱帧进行获取和设置工作,也可以达到反硬件断点与检测的目的



idt hook
1.想办法干掉驱动保护的监控,然后恢复
    这种显然是可行的,但是不通用,代码比较难实现。
2.修改中断的段地址
    kpcr中存在idt和gdt表
    idt表有lowoffset和hioffset拼接出陷阱处理器,
    表中selector是段选择符,fs是一个段寄存器,再windows中,实际它就是一个段选择符
    段选择符
   0-1 00是内核模式 否则是用户模式
   2    表指示位 LDT GDT全局描述符和局部描述符 如果为0就是GDT 
   3-15 表示第几个
GDT表中为KGDTENTRY
GDT可理解为段基质数据库
3.利用硬件断点接管中断
     可行,稳定性有问题,还有细节方面
4.vt接管中断执行流程
     接管中断,目前比较流行的技术,有人通过vt技术实现了无限的硬件断点,通过修改内存属性,然后vt接管异常


Fs指向了处理器控制块
fs[0x124]代表当前线程
cpu在读取fs的时候,不是按照地址来进行解析的,而是fs本身就是一个数据寄存器,所以cpu把fs当成一个段选择符然后进行解析。
首先cpu会判断当前fs再gdt还是在ldt表中,然后去对应的表根据fs的第三位-第十五位的值得到在该表的下标值,最后解析出下标值得地址,就是我们的处理器控制块
cpu在执行中断的时候,会得到中断描述符中中断的中断例程(InterruptFunc)。然后cpu同时也会得到该中断描述符中的段选择符(Base),解析出段选择符对应的GDT中的地址,然后执行
Base+InterruptFunc=目标真正要执行的地址

dt nt!fast_mutex打印快速互斥体

替换FastMutext锁
用内核重载重定位确定位置

switch分支语句过多时,编译器再编译的时候会构造一个跳转表,然后通过功能号可以跳转到对应的语句来执行代码,节省了时间
我们的跳转表是需要重定位的,所以我们在重载内核的时候,等于也重载了定位
eg.ZwQueryInformation

系统创建线程,初始化的时候,会默认当前创建的线程是cui线程(无窗口)
系统调用服务的时候,不是通过在ssdt查找某个系统服务
而是用过我们当前线程KTHREAD的ServiceTable成员
会使用+0bc ServiceTable
edi+ServiceTable
如果是cui edi为0
如果是gui edi为10
窗口的所有系统服务没有在ssdt中,而是在shadow ssdt中,所有如果某个线程是cui线程,他内部调用了某个窗口相关的api
这个时候,进入KifastCallentry后,会在ServiceTable去找对应的系统服务地址,而现在Service是ssdt,ssdt中没有任何相关的窗口的系统服务
这时候就找不到系统服务了,这时候 kifastcallentry就会去调用psCovertToGuiThread,把当前线程转换成Gui线程,然后重新再找一遍
psCovertToGuiThread内部实际做的最关键的事情就是把当前线程的ServiceTable改写成Shadow SSDT的位置    
PsSetThreadWin32Thread在psCovertToGuiThrea的上面


objecthook object_type_initialize的openprocedure
对对象类型的SecurityProcedure进行改写,就可以达到过滤解析对象的目的
替换绕过object hook方法

1.常见一个新的函数对象
2.对SecurityProcedure下断点,对调用SecurityProcedure的进行hook达到反调试的目的

1.重定位问题
普通情况下,switch跳转语句比较少时,它基本模拟if else

线程创建的时候,因为默认是cui线程,所以线程的ServiceTable会被初始化SSDT地址

句柄表是存储当前进程所有引用的对象的一个数组
放入句柄表就是放入objecttable
我们调试某个进程的时候,调试器会打开目标进程,线程,甚至是文件等等。
这些打开操作一般都会返回一个句柄值给调试器操作,所以调试器进程的句柄表中
就有了关联目标进程的一些句柄,驱动保护就会遍历系统中所有进程的句柄表,然后查找句柄表中是否有自己的信息,如果有的话,那就是被打开或者调试了

遍历句柄表
1.调用ZwQueryInformationSystem
2.调用ExEnumHandleTable
3.手动遍历

回手掏
1.把访问句柄表的地方全部hook,修改eprocess下面的objecttable指向一张假的句柄表,之后当有访问句柄表的操作是,我们判断是否遍历我们要保护的进程的句柄表,如果是的话,我们看看是谁在遍历,如果是我们自己的进程再遍历我们就放过,如果是其他进程再遍历,我们就不用管了

ApcStateIndex是1为apc状态为0则不是
如果是则去SaveStateApc

2.用硬件断点实现隐藏

第一类 钩子
目前很多保护都是采用以钩子为主,其他方式为否的反调试手段。比如对NtOpenProcess挂钩来达到反调试效果。其他还有很多函数可以达到反调试效果,在这里不一一列举。
处理方式可以想办法恢复hook,或者重载内核便能绕过这些钩子。
第二类 数值修改
1,目标进程EPROCESS下面的ProtectedProcess成员为TRUE,并且调试器进程EPROCESS下面的ProtectedProcess为FALSE时,可以达到反调试效果。症状是调试器无法打开你的进程。
2,目标进程EPROCESS下面的ProcessDelete成员为TRUE,可以达到反调试效果,症状是调试器无法附加你的进程。但是在进程退出时,要把ProcessDelete改变成FALSE,否则系统不会释放进程。
3,对DbgkpProcessDebugPortMutex全局变量的修改,可以使调试器调试某进程时卡死。
4,因为调试器和目标进程建立调试关系后,会创建一个DebugObject用于通信,所以对此DebugObject内部的锁进行改写可以使某调试事件产生时,目标进程会卡死。和第三种方式有异曲同工之妙。当然也可以对DebugObject的其他成员动手脚也可进行反调试,请大家自行测试。
5,可以创建一个DebugObject,填写到EPROCESS下面,由于DebugObject已经存在,所以调试器附加会失效。
6,改写目标进程对象的对象头内的TypeIndex为一个其他值,这会导致调试器无法打开目标进程,从而达到反调试目的。注意在进程退出时要把TypeIndex值更改回来,否则会产生蓝屏。
7,改写内核全局指针变量DbgkDebugObjectType指向的内容,可以阻止调试器附加进程。症状是调试器无法附加进程。
8,改写目标进程线程对象ETHREAD的HideFromDebugger成员为TRUE。当被调试时,当有调试事件产生,目标进程会崩溃。
9,改写调试对象类型TypeInfo成员下ValidAccessMask的值,可以达到反调试作用。
第三类 回调函数钩子
1,object 钩子,此方法已进行过多讲解,这里不再叙述。
2,对内核pIofCallDriver内容进行修改,使其指向一个我们定义好的函数。如果产生IRP下发操作,就可以被我们拦截到,以此达到反调试目的。

检测调试总结:
1,对一些出名的调试器进程进行检测,比如od,ce等。
2,对一些窗口进行检测,发现是否有调试器的窗口。
3,对调试对象类型的TotalNumberOfObjects和TotalNumberOfHandles进行检测,判断系统是否有调试器存在。
4,设置调试对象类型TypeInfo成员的MaintainTypeList为TRUE,然后监控调试对象类型的TypeList链表,如果有调试器创建调试对象,那么该调试对象会生成一个结构_OBJECT_HEADER_CREATOR_INFO并加入到此链表,这时就可以判定是否有调试目标进程。(未完成)
5,针对硬件断点的检测,可以判定目标进程的线程KTHREAD下Header成员的DebugActive是否有值,有值的话就代表当前线程存在硬件断点。(硬件断点被windows设计为针对某线程的)
6,针对硬件断点的检测,可以获取目标进程的线程KTHREAD下InitialStack成员,然后这个值减去0x29C得到的是一个陷阱帧_KTRAP_FRAME结构的指针,其中的DRX系列成员如果有值,就代表当前线程存在硬件断点。
7,对系统所有进程的句柄表进行检测,看看目标进程的进程对象、线程对象、或者一些调试对象是否存在,存在的话就代表目标进程已经被别的进程打开,可以选择关闭相关句柄,也可根据情况退出进程。
8,对目标线程对象KTHREAD下SuspendCount成员进行改写,使之不等于零。当调试器调试目标进程,被中断时,由于目标进程线程的SuspendCount不为零,所以目标进程的线程就无法被暂停。那么调试器实际就等于没有中断目标进程,这会使调试器产生错误行为。


首先我们抓一下包,可以得到数据
http://study2.huatec.com/api/portalnew/loginNew {"loginType":1,"username":"%s","password":"%s"}
然后就可以写代码啦~(代码搞的太乱,已经弃坑)

这里是青山志,我的个人Blog,曾经写过很多文章,但是由于一次更新的意外以及没有备份导致丢失了(我晕

所以这次我决定认认真真的从头做起,留下一点痕迹。

本人喜欢养狗,不爱洗头,无所事事并且喜欢满嘴跑火车。

如果我哪里冒犯到了你,请你一定相信那很有可能不是我的本意。

2020年2月22日 青山留