利用钩子技术控制进程创建

做一个检测钩子程序的工具 (本文最早发表在《电脑编程技巧与维护》杂志) 输入您的搜索字词 提交搜索表单 一、引言 Windows系统是建立在事件驱动的机制上的,每一个事件就是一个消息,每个运行中的程序,也就是所谓的 阅读详情

原英文地址:
http://www.codeproject.com/KB/system/soviet_protector.aspx


Download source files - 10.8 Kb
Download demo project - 12.1 Kb

一、 简介

   最近,我了解到一个叫做Sanctuary的相当有趣的安全产品。它能够阻止任何程序的运行-这些程序没有显示在软件列表中-该表中的程序被允许在一个 特定的机器上运行。结果,PC用户得到保护而免于各种插件间谍软件、蠕虫和特洛伊木马的侵袭-就算能够进入他/她的计算机,它们也没有机会执行,并因此没 有机会对该机器造成任何损害。当然,我觉得这个特征相当有趣;并且,在稍作思考以后,我就有了一个自己的实现。因此,本文将描述如何通过钩住本机API的 方式来实现监控一个进程的创建并在系统级上对之进行控制。

  本文大胆假设,目标进程是以一种用户模式(外壳函数, CreateProcess(),用一系列的本机API调用的手工的进程创建,等等)创建的。尽管从理论上,一个进程能够以内核方式启动;不过从实际来 看,如此的可能性是可以忽略不计的,因此我们不必为此担心。为什么?请逻辑地思考一下-为了以内核方式启动一个进程,用户必须装载一个驱动程序,该驱动程 序反过来首先要暗示某种用户模式代码的执行。因此,为了防止未被授权程序的执行,我们可以安全地在系统级上以用户模式限制我们自己控制的进程的创建。

  二、 定义策略

  首先让我们明确,之所以这样做的目的是为了在系统级上监视和控制进程创建。

  进程创建是一件相当复杂的事情-它包含相当多的工作(如果你不相信我,可以反汇编CreateProcess(),这样你就会亲眼看到这点)。为了启动一个进程,可以使用下列步骤:

  1.可执行文件必须被以FILE_EXECUTE存取方式打开。

  2.可执行映像必须被装载进RAM。

  3.必须建立进程执行对象(EPROCESS,KPROCESS和PEB结构)。

  4.必须为新建进程分配地址空间。

  5.必须建立进程的主线程的线程执行对象(ETHREAD,KTHREAD和TEBstructures)。

  6.必须为主线程分配堆栈。

  7.必须建立进程的主线程的执行上下文。

  8.必须通知Win32子系统有关该新进程的创建情况。

   为确保这些步骤中的任何一步的成功,所有其前面的步骤必须是成功执行的(你不能够在没有一个可执行区句柄的情况下建立一个可执行进程对象;没有文件句柄 的情况下你无法映射一个可执行区,等等)。因此,如果我们决定退出任何这些步骤,所有后面的步骤也会失败,以至于整个进程创建会失败。上面所有的步骤都可 以通过调用某些本机API函数的方式来实现,这是可以理解的。因此,为了监视和控制进程创建,我们所有要做的就是钩住这些API函数-它们无法旁路掉要创 建一新进程所要执行的代码。

  我们应该钩住哪些本机API函数呢?尽管NtCreateProcess()似乎是问题的最显然的答案,但 是,这个答案是错误的-有可能不需要调用这个函数也可以创建一个新的进程。例如,CreateProcess()可以在不调用 NtCreateProcess()的情况下创建与进程相关的内核模式结构.因此,这样以来钩住NtCreateProcess()对我们毫无帮助。

   为了监视进程的创建,我们必须钩住NtCreateFile()和NtOpenFile(),或者NtCreateSection()之中的一个,-不 经调用这些API是绝对无法运行任何可执行文件的。如果我们决定监视对NtCreateFile()和NtOpenFile()的调用,那么我们必须区别 开进程创建和常规的文件IO操作。这项任务并不总是那么容易。例如,如果一些可执行文件正在被以FILE_ALL_ACCESS存取方式打开,我们该怎么 办?这仅是一个IO操作还是一个进程创建的一部分?在这点上,是很难判断的-我们需要了解调用线程下一步要干什么。因此,钩住NtCreateFile ()和NtOpenFile()可能不是最好的选择。

  钩住NtCreateSection()是更为合理的-如果我们在发生把可执行文 件映射为映像(SEC_IMAGE 属性)的请求发生时拦截对NtCreateSection()的调用,结合允许执行页面保护的请求;那么,我们可以确信该进程将要被启动。在这一点上,我 们是能够作出决定,如果我们不想该进程被创建,可以让NtCreateSection()返回STATUS_ACCESS_DENIED。因此,为了完全 控制目标机器上的进程创建,所有我们要做的是在系统级上钩住NtCreateSection()。

  象来自于ntdll.dll中的任何 其它代理一样,NtCreateSection()用服务索引加载EAX,使EDX指向函数参数,并且把执行权传递到KiDispatchService ()内核模式例程(这是通过Windows NT/2000中的INT 0x2E指令或者Windows XP下的SYSENTER指令实现的)。在校验完函数参数之后,KiDispatchService()把执行权传递到服务的实际实现部分-它的地址可用 于服务描述表(指向这个表的指针由ntoskrnl.exe作为KeServiceDescriptorTable变量所输出,所以它对于内核模式驱动程 序是可用的)中。服务描述表通过下列结构所描述:
 

  struct SYS_SERVICE_TABLE {

  void **ServiceTable;

  unsigned long CounterTable;

  unsigned long ServiceLimit;

  void **ArgumentsTable;

  };

 

   这个结构中的ServiceTable字段指向一个数组-它拥有所有实现系统服务的函数的地址。因此,为了在系统级上钩住任何本机API函数,所有我们 必须做的是把我们的代理函数的地址写入被KeServiceDescriptorTable的ServiceTable字段所指向的数组的第i个入口(i 是服务索引)。

  至此,看起来我们已了解了在系统级上监视和控制进程创建的一切。现在让我们开始实际的工作。

三、 控制进程创建

  我们的解决方案由一个内核模式驱动程序和一个用户模式应用程序组成。为了开始监视进程创建,我们的应用程序要把服务索引(相应于NtCreateSection())以及交换缓冲区的地址传递到我们的驱动程序。这是由下列代码所完成的:

//打开设备
device=CreateFile("//./PROTECTOR",GENERIC_READ|GENERIC_WRITE,
0,0,OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM,0);
//得到NtCreateSection的索引并把它连同输出缓冲区的地址传递给设备
DWORD * addr=(DWORD *)
(1+(DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtCreateSection"));
ZeroMemory(outputbuff,256);
controlbuff[0]=addr[0];
controlbuff[1]=(DWORD)&outputbuff[0];
DeviceIoControl(device,1000,controlbuff,256,controlbuff,256,&dw,0);

   此代码是显然的-唯一需要注意的是我们得到服务索引的方式。所有来自于ntdll.dll的代理都从一行代码MOV EAX,ServiceIndex开始-它可以适用于任何版本和风味的Windows NT。这是一条5字节长的指令,以MOV EAX操作码作第一字节,服务索引作为留下的4字节。因此,为了得到相应于一些特别的本机API函数的服务索引,所有你要做的是从该地址读取4个字节,- 位于从这个代理开始1字节距离的地方。

  现在让我们看一下我们的驱动程序做什么,当它收到来自我们的应用程序的IOCTL时:

NTSTATUS DrvDispatch(IN PDEVICE_OBJECT device,IN PIRP Irp)
{
 UCHAR*buff=0; ULONG a,base;
 PIO_STACK_LOCATION loc=IoGetCurrentIrpStackLocation(Irp);
 if(loc->Parameters.DeviceIoControl.IoControlCode==1000)
 {
  buff=(UCHAR*)Irp->AssociatedIrp.SystemBuffer;
  //钩住服务调度表
  memmove(&Index,buff,4);
  a=4*Index+(ULONG)KeServiceDescriptorTable->ServiceTable;
  base=(ULONG)MmMapIoSpace(MmGetPhysicalAddress((void*)a),4,0);
  a=(ULONG)&Proxy;
  _asm
  {
   mov eax,base
   mov ebx,dword ptr[eax]
   mov RealCallee,ebx
   mov ebx,a
   mov dword ptr[eax],ebx
  }
  MmUnmapIoSpace(base,4);
  memmove(&a,&buff[4],4);
  output=(char*)MmMapIoSpace(MmGetPhysicalAddress((void*)a),256,0);
 }
 Irp->IoStatus.Status=0;
 IoCompleteRequest(Irp,IO_NO_INCREMENT);
 return 0;
}

   正如你所见,这里没有什么特别的-我们只是通过MmMapIoSpace()来把交换缓冲区映射到内核中,另外把我们的代理函数的地址写到服务表(当 然,我们这是在把实际的服务执行的地址保存到全局变量RealCallee以后这样做的)。为了改写服务表的适当入口,我们通过MmMapIoSpace ()来映射目标地址。为什么我们要这样做?不管怎么说,我们已经可以存取服务表了,不是吗?问题是,服务表可能驻留在一段只读内存中。因此,我们必须检查 一下是否我们有对目标空间写的权限,而如果我们没有这个权限,那么在改写服务表之前,我们必须改变页面保护。你不认为这样以来工作太多了吗?因此,我们仅 用MmMapIoSpace()来映射我们的目标地址,这样以来,我们就不必担心任何的页面保护问题了-从现在开始,我们假定已有到目标页面写的权限了。 现在让我们看一下我们的代理函数:

//这个函数用来确定是否我们应该允许NtCreateSection()调用成功
ULONG __stdcall check(PULONG arg)
{
 HANDLE hand=0;PFILE_OBJECT file=0;
 POBJECT_HANDLE_INFORMATION info;ULONG a;char*buff;
 ANSI_STRING str; LARGE_INTEGER li;li.QuadPart=-10000;
 //检查标志。如果所要求的存取方式不是PAGE_EXECUTE,
 //这并不要紧
 if((arg[4]&0xf0)==0)return 1;
 if((arg[5]&0x01000000)==0)return 1;
 //经由文件句柄得到文件名
 hand=(HANDLE)arg[6];
 ObReferenceObjectByHandle(hand,0,0,KernelMode,&file,&info);
 if(!file)return 1;
  RtlUnicodeStringToAnsiString(&str,&file->FileName,1);
  a=str.Length;buff=str.Buffer;
  while(1)
  {
   if(buff[a]=='.'){a++;break;}
   a--;
  }
  ObDereferenceObject(file);
  //如果它是不可执行的,这也不要紧
  //返回1
  if(_stricmp(&buff[a],"exe")){RtlFreeAnsiString(&str);return 1;}
   //现在,我们要询问用户的选择。
   //把文件名写入缓冲区,并等待直到用户显示响应
   //(第一个DWORD为1意味着我们可以继续)
   //同步存取该缓冲区
   KeWaitForSingleObject(&event,Executive,KernelMode,0,0);
   //把缓冲区的前两个DWORD置为0,
   //把字符串复制到该缓冲区中,并循环下去,直到用户把每一个
   //DWORD置为1.
   //第二个DWORD的值指明用户的响应
  strcpy(&output[8],buff);
  RtlFreeAnsiString(&str);
  a=1;
  memmove(&output[0],&a,4);
  while(1)
  {
   KeDelayExecutionThread(KernelMode,0,&li);
   memmove(&a,&output[0],4);
   if(!a)break;
  }
  memmove(&a,&output[4],4);
  KeSetEvent(&event,0,0);
  return a;
 }
 //仅保存执行上下文并调用check()
 _declspec(naked) Proxy()
 {
  _asm{
   //保存执行上下文并调用check()
   //-后面的依赖于check()所返回的值
   // 如果返回值是1,继续实际的调用。
   //否则,返回STATUS_ACCESS_DENIED
   pushfd
   pushad
   mov ebx,esp
   add ebx,40
   push ebx
   call check
   cmp eax,1
   jne block
   //继续实际的调用
   popad
   popfd
   jmp RealCallee
   //返回STATUS_ACCESS_DENIED
   block:popad
   mov ebx, dword ptr[esp+8]
   mov dword ptr[ebx],0
   mov eax,0xC0000022L
   popfd
   ret 32
  }
 }

   Proxy()保存寄存器和标志,把一个指向服务参数的指针压入栈中并调用check()。其它的依赖于check()所返回的值。如果check() 返回TRUE(也就是,我们想要继续请求),那么,Proxy()将恢复寄存器和标志,并且把控制权交给服务实现部分。否则,Proxy()将把 STATUS_ACCESS_DENIED写入EAX,恢复ESP并返回-从调用者的观点来看,这就象对NtCreateSection()的调用失败一 样-以错误状态STATUS_ACCESS_DENIED返回。
 check()函数是怎样做出决定的?一旦它收到一个指向服务参数的指针参数, 它就可以检查这些参数。首先,它检查标志和属性-如果有一部分没有被要求作为一个可执行映像映射,或如果要求的页面保护不允许执行,那么我们可以确定 NtCreateSection()调用与进程创建毫无关系。在这种情况下,check()直接返回TRUE。否则,它将检查该潜在文件的扩展-毕竟, SEC_IMAGE属性和允许执行的页面保护可能被要求来映射某个DLL文件。如果该潜在文件不是一个.exe文件,那么,check()将返回 TRUE。否则,它给用户模式代码一个作出决定的机会。因此,它仅把文件名和路径写到交换缓冲区,并且对它循环查询,直到它得到响应为止。

  在打开我们的驱动程序前,我们的应用程序创建一个运行下面函数的线程:

void thread()
{
 DWORD a,x; char msgbuff[512];
 while(1)
 {
  memmove(&a,&outputbuff[0],4);
  //如果什么也没有,Sleep() 10毫秒并再检查
  if(!a){Sleep(10);continue;}
  //看起来象我们的权限被询问。
  //如果被怀疑的文件已经存在于空白列表中,
  // 则给出一个积极的响应。
  char*name=(char*)&outputbuff[8];
  for(x=0;x<stringcount;x++)
  {
   if(!stricmp(name,strings[x])){a=1;goto skip;}
  }
  //要求用户允许运行该程序
  strcpy(msgbuff, "Do you want to run ");
  strcat(msgbuff,&outputbuff[8]);
  //如果用户的答复是积极的,那么把这个程序添加到空白列表中
  if(IDYES==MessageBox(0, msgbuff,"WARNING",MB_YESNO|MB_ICONQUESTION|0x00200000L))
   {a=1; strings[stringcount]=_strdup(name);stringcount++;}
  else a=0;
  // 把响应写入缓冲区中,而由驱动程序之后取回它
  skip:memmove(&outputbuff[4],&a,4);
  //告诉驱动程序继续
  a=0;
  memmove(&outputbuff[0],&a,4);
 }
}

   这段代码是显然的-我们的线程每10毫秒查询交换缓冲区。如果它发现我们的驱动程序已经把它的请求寄到了该缓冲区中,它就检查被允许在本机上运行的程序 列表中的文件的文件名和路径。如果发现匹配,它直接给出一个OK响应。否则,它显示一个消息窗口,询问用户是否允许有问题的程序执行。如果响应是积极的, 我们就把有问题的程序添加到允许在本机上运行的软件列表中。最后,我们把用户响应写入缓冲区,也就是说,把它传递到我们的驱动程序。因此,该用户就能完全 控制它的PC上的进程的创建-只要我们的程序运行,在没有用户所给予权限的情况下,绝对没有办法来启动该PC上的任何进程。

  正如你所 见,我们让内核方式代码等待用户反应。这是否是一种聪明的举措呢?为了回答这个问题,你必须问你自己你是否正在堵住任何关键的系统资源-一切都依赖于具体 的情况。在我们的情况下,一切发生在IRQLPASSIVE_LEVEL级上,并没有包含对IRPs的处理,并且必须等待用户响应的线程并不十分重要。因 此,在我们的情况下,一切工作正常。然而,本例仅为演示之目的而编写。为了实际地使用它,以一个自动启动的服务的方式来重写我们的应用程序是很重要的。在 这种情况下,我建议我们解除LocalSystem帐户,并且,在NtCreateSection()被用LocalSystem帐户特权在一个线程的上 下文中调用的情况下,可以继续实际的服务实现而不施行任何检查-不管怎么说,LocalSystem帐户仅运行那些在注册表中指定的可执行程序。因此,这 样的一种解除不会是与我们的安全相妥协的。

   四、 结论

  最后,我必须指出,钩 住本机API很明显是现已存在的最强有力的编程技术之一。本文通过一个例子向你展示通过钩住本机API可以实现的能力-正如你所见,我们已设法防止未被授 权的程序的执行-这可以通过钩住单一的本机API函数来实现。你可以进一步扩展这个方法,并且获得对硬件设备、文件IO操作、网络流量等等的完全控制。然 而,我们现在的解决方案并不是准备为内核模式API调用者所用-一旦内核模式代码被允许直接调用ntoskrnl.exe的输出,则这些调用就不需要经由 系统服务发送者进行了。

  本文源码在运行Windows XP SP2的若干机器上成功地测试过。尽管我还没在任何另外的环境下面测试它,我相信它应该到处工作正常-不管怎么说,它从未使用任何系统特定的结构。为了运 行这个示例,所有你要做的是放置protector.exe和protector.sys到相同的目录下,并且运行protector.exe。直到 protector.exe的应用程序窗口被关闭为止;否则,每次你都会被提示你试图运行任何可执行程序。
HOOK钩子技术5 SSDT Inline Hook r3下的inline Hook 在r0下的实现。 过ssdt保护检测。 阅读详情

相关推荐

基于eBPF与cgroup实现进程级网络路由控制:原理、架构与实战

在网络编程与系统运维中,网络路由是决定数据包传输路径的核心机制。传统路由策略通常基于IP地址或端口,难以实现应用级别的精细化控制。其原理是通过路由表与策略规则,将数据包导向不同网络接口。这项技术的价值在于能够实现安全隔离、流量调度与资源优化,广泛应用于混合云、安全合规与开发测试等场景。本文聚焦于利用eBPF和cgroup技术,实现基于进程粒度的网络路由控制,通过将特定进程的流量定向至指定隧道(如WireGuard),解决了传统“一刀切”代理的局限,为网络精细化管理和安全加固提供了系统级解决方案。

weixin_32822843的博客 258

WIN32汇编: 24.WINDOWS钩子函数

第二十四课 WINDOWS钩子函数本课中我们将要学习WINDOWS钩子函数的使用方法。WINDOWS钩子函数的功能非常强大,有了它您可以探测其它进程并且改变其它进程的行为。 理论:WINDOWS的钩子函数可以认为是WINDOWS的主要特性之一。利用它们,您可以捕捉您自己进程或其它进程发生的事件。通过“钩挂”,您可以给WINDOWS一个处理或过滤事件的回调函数,该函数也叫做“钩子函数”,

GodDragon的专栏 2599

windows系统之Hook实例DIPS

windows系统下Hook 文章示例来至《Windows核心编程》第22章实例Desktop Item Position Saver工具 原理说明 windows下使用Hook实现对远程进程注入DLL技术,通过共用操作系统下同一份DLL,实现对目标进程的特定消息传输,甚至达到控制的技术。 首先需要了解的实现DLL注入实现Hook进程的方式 HHOOK hHook = SetWindowsHook...

WeinKee的博客 556

Window Hook 技术详解

Hook简介 微软的MSDN中,对Hook的解释为: A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain

cx1990820的专栏 8692

钩子编程(HOOK) 安装进程内鼠标钩子 (0)

[钩子编程(HOOK) 安装进程内鼠标钩子]钩子函数,Windows的钩子函数可以认为是Windows的主要特性之一。利用它们,您可以捕捉您自己进程或其它进程发生的事件。通过“钩挂”,您可以给Windows一个处理或过滤事件的回调函数,该函数也叫做“钩子函数”,当每次发生您感兴趣的事件时,Windows都将调用该函数。

机器学习 4万+

Windows底层控制权攻防:从驱动钩子到进程守护的技术博弈

在Windows系统安全领域,驱动级钩子(Driver-level Hook)和进程守护(Process Guarding)是实现软件强控制的核心技术。驱动运行于内核态(Ring 0),通过拦截系统调用和硬件输入消息,可实现对键盘、鼠标等设备的全局过滤。用户态进程则通过API钩子、窗口管理和网络心跳机制,构建多层防御体系。这些技术广泛应用于教学管理、企业监控等场景,确保终端行为的可控性。本文以极域电子教室为例,深入剖析其通过驱动注入、消息过滤和私有协议通信实现的立体化控制原理,并探讨在类似环境下进行技术分析

weixin_28607671的博客 267

利用钩子技术控制进程创建(附源代码)

一、 简介   最近,我了解到一个叫做Sanctuary的相当有趣的安全产品。它能够阻止任何程序的运行-这些程序没有显示在软件列表中-该表中的程序被允许在一个特定的机器上运行。结果,PC用户得到保护而免于各种插件间谍软件、蠕虫和特洛伊木马的侵袭-就算能够进入他/她的计算机,它们也没有机会执行,并因此没有机会对该机器造成任何损害。当然,我觉得这个特征相当有趣;并且,在稍作思考以后,我就有了一个

swanabin的专栏 1481

C++高级系统编程:双进程防杀与键盘钩子实战

在系统级程序开发中,实现高存活率和键盘输入控制是一项复杂而关键的任务。本章聚焦于双进程防杀机制与全局键盘钩子技术,探讨其在对抗程序终止与输入拦截中的核心作用。“双进程机制”通过主进程与守护子进程的协同工作,确保关键功能在被强制终止后能迅速恢复运行。而“键盘钩子”则利用Windows消息拦截机制,实现对特定按键(如 Ctrl+Alt+Del)的屏蔽控制。二者结合,构建出具备高鲁棒性与控制力的系统级程序框架,为后续章节的实现奠定理论与架构基础。本章从钩子回调函数的设计出发,深入解析了。

weixin_42620563的博客 743

PsSetCreateProcessNotifyRoutineEx 防多开

这个函数的功能是设置一个回调钩子,该钩子会在进程创建和进程销毁时被调用,钩子由用户提供 API格式 NTSTATUS PsSetCreateProcessNotifyRoutineEx( PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, BOOLEAN Remove ); NotifyRoutine 是IN,传入用户指定的回调钩子 Remove 是IN ,创建回调钩子时传FALSE,创建了肯定需要在

sanqiuai的博客 1171

python文件转为可执行文件(.exe)的详细步骤

本文详细介绍了如何从零开始把python文件打包成为可执行文件(.exe)。

m0_67093160的博客 3万+

2024年五大革新图像分割模型解析:架构创新与实战应用指南

本文深入解析2024年五大革新图像分割模型,包括SAM2、Mask2Former、DINOv2和Swin Transformer等,探讨其架构创新与实战应用。从多尺度处理、提示交互到自监督学习,这些模型在精度提升、效率优化和交互增强方面取得突破,广泛应用于医疗影像、工业质检和智慧城市等领域。

hadoop5ranger的博客 895

ETW 技术全解析

ETW(Event Tracing for Windows)是Windows内置的高效事件跟踪系统,由提供者、控制器、会话和使用者四大组件构成。它支持动态监控系统事件,无需重启即可收集内核和应用级数据。通过Logman工具可快速演示进程创建监控:启动内核会话捕获进程事件,使用WPA分析日志。ETW架构清晰,提供者生成事件,控制器管理会话,使用者处理数据,适用于诊断调试、性能分析等场景,是Windows平台强大的诊断基础设施。

网络安全相关文章分享 1451

大华/华睿工业相机(摄像头)链接电脑,并用OpenCV打开

睿科是大华旗下机器视觉子公司,现在工业相机一般是华睿在产,要下载驱动程序,要到华睿官网哪里下载。由于我的电脑没有摄像头,所以摄像头序列化为0,电脑的第一个摄像头默认为0,序列号是0,1,2,3……,你是什么接口连接的就选择什么, 建议直接全选。我这里选择第二个,下载后点击安装,的你要点击识别右侧设置,设置。这个过程很简单,故不赘述。打开你的设备会在右边显示。

qq_48050838的博客 1万+

Windows HOOK钩子技术

Windows HooK钩子技术是指基于Windows中窗口的程序的消息处理里机制,对系统或者进程中的消息进行截获和处理,并将截获和处理的消息在重新处理和发送,使其可以实现不同的功能。 钩子技术分为系统钩子技术和线程钩子技术 系统钩子:是用于监视真个系统中的消息的钩子技术,因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 中。做好之后使用其他程序将钩子挂载到系统的进程中。 线程钩子:指的是对指定线程进行监视。 钩子原理 在使用钩子技术的时候,WINDOWS会先在内存中创

qq_43812868的博客 2869

ONFI5.0命令字实战指南:从基础操作到高级应用

本文深入解析ONFI5.0协议的核心——命令字,提供从基础操作到高级应用的实战指南。详细拆解了NAND闪存的复位、读状态、数据读取(00-30)、页编程(80-10)和块擦除(60-D0)等关键命令周期,并探讨了多平面操作与缓存操作等高级性能优化技巧,帮助开发者高效、稳定地与NAND芯片进行底层交互。

vscode5coder的博客 463

DeepL Pro免费使用技巧与API调用优化方案

机器翻译技术通过神经网络模型实现跨语言语义转换,其核心价值在于保持专业术语准确性的同时实现自然语言流畅度。现代翻译工具如DeepL采用注意力机制等深度学习技术,在医疗、法律等专业领域展现出接近人工翻译的质量。针对API调用场景,开发者可通过请求模拟实现小批量自动化处理,而文档预处理和术语库优化则能显著提升免费额度使用效率。本文重点解析如何通过字符计算优化、临时接口调用等工程实践方法,在合规前提下最大化利用免费翻译资源。

weixin_29038303的博客 266
上一篇: unhook——如何得到原始派遣函数的地址
下一篇: Windows软件防火墙实现技术简述
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值