什么是中断?

 指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。即在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,此时,CPU暂时中止程序的执行转而处理这个新的情况的过程就叫做中断。

比如:除零(0号中断)、断点(3号中断)、系统调用(2e号中断)、以及异常处理等都会引发中断,所以自然需要相应的中断例程去进行处理。

这样操作系统就会用数据结构来维护这些中断例程,这个数据结构就是IDT(Interrupt Descriptor Table)。

中断描述表

IDT表的长度与地址是由CPU的IDTR寄存器来描述的。IDTR寄存器共有48位,高32位是IDT表的基地址,低16位是IDT的长度。

typedef struct _IDTR{
USHORT IDT_limit;
USHORT IDT_LOWbase;
USHORT IDT_HIGbase;
}IDTR,*PIDTR; IDTR idtr; __asm SIDT idtr;

可以通过以上SIDT指令可以读取IDTR寄存器。然后通过MAKEWORD宏把高位与地位组合起来就可以获得IDT表的基地址了。

简单来说,IDT表是一张位于物理内存的线性表,共有256个表项。在32位模式下,每个IDT表项的长度是8个字节(64 bit),IDT表的总长度是2048字节。

kd> r idtr
idtr=8003f400
kd> r idtl
idtl=000007ff

通过Windbg命令 r idtr、r idtl可以读取IDT表的基地址与边界。

如图可以清晰的看见每一个表项了,可是每一个表项8字节都代表什么意思呢?

IDT表中每一项也称为“门描述符”,之所以这样称呼,是因为IDT表项的基本用途就是引领CPU从一个空间到另一个空间去执行,每个表项好像是一个空间到另一个空间的大门。

IDT表中可以包含以下3种门描述符:

任务门描述符:用于任务切换,里面包含用于选择任务状态段(TSS)的段选择子。可以使用JMP或CALL指令通过任务门来切换到任务门所指向的任务,当CPU因为中断或异常转移到任务门时,也会切换到指定任务。

中断门描述符:用于描述中断例程的入口。

陷阱门描述符:用于描述异常处理例程的入口。

以下为三种门描述符的内存布局:

结构体定义为

typedef struct _IDTENTRY
{
unsigned short LowOffset;
unsigned short selector;
unsigned char retention:5;
unsigned char zero1:3;
unsigned char gate_type:1;
unsigned char zero2:1;
unsigned char interrupt_gate_size:1;
unsigned char zero3:1;
unsigned char zero4:1;
unsigned char DPL:2;
unsigned char P:1;
unsigned short HiOffset;
} IDTENTRY,*PIDTENTRY;

其中DPL代表描述符优先级,用于优先级控制,P是段存在标志,段选择子用来选择一个段描述符(LDT或GDT)偏移部分用来指定段中偏移。两者共同准确的定义一个内存地址,对于中断门和陷阱门,他们指定的就是中断或异常处理例程的地址,对于任务门它们指定的就是任务状态段地址。

也就是说段选择子提供一个所谓的段基地址,那么处理例程 = 段基址 + 段偏移。

  那么HOOK方式就有两种了:更改段偏移或者更改段基址。

  我们先忽略段选择子提供的段基址,先简单的认为门描述符提供的高16位和低16位的段内偏移就是该中断的处理例程(其实本来就是,因为32位操作系统中,已经弱化了段基址的概念,运用了平坦模型,也就是说段基址就是0)。

然后我们看看HOOK代码:

#ifndef CXX_IDTHOOK_H
# include "IDTHook.h"
#endif #define WORD USHORT
#define DWORD ULONG ULONG g_InterruptFun = 0; #define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) \
| ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16)) NTKERNELAPI VOID KeSetSystemAffinityThread ( KAFFINITY Affinity );
NTKERNELAPI VOID KeRevertToUserAffinityThread ( VOID ); PULONG GetKiProcessorBlock()
{
ULONG* KiProcessorBlock = 0; KeSetSystemAffinityThread(1); //使当前线程运行在第一个处理器上 _asm
{
push eax
mov eax,FS:[0x34]
add eax,20h
mov eax,[eax]
mov eax,[eax]
mov eax,[eax+218h]
mov KiProcessorBlock,eax
pop eax
} KeRevertToUserAffinityThread(); return KiProcessorBlock ; }
void PageProtectOn()
{
__asm{//恢复内存保护
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
} void PageProtectOff()
{
__asm{//去掉内存保护
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
} void _stdcall FilterInterruptFun()
{
DbgPrint("CurrentProcess : %s",(char*)PsGetCurrentProcess()+0x174);
} _declspec(naked)
void Fake_InterruptFun()
{
_asm{
pushad
pushfd push fs
push 0x30
pop fs call FilterInterruptFun;
pop fs popfd
popad jmp g_InterruptFun
}
}; NTSTATUS
DriverEntry(IN PDRIVER_OBJECT pDriverObj, IN PUNICODE_STRING pRegistryString)
{
IDTR Idtr;
PIDTENTRY pIdtEntry;
ULONG ulIndex = 0 ;
ULONG* KiProcessorBlock; pDriverObj->DriverUnload = DriverUnload; KiProcessorBlock = GetKiProcessorBlock(); DbgPrint("%X\r\n",KiProcessorBlock); while (KiProcessorBlock[ulIndex])
{
pIdtEntry = *(PIDTENTRY*)(KiProcessorBlock[ulIndex] - 0x120 + 0x38) ; DbgPrint("IDT Base:%X\r\n",pIdtEntry); g_InterruptFun = MAKELONG(pIdtEntry[3].LowOffset,pIdtEntry[3].HiOffset); DbgPrint("InterruptFun3:%X\r\n",g_InterruptFun); PageProtectOff();
pIdtEntry[3].LowOffset = (unsigned short)((ULONG)Fake_InterruptFun & 0xffff);
pIdtEntry[3].HiOffset = (unsigned short)((ULONG)Fake_InterruptFun >> 16);
PageProtectOn(); ulIndex++;
} return STATUS_SUCCESS;
} VOID
DriverUnload(IN PDRIVER_OBJECT pDriverObj)
{ return;
}

稍微解释一下,里面获得IDT表的时候没有通过寄存器IDTR进行读取,是因为对于多核CPU来说不一定只有一个IDT表,而通过IDTR来读取只能读到一份表,所以HOOK IDT的时候一定要注意多核问题

系统维护了一个全局的处理器数组KiProcessorBlock,其中每个元素对应于一个处理器的KPRCB 对象。

kd> dt _KPCR
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : Ptr32 _KPCR
+0x020 Prcb : Ptr32 _KPRCB
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void
+0x038 IDT : Ptr32 _KIDTENTRY
+0x03c GDT : Ptr32 _KGDTENTRY
+0x040 TSS : Ptr32 _KTSS
+0x044 MajorVersion : Uint2B
+0x046 MinorVersion : Uint2B
+0x048 SetMember : Uint4B
+0x04c StallScaleFactor : Uint4B
+0x050 DebugActive : UChar
+0x051 Number : UChar
+0x052 Spare0 : UChar
+0x053 SecondLevelCacheAssociativity : UChar
+0x054 VdmAlert : Uint4B
+0x058 KernelReserved : [14] Uint4B
+0x090 SecondLevelCacheSize : Uint4B
+0x094 HalReserved : [16] Uint4B
+0x0d4 InterruptMode : Uint4B
+0x0d8 Spare1 : UChar
+0x0dc KernelReserved2 : [17] Uint4B
+0x120 PrcbData : _KPRCB

在0X120处,有个_KPRCB结构。再来看看这个结构。

kd> dt _KPCR
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : Ptr32 _KPCR
+0x020 Prcb : Ptr32 _KPRCB
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void
+0x038 IDT : Ptr32 _KIDTENTRY
+0x03c GDT : Ptr32 _KGDTENTRY
+0x040 TSS : Ptr32 _KTSS
+0x044 MajorVersion : Uint2B
+0x046 MinorVersion : Uint2B
+0x048 SetMember : Uint4B
+0x04c StallScaleFactor : Uint4B
+0x050 DebugActive : UChar
+0x051 Number : UChar
+0x052 Spare0 : UChar
+0x053 SecondLevelCacheAssociativity : UChar
+0x054 VdmAlert : Uint4B
+0x058 KernelReserved : [14] Uint4B
+0x090 SecondLevelCacheSize : Uint4B
+0x094 HalReserved : [16] Uint4B
+0x0d4 InterruptMode : Uint4B
+0x0d8 Spare1 : UChar
+0x0dc KernelReserved2 : [17] Uint4B
+0x120 PrcbData : _KPRCB
kd> dt _PRCB
Symbol _PRCB not found.
kd> dt _KPRCB
nt!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
+0x010 Number : Char
+0x011 Reserved : Char
+0x012 BuildType : Uint2B
+0x014 SetMember : Uint4B
+0x018 CpuType : Char
+0x019 CpuID : Char
+0x01a CpuStep : Uint2B
+0x01c ProcessorState : _KPROCESSOR_STATE
+0x33c KernelReserved : [16] Uint4B
+0x37c HalReserved : [16] Uint4B
+0x3bc PrcbPad0 : [92] UChar
+0x418 LockQueue : [16] _KSPIN_LOCK_QUEUE
+0x498 PrcbPad1 : [8] UChar
+0x4a0 NpxThread : Ptr32 _KTHREAD
+0x4a4 InterruptCount : Uint4B
+0x4a8 KernelTime : Uint4B
+0x4ac UserTime : Uint4B
+0x4b0 DpcTime : Uint4B
+0x4b4 DebugDpcTime : Uint4B
+0x4b8 InterruptTime : Uint4B
+0x4bc AdjustDpcThreshold : Uint4B
+0x4c0 PageColor : Uint4B
+0x4c4 SkipTick : Uint4B
+0x4c8 MultiThreadSetBusy : UChar
+0x4c9 Spare2 : [3] UChar
+0x4cc ParentNode : Ptr32 _KNODE
+0x4d0 MultiThreadProcessorSet : Uint4B
+0x4d4 MultiThreadSetMaster : Ptr32 _KPRCB
+0x4d8 ThreadStartCount : [2] Uint4B
+0x4e0 CcFastReadNoWait : Uint4B
+0x4e4 CcFastReadWait : Uint4B
+0x4e8 CcFastReadNotPossible : Uint4B
+0x4ec CcCopyReadNoWait : Uint4B
+0x4f0 CcCopyReadWait : Uint4B
+0x4f4 CcCopyReadNoWaitMiss : Uint4B
+0x4f8 KeAlignmentFixupCount : Uint4B
+0x4fc KeContextSwitches : Uint4B
+0x500 KeDcacheFlushCount : Uint4B
+0x504 KeExceptionDispatchCount : Uint4B
+0x508 KeFirstLevelTbFills : Uint4B
+0x50c KeFloatingEmulationCount : Uint4B
+0x510 KeIcacheFlushCount : Uint4B
+0x514 KeSecondLevelTbFills : Uint4B
+0x518 KeSystemCalls : Uint4B
+0x51c SpareCounter0 : [1] Uint4B
+0x520 PPLookasideList : [16] _PP_LOOKASIDE_LIST
+0x5a0 PPNPagedLookasideList : [32] _PP_LOOKASIDE_LIST
+0x6a0 PPPagedLookasideList : [32] _PP_LOOKASIDE_LIST
+0x7a0 PacketBarrier : Uint4B
+0x7a4 ReverseStall : Uint4B
+0x7a8 IpiFrame : Ptr32 Void
+0x7ac PrcbPad2 : [52] UChar
+0x7e0 CurrentPacket : [3] Ptr32 Void
+0x7ec TargetSet : Uint4B
+0x7f0 WorkerRoutine : Ptr32 void
+0x7f4 IpiFrozen : Uint4B
+0x7f8 PrcbPad3 : [40] UChar
+0x820 RequestSummary : Uint4B
+0x824 SignalDone : Ptr32 _KPRCB
+0x828 PrcbPad4 : [56] UChar
+0x860 DpcListHead : _LIST_ENTRY
+0x868 DpcStack : Ptr32 Void
+0x86c DpcCount : Uint4B
+0x870 DpcQueueDepth : Uint4B
+0x874 DpcRoutineActive : Uint4B
+0x878 DpcInterruptRequested : Uint4B
+0x87c DpcLastCount : Uint4B
+0x880 DpcRequestRate : Uint4B
+0x884 MaximumDpcQueueDepth : Uint4B
+0x888 MinimumDpcRate : Uint4B
+0x88c QuantumEnd : Uint4B
+0x890 PrcbPad5 : [16] UChar
+0x8a0 DpcLock : Uint4B
+0x8a4 PrcbPad6 : [28] UChar
+0x8c0 CallDpc : _KDPC
+0x8e0 ChainedInterruptList : Ptr32 Void
+0x8e4 LookasideIrpFloat : Int4B
+0x8e8 SpareFields0 : [6] Uint4B
+0x900 VendorString : [13] UChar
+0x90d InitialApicId : UChar
+0x90e LogicalProcessorsPerPhysicalProcessor : UChar
+0x910 MHz : Uint4B
+0x914 FeatureBits : Uint4B
+0x918 UpdateSignature : _LARGE_INTEGER
+0x920 NpxSaveArea : _FX_SAVE_AREA
+0xb30 PowerState : _PROCESSOR_POWER_STATE

在0x38的地方是不是看到了我们熟悉的IDT表。

我们在Windbg下dd KiProcessBlock

kd> dd KiProcessorBlock
80553e40 ffdff120 00000000 00000000 00000000
80553e50 00000000 00000000 00000000 00000000
80553e60 00000000 00000000 00000000 00000000
80553e70 00000000 00000000 00000000 00000000

因为是虚拟机里面做的测试,所以只是单核CPU,那么问题来了,如何获得KiProcessBlock?

在Win732位与XP中它是未导出的,那么可以用IDA在ntosknl导出表搜索,找到哪个函数中用了这个变量就可以用这个函数加硬编码的方式进行强行定位,上次看到一篇帖子是如何获得系统未导出的全局变量,是通过遍历未公开的结构体,我就试了试,没想到还成功了,然后就用了这种方法,其实解决多核问题还有好多种方法,不一定局限于哪一种。

下面着重讲解下,如何修改段基址的方法实现HOOK,然后躲过xuetr的检测。

IA-32处理器有三种描述符表:全局描述符表GDT,局部描述符表LDT,中断描述符表IDT。

GDT表是全局的,一个系统中通常只有一个GDT表,供系统中所有程序和任务进行使用。LDT与任务相关,每个任务可以有一个LDT,也可以让多个任务共享一个LDT。

我们用WINDBG观察下GDT

kd> r gdtr
gdtr=8003f000

获得GDT的基地址之后观察下它的内存

是不是和IDT表一样啊,这时候里面的一项叫做段描述符。

这个图就是段描述符的内存结构。有点看不清,将就一下,详情可以参考张银奎老师的《软件调试》。

typedef struct _KGDTENTRY {
USHORT LimitLow;
USHORT BaseLow;
union {
struct {
UCHAR BaseMid;
UCHAR Flags1; // Declare as bytes to avoid alignment
UCHAR Flags2; // Problems.
UCHAR BaseHi;
} Bytes;
struct {
ULONG BaseMid : 8;
ULONG Type : 5;
ULONG Dpl : 2;
ULONG Pres : 1; ULONG LimitHi : 4;
ULONG Sys : 1;
ULONG Reserved_0 : 1;
ULONG Default_Big : 1;
ULONG Granularity : 1;
ULONG BaseHi : 8;
} Bits;
} HighWord;
} KGDTENTRY, *PKGDTENTRY;

段选择子:

 还记得门描述符里的选择子吗?选择子的作用就是选择一个段描述符,相当于索引。

段选择子的T1位代表要索引的段描述符表,T1=0表示全局描述符表,T1=1表示局部描述符表。

段选择子的高13位是描述符索引,即要选择的段描述符在T1所表示的段描述符表中的索引号。因为这里使用的是13位,意味着最多可索引8192个描述符,所以GDT和LDT表的最大表项数都是8192.因为X86CPU最多支持256个中断向量,所以IDT 表的最多表项数是256.

然后我们来看看如何修改段基址来实现IDT HOOK。

要是段内偏移不变,那么必须满足:原始偏移 + newbase = newfuntion

那么newbase  = newfuntion - 原始偏移  


然后把newbase分解了再填到段描述符中就可以了吗?答案是不可以,因为一个段描述符可能会有很多程序在用,如果无故修改段描述符那么就会产生不可预料的错误,那么我们如何修改段基址呢?

我们可以变通一下,因为段描述符表里有很多空的未利用的,我们可以把相应的门描述符里的段选择子改掉,让它去选择一个空的段描述符,我们把原先段选择符内容拷贝过来再修改这个段描述符就可以达到目的了!

#include "ntifs.h"

#define WORD    USHORT
#define DWORD ULONG #define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) \
| ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16)) typedef struct _IDTR{
USHORT IDT_limit;
USHORT IDT_LOWbase;
USHORT IDT_HIGbase;
}IDTR,*PIDTR; typedef struct _IDTENTRY
{
unsigned short LowOffset;
unsigned short selector;
unsigned char retention:5;
unsigned char zero1:3;
unsigned char gate_type:1;
unsigned char zero2:1;
unsigned char interrupt_gate_size:1;
unsigned char zero3:1;
unsigned char zero4:1;
unsigned char DPL:2;
unsigned char P:1;
unsigned short HiOffset;
} IDTENTRY,*PIDTENTRY; typedef struct _KGDTENTRY {
USHORT LimitLow;
USHORT BaseLow;
union {
struct {
UCHAR BaseMid;
UCHAR Flags1; // Declare as bytes to avoid alignment
UCHAR Flags2; // Problems.
UCHAR BaseHi;
} Bytes;
struct {
ULONG BaseMid : 8;
ULONG Type : 5;
ULONG Dpl : 2;
ULONG Pres : 1; ULONG LimitHi : 4;
ULONG Sys : 1;
ULONG Reserved_0 : 1;
ULONG Default_Big : 1;
ULONG Granularity : 1;
ULONG BaseHi : 8;
} Bits;
} HighWord;
} KGDTENTRY, *PKGDTENTRY; //global
USHORT g_FilterJmp[3];
ULONG g_uOrigInterruptFunc; void PageProtectOn()
{
__asm{//恢复内存保护
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
} void PageProtectOff()
{
__asm{//去掉内存保护
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
} USHORT g_u_cs; void __stdcall FilterInterrupt()
{
KdPrint(("%s---%X",(char*)PsGetCurrentProcess()+0x16c,g_u_cs));
} __declspec(naked)
void NewInterrupt3OfOrigBase()
{
__asm{
pushad
pushfd push fs
push 0x30
pop fs call FilterInterrupt pop fs popfd
popad jmp g_uOrigInterruptFunc
}
} __declspec(naked)
void NewInterrupt3()
{
__asm{
mov g_u_cs,cs
jmp fword ptr[g_FilterJmp]
}
} ULONG GetInterruptFuncAddress(ULONG InterruptIndex)
{
IDTR idtr;
IDTENTRY *pIdtEntry; __asm SIDT idtr; pIdtEntry = (IDTENTRY *)MAKELONG(idtr.IDT_LOWbase,idtr.IDT_HIGbase); return MAKELONG(pIdtEntry[InterruptIndex].LowOffset,pIdtEntry[InterruptIndex].HiOffset);
} ULONG GetNewBase(ULONG NewInterruptFunc,ULONG OrigInterruptOffset)
{
return (NewInterruptFunc - OrigInterruptOffset);
} VOID SetInterrupt(ULONG InterruptIndex,ULONG uNewBase,BOOLEAN bIsNew)
{
ULONG u_fnKeSetTimeIncrement;
UNICODE_STRING usFuncName;
ULONG u_index;
ULONG *u_KiProcessorBlock; IDTENTRY *pIdtEntry;
PKGDTENTRY pGdt; RtlInitUnicodeString(&usFuncName,L"KeSetTimeIncrement"); u_fnKeSetTimeIncrement = (ULONG)MmGetSystemRoutineAddress(&usFuncName);
if (!MmIsAddressValid((PVOID)u_fnKeSetTimeIncrement))
{
return;
} u_KiProcessorBlock = *(ULONG**)(u_fnKeSetTimeIncrement + 44); u_index = 0;
while (u_KiProcessorBlock[u_index])
{
pIdtEntry = *(IDTENTRY**)(u_KiProcessorBlock[u_index] - 0xE8);
pGdt = *(PKGDTENTRY*)(u_KiProcessorBlock[u_index] - 0xE4); PageProtectOff(); if (bIsNew)
{
pIdtEntry[InterruptIndex].selector = 0xA8; //10101 000 //低1 2位 RPL用于检测权限 低 3 位用于选择 GDT 或者 LDT 高五位用于代表表中索引号 这个索引是21
RtlCopyMemory(&pGdt[21],&pGdt[1],sizeof(KGDTENTRY));
pGdt[21].BaseLow = (USHORT)(uNewBase&0xffff);
pGdt[21].HighWord.Bytes.BaseMid = (UCHAR)((uNewBase>>16)&0xff);
pGdt[21].HighWord.Bytes.BaseHi = (UCHAR)(uNewBase>>24); //把原来的段描述符拷过来 修改段描述符的基址
}else{
pIdtEntry[InterruptIndex].selector = 0x8;
memset(&pGdt[21],0,sizeof(KGDTENTRY));
} PageProtectOn(); u_index++;
}
} VOID HookInterruptFunc(ULONG InterruptIndex,ULONG NewInterruptFunc)
{
ULONG uNewBase; g_uOrigInterruptFunc = GetInterruptFuncAddress(InterruptIndex);
uNewBase = NewInterruptFunc - g_uOrigInterruptFunc; //段基地址 + g_uOrigInterruptFunc = NewInterruptFunc *(ULONG*)g_FilterJmp = (ULONG)NewInterrupt3OfOrigBase;
g_FilterJmp[2] = 0x8; SetInterrupt(InterruptIndex,uNewBase,TRUE);
} void UnHookInterruptFunc(ULONG InterruptIndex)
{
SetInterrupt(InterruptIndex,0,FALSE);
} VOID MyUnload(PDRIVER_OBJECT pDriverObject)
{
UnHookInterruptFunc(3);
} NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING Reg_Path)
{
HookInterruptFunc(3,(ULONG)NewInterrupt3);
pDriverObject->DriverUnload = MyUnload;
return STATUS_SUCCESS;
}

IDT系统中断描述表以及绕过Xurtr检测的HOOK姿势的更多相关文章

  1. 安装Windows11操作系统(不需要绕过TPM检测脚本等) - 初学者系列 - 学习者系列文章

    Windows11操作系统是去年微软公司的最新力作.对于该操作系统的安装,网上有很多的教程了.这次主要写的是不需要绕过TPM检测操作安装Windows11操作系统. 1.        制作启动U盘: ...

  2. 在iOS应用程序中使用Frida绕过越狱检测

           阿里聚安全在之前的三篇博客中介绍了利用Frida攻击Android应用程序,整个过程仿佛让开发者开启上帝视角,在本篇博客中,我们将会介绍在iOS应用程序中使用Frida绕过越狱检测.即使 ...

  3. java实现 HTTP/HTTPS请求绕过证书检测代码实现

    java实现 HTTP/HTTPS请求绕过证书检测代码实现 1.开发需求 需要实现在服务端发起HTTP/HTTPS请求,访问其他程序资源. 2.URLConnection和HTTPClient的比较 ...

  4. Java实现 HTTP/HTTPS请求绕过证书检测

    java实现 HTTP/HTTPS请求绕过证书检测 一.Java实现免证书访问Https请求 创建证书管理器类 import java.security.cert.CertificateExcepti ...

  5. JavaScript 小实例 - 表单输入内容检测,对页面的增删改

    JavaScript 小实例 - 表单输入内容检测,对页面的增删改 效果体验地址:https://xpwi.github.io/js/JavaScript01/jsForm.html 功能: 1.向页 ...

  6. 尝试使用Osg共享渲染描述表(HGLRC)实现多线程编译显示列表--总结

    在realize()前打开预编译选项指令: osg::DisplaySettings::instance()->setCompileContextsHint(true);    mpr_osgv ...

  7. 网络爬虫之使用pyppeteer替代selenium完美绕过webdriver检测

    1引言 曾经使用模拟浏览器操作(selenium + webdriver)来写爬虫,但是稍微有点反爬的网站都会对selenium和webdriver进行识别,网站只需要在前端js添加一下判断脚本,很容 ...

  8. CVPR2019论文解读:单眼提升2D检测到6D姿势和度量形状

    CVPR2019论文解读:单眼提升2D检测到6D姿势和度量形状 ROI-10D: Monocular Lifting of 2D Detection to 6D Pose and Metric Sha ...

  9. 在SQL注入中利用MySQL隐形的类型转换绕过WAF检测

    web应用一般采用基于表单的身份验证方式(页面雏形如下图所示),处理逻辑就是将表单中提交的用户名和密码传递到后台数据库去查询,并根据查询结果判断是否通过身份验证.对于LAMP架构的web应用而言,处理 ...

随机推荐

  1. [noi38]游戏

    用线段数维护一段区间内的两个信息:1.需要多少经验就可以让有一个人升级,2.等级和.单点修改直接暴力做就可以,区间修改考虑如果这个区间不会产生升级就不递归下去而是打上懒标记. 考虑这个算法的时间复杂度 ...

  2. 填坑总结:python内存泄漏排查小技巧

    摘要:最近服务遇到了内存泄漏问题,运维同学紧急呼叫解决,于是在解决问题之余也系统记录了下内存泄漏问题的常见解决思路. 本文分享自华为云社区<python内存泄漏排查小技巧>,作者:luti ...

  3. Stream流的使用

    创建流 创建流的方式很多,从jdk8起,很多类中添加了一些方法来创建相应的流,比如:BufferedReader类的lines()方法:Pattern类的splitAsStream方法.但是开发中使用 ...

  4. 「日志」Navicat统计的行数竟然和表实际行数不一致

    背景 近期为了保障线上数据库的稳定性,我决定针对一些大表的历史数据有计划地进行备份迁移,但是呢,发现一个奇特的现象,Navicat统计行数和表自身count统计数竟然不一致!?0.0 Navicat ...

  5. Codeforces 251D - Two Sets(异或方程组)

    题面传送门 题意: 你有一个可重集 \(S=\{a_1,a_2,\dots,a_n\}\),你要把它划分成两个可重集 \(S_1,S_2\) 使得 \(S\) 中每个元素都恰好属于 \(S_1\) 与 ...

  6. Python基础笔记1

    这篇笔记来自廖雪峰的Python教程. 一.Python基础 Python使用缩进来组织代码块,务必遵守约定俗成的习惯,坚持使用4个空格的缩进. 在文本编辑器中,需要设置把Tab自动转换为4个空格,确 ...

  7. Vue.js知识点总结

    1. Vue.js简介 1.1 Vue.js简介 1.2 创建一个vue实例 2. Vue.js基础 2.1 模板语法 2.2 环境搭建 2.3 生命周期钩子

  8. 14-Reverse Integer

    思路: 先判定符号,整型范围[-2^32,2^32] 取余除10操作,依次进行,越界返回0 Reverse digits of an integer. Example1: x = 123, retur ...

  9. Redis篇:单线程I/O模型

    关注公众号,一起交流,微信搜一搜: 潜行前行 redis 单线程 I/O 多路复用模型 纯内存访问,所有数据都在内存中,所有的运算都是内存级别的运算,内存响应时间的时间为纳秒级别.因此 redis 进 ...

  10. 巩固javaweb第十二天

    巩固内容: HTML 图像- 图像标签( <img>)和源属性(Src) 在 HTML 中,图像由<img> 标签定义. <img> 是空标签,意思是说,它只包含属 ...