汇编Ring 3下实现 HOOK API

【文章标题】汇编ring3下实现HOOK API
【文章作者】nohacks(非安全,hacker0058)
【作者主页】hacker0058.ys168.com
【文章出处】看雪论坛(bbs.pediy.com)

==================[ 汇编ring3下实现HOOK API ]=====================

                                                 Author: nohacks
                                                 Emil: kker.cn@163.com
                                                 Version: 1.1
                                                 Date: 7.18.2006
                                                 

=====[ 1. 内容 ]=============================================

1. 内容
2. 介绍
  2.1 什么叫Hook API?
  2.2 API Hook的应用介绍
  2.3 API Hook的原则
3. 挂钩方法
  3.1 改写IAT导入表法
  3.2 改写内存地址JMP法
4. 汇编实现
  4.1. 代码 
  4.2. 分析
5. 结束语


=====[ 2. 介绍 ]================================================

   这篇文章是有关在OS Windows下挂钩API函数的方法。所有例子都在基于NT技术的Windows版本NT4.0

及以上有效(Windows NT 4.0, Windows 2000, Windows XP)。可能在其它Windows系统也会有效。

   你应该比较熟悉Windows下的进程、汇编器、和一些API函数,才能明白这篇文章里的内容。


=====[2.1 什么叫Hook API?]=================================
   
   所谓Hook就是钩子的意思,而API是指Windows开放给程序员的编程接口,使得在用户级别下可

以对操作系统进行控制,也就是一般的应用程序都需要调用API来完成某些功能,Hook API的意思

就是在这些应用程序调用真正的系统API前可以先被截获,从而进行一些处理再调用真正的API来完

成功能。

 ====[2.2 API Hook的应用介绍]=================================
    
   API Hook技术应用广泛,常用于屏幕取词,网络防火墙,病毒木马,加壳软件,串口红外通讯,游戏外

挂,internet通信等领域API HOOK的中文意思就是钩住API,对API进行预处理,先执行我们的函数,例

如我们用API Hook技术挂接ExitWindowsEx API函数,使关机失效,挂接ZwOpenProcess函数(如:老王的

EncryptPE),隐藏进程等等......

====[2.3 API Hook的原则]=====================================
   
   HOOK API有一个原则,这个原则就是:被HOOK的API的原有功能不能受到任何影响。就象医生救人,

如果把病人身体里的病毒杀死了,病人也死了,那么这个“救人”就没有任何意义了。如果你HOOK API

之后,你的目的达到了,但API的原有功能失效了,这样不是HOOK,而是REPLACE,操作系统的正常功能

就会受到影响,甚至会崩溃。

====[ 3. 挂钩方法 ]==============================================

总的来说,常用的挂钩API方法有以下两种:

3.1 改写IAT导入表法

   修改可执行文件的IAT表(即输入表)因为在该表中记录了所有调用API的函数地址,则只需将这些

地址改为自己函数的地址即可,但是这样有一个局限,因为有的程序会加壳,这样会隐藏真实的IAT表

,从而使该方法失效。

3.2 改写内存地址JMP法

    直接跳转,改变API函数的入口或出口的几个字节,使程序跳转到自己的函数,该方法不受程序加壳

的限制。这种技术,说起来也不复杂,就是改变程序流程的技术。在CPU的指令里,有几条指令可以改变

程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。理论上只要改变API入口和出口的任何机器码

,都可以HOOK,下面我就说说常用的改写API入口点的方法:
    
    因为工作在Ring3模式下,我们不能直接修改物理内存,只能一个一个打开修改,但具体的方法又分成

好几种,我给大家介绍几种操作思路:

  <1>首先改写API首字节,要实现原API的功能需要调用API时先还原被修改的字节,然后再调用原API,调

用完后再改回来,这样实现有点麻烦,但最简单,从理论上说有漏HOOK的可能,因为我们先还原了API,如果

在这之前程序调用了API,就有可能逃过HOOK的可能!

  (2)把被覆盖的汇编代码保存起来,在替代函数里模拟被被覆盖的功能,然后调用原函数(原地址+被覆

盖长度).但这样会产生一个问题,不同的汇编指令长度是不一样的(比如说我们写入的JMP指令占用5个字

节,而我们写入的这5个字节占用的位置不一定正好是一个或多个完整的指令,有可能需要保存7个字节,

才不能打乱程序原有的功能,需要编写一个庞大的判断体系来判断指令长度,网上已经有这样的汇编程序

(Z0MBiE写的LDE32),非常的复杂!

  (3)把被HOOK的函数备份一下,调用时在替代函数里调用备份函数.为了避免麻烦,可以直接备份整个

DLL缺点就是太牺牲内存,一般不推荐使用这种方法!

 
=====[ 4. 汇编实现 ]==============================================

本文就是建立在第2种方法之上的!本着先易后难的原则,今天我们先来说说它的第1种操作思路.
  
  我们拿API函数ExitWindowsEx来说明,下面是我在OD里拦下的ExitWindowsEx原入口部分

     77D59E2D            $  8BFF          mov edi,edi  
     77D59E2F            .  55            push ebp
     77D59E30            .  8BEC          mov ebp,esp
     77D59E32            .  83EC 18       sub esp,18
      ......

  如果我们把ExitWindowsEx的入口点改为下面的,会出现什么情况?

    77D59E2D               B8 00400000   mov eax,4000
    77D59E32               FFE0          jmp eax
    ......


  我们可想而知,程序执行到77D59E32处就会改变流程跳到00400000的地方


  如果我们的00400000处是这样的子程:

=======================
MyAPI proc  bs:DWORD  ,dwReserved:DWORD  ;和ExitWindowsEx一样带2个参数                 

;做你想做的事

......

;这里放API入口点改回原机器码的代码

;如果你是备份的整个DLL,就直接调用备份API,不用改来改去了,不会有漏勾API的可能!

invoke ExitWindowsEx,bs,dwReserved 
                          
;这里放HOOK API的代码
  
.endif

mov eax,TRUE

ret
=======================

   这里的MyAPI是和ExitWindowsEx参数一样的的子程,因为程序是在API的入口部分跳转的,根据

stdcall约定(参数数据从右向左依次压栈,恢复堆栈的工作交由被调用者),此时堆栈还没有恢复,我们

在子程里取出的参数数据依然有效,我们可以在这里执行自己的代码,你可以决定是否继续按原参数或改

变参数后再调用原API,也可以什么都不做,当然在调用之前,我们要先还原我们修改过的API(可以事先用

API函数ReadProcessMemory读出原API的前几个字节备份之),调用完后再改回来继续HOOK API,不过这种

方法有漏API的可能(原因前面已经说了),你如果觉得这个方法不妥,因为一般系统DLL都不大,你可以备

份整个DLL.

下面我就列出ring3下HOOK API的几个步骤:

1.得到要挂勾API的入口点

2.修改API的入口点所在页的页面保护为可读写模式

3.用ReadProcessMemory读出API的入口点开始的几字节备份

4.用WriteProcessMemory修改API的入口点象这样的形式:
  
  mov eax,4000
  
  jmp eax

 其中的4000要用和原API参数一样的子程序地址代替


  在这个子程序里我们决定用什么参数再调用原API,不过调用之前要用备份的前8字节改回来

调用之后在挂勾,如此反复.



=====[ 4.1. 代码 ]==============================================

  前面所讲的是本进程挂勾,我们要挂勾所有进程,可以用全局勾子,需要单独的一个DLL,我们可

以在DLL的DLL_PROCESS_ATTACH事件里来HOOK API

=================================hookdll.dll==========================
.486 
.model flat,stdcall   ;参数的传递约定是stdcall(从右到左,恢复堆栈的工作交由被调用者)
option casemap:none 
include \masm32\include\windows.inc 
include \masm32\include\kernel32.inc 
includelib \masm32\lib\kernel32.lib 
include \masm32\include\user32.inc 
includelib \masm32\lib\user32.lib 


HOOKAPI struct 
a  byte ? 
PMyapi DWORD ?   
d BYTE ?  
e BYTE ?
HOOKAPI ends


;子程序声明
WriteApi proto :DWORD ,:DWORD,:DWORD,:DWORD
MyAPI proto  :DWORD  ,:DWORD
GetApi proto  :DWORD,:DWORD


;已初始化数据
.data 
hInstance dd 0
WProcess dd 0
hacker HOOKAPI <> 
CommandLine LPSTR ? 

Papi1 DWORD ? 
Myapi1 DWORD ?
ApiBak1 db 10 dup(?) 
DllName1  db "user32.dll",0      
ApiName1  db "ExitWindowsEx",0 
mdb db "下面的程序想关闭计算机,要保持阻止吗?",0


;未初始化数据

.data? 
hHook dd ? 
hWnd dd ? 

;程序代码段

.code 

DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD 
   
  
 .if reason==DLL_PROCESS_ATTACH     ;当DLL加载时产生此事件
        push hInst 
        pop hInstance 

invoke GetCommandLine   
mov CommandLine,eax                                         ;取程序命令行

;初始化

mov hacker.a,0B8h     ;mov eax,
;mov hacker.d PMyapi  ;0x000000
mov hacker.d,0FFh     ;jmp 
mov hacker.e, 0E0h    ;eax
 

invoke   GetCurrentProcess                                   ;取进程伪句柄

 mov WProcess ,eax
    
invoke GetApi,addr DllName1,addr ApiName1                    ;取API地址
  
 mov Papi1,eax                                               ;保存API地址

invoke ReadProcessMemory,WProcess,Papi1,addr ApiBak1,8,NULL  ;备份原API的前8字节

 mov hacker.PMyapi,offset MyAPI   ;0x0000,这里设置替代API的函数地址

invoke WriteApi,WProcess,Papi1, addr hacker ,size HOOKAPI    ;HOOK API

.endif 

.if  reason==DLL_PROCESS_DETACH 

invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8               ;还原API

.endif 

 mov  eax,TRUE 
    ret 
DllEntry Endp 

GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD 
    invoke CallNextHookEx,hHook,nCode,wParam,lParam 
     mov eax,TRUE
     
      ret 
GetMsgProc endp 

InstallHook proc
   
    invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL 
    mov hHook,eax 
    ret 
InstallHook endp 

UninstallHook proc 
    invoke UnhookWindowsHookEx,hHook 
   invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
  ret 
UninstallHook endp 

GetApi proc DllNameAddress:DWORD,ApiNameAddress:DWORD

invoke  GetModuleHandle,DllNameAddress     ;取DLL模块句柄
   
  .if eax==NULL
  
  invoke LoadLibrary ,DllNameAddress    ;加载DLL
  
   .endif
  
 invoke GetProcAddress,eax,ApiNameAddress  ;取API地址
   

mov eax,eax
  
ret

GetApi endp


;============================下面是核心部分=========================


WriteApi proc Process:DWORD ,Papi:DWORD,Ptype:DWORD,Psize:DWORD

LOCAL mbi:MEMORY_BASIC_INFORMATION
LOCAL msize:DWORD


;返回页面虚拟信息
invoke VirtualQueryEx,Process, Papi,addr mbi,SIZEOF MEMORY_BASIC_INFORMATION

;修改为可读写模式

invoke VirtualProtectEx,Process, mbi.BaseAddress,8h,PAGE_EXECUTE_READWRITE,addr 

mbi.Protect

;开始写内存

invoke  WriteProcessMemory,Process, Papi, Ptype,Psize ,NULL

PUSH eax

;改回只读模式

invoke VirtualProtectEx,Process,mbi.BaseAddress,8h,PAGE_EXECUTE_READ,addr mbi.Protect

pop eax

ret

WriteApi endp



;替代的API,参数要和原来一样

MyAPI proc  bs:DWORD  ,dwReserved:DWORD                      

invoke MessageBox, NULL,  CommandLine, addr mdb, MB_YESNO      ;弹出信息框选择是否阻止

.if eax==7                                                   ;如果选择否

 invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8              ;先还原API
 
 invoke ExitWindowsEx,bs,dwReserved                           ;再调用API
 
 invoke WriteApi,WProcess,Papi1, addr hacker ,sizeof HOOKAPI  ;调用完后再改回来
  
.endif

mov eax,TRUE 
ret

MyAPI endp

End DllEntry


===============================hookdll.def=============================

LIBRARY hookdll
EXPORTS InstallHook
EXPORTS UninstallHook


=====[ 4.2. 分析 ]==============================================

HOOKAPI struct 
a  byte ? 
PMyapi DWORD ?   
d BYTE ?  
e BYTE ?
HOOKAPI ends


   为了便于理解和使用,我定义了一个结构:这个结构有4个成员,第一个成员a,是个字节型,我用来放

0B8h(mov eax),PMyapi一个整数型,用来放我们的替代API函数的地址(0X000),第3个和第4个成员我分别

用来放JMP和EAX(jmp eax)那么连起来就是 mov,0X0000 ; jmp eax  


 .if reason==DLL_PROCESS_ATTACH     
        push hInst 
        pop hInstance 

invoke GetCommandLine   
mov CommandLine,eax                                         

;初始化

mov hacker.a,0B8h     ;mov eax,
;mov hacker.d PMyapi  ;0x0000
mov hacker.d,0FFh     ;jmp 
mov hacker.e, 0E0h    ;eax


invoke   GetCurrentProcess                                   

 mov WProcess ,eax


  当DLL加载时,我们先保存模块句柄,读取程序命令行,然后初始化HOOKAPI结构,写入我们要写到内存的

指令(PMyapi以后写入)并调用GetCurrentProcess取出进程伪句柄方便以后写内存.

invoke GetApi,addr DllName1,addr ApiName1                    
  
 mov Papi1,eax                                               

invoke ReadProcessMemory,WProcess,Papi1,addr ApiBak1,8,NULL  
 
mov hacker.PMyapi,offset MyAPI   ;0x0000   

invoke WriteApi,WProcess,Papi1, addr hacker ,size HOOKAPI    ;HOOK API


  接下来用子程GetApi取出要挂勾API的入口点,并用ReadProcessMemory读出入口点8字节备份之,写入
PMyapi调用子程WriteApi改写API的入口点,这个子程我不准备详细说了,它非常的简单,无非就是几个

API的调用.它的核心就是通过WriteProcessMemory改写内存.

.if  reason==DLL_PROCESS_DETACH 

invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8               

.endif 

 mov  eax,TRUE 
    ret 

   如果这个DLL被卸载了,那么那个在DLL里的替代函数(MyAPI)将是无效的,如果这个时候程序再调用这

个API,将出现非法操作,因此在DLL卸载前,我们必须还原API.

   总结一下,现在只要程序加载这个DLL,这个程序的ExitWindowsEx就会被我们勾住,接下来要怎样才能

让所有的程序都加载这个DLL呢?这就需要安装全局勾子:

  InstallHook proc
   
      invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL 
    
      invoke WriteApi,WProcess,Papi1, addr hacker ,sizeof HOOKAPI

      mov hHook,eax 
     ret 
  InstallHook endp 

   通过SetWindowsHookEx安装勾子,最后一个参数可以决定该钩子是局部的还是系统范围的。如果该值

为NULL,那么该钩子将被解释成系统范围内的,那它就可以监控所有的进程及它们的线程。

如果该函数调用成功的话,将在eax中返回钩子的句柄,否则返回NULL。我们必须保存该句柄,因为后

面我们还要它来卸载钩子,可以看出,我们创建的Hook类型是WH_CALLWNDPROC类型,该类型的Hook在进程

与系统一通信时就会被加载到进程空间,从而调用dll的初始化函数完成真正的Hook,值得一提的是:因

为要调用SetWindowsHookEx来安装钩子,我们GUI程序的这个DLL不会被

UnhookWidowHookEx卸载,也就只有一次DLL_PROCESS_ATTACH事件,因此这里再要

HOOK API一次!

我们回头来看看钩子回调函数:

  GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD 
      invoke CallNextHookEx,hHook,nCode,wParam,lParam 
       mov eax,TRUE
     
       ret 
  GetMsgProc endp 

   可以看到这里只是调用CallNextHookEx将消息交给Hook链中下一个环节处理,因为这里API函数
SetWindowsHookEx的唯一作用就是让进程加载我们的dll。

  UninstallHook proc 
     invoke UnhookWindowsHookEx,hHook 
     invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
   ret 
  UninstallHook endp 

要卸载一个钩子时调用UnhookWidowHookEx函数,该函数仅有一个参数,就是欲卸载的钩子的句柄。钩

子卸载后我们也要还原我们GUI程序的API.

  LIBRARY hookdll
  EXPORTS InstallHook
  EXPORTS UninstallHook

   我们公开DLL里的InstallHook和UninstallHook函数,方便程序调用,这样我们只要在另外的程序中调

用InstallHook便可安装全局勾子,勾住所有程序中的API:ExitWindowsEx,执行我们自定的子程!

如果不需要了,可以调用UninstallHook卸载全局勾子.

   请注意:对于远程钩子,钩子函数必须放到DLL中,它们将从DLL中映射到其它的进程空间中去。当

WINDOWS映射DLL到其它的进程空间中去时,不会把数据段也进行映射。简言之,所有的进程仅共享DLL

的代码,至于数据段,每一个进程都将有其单独的拷贝。这是一个很容易被忽视的问题。您可能想当然

的以为,在DLL中保存的值可以在所有映射该DLL的进程之间共享。在通常情况下,由于每一个映射该

DLL的进程都有自己的数据段,所以在大多数的情况下您的程序运行得都不错。但是钩子函数却不是如

此。对于钩子函数来说,要求DLL的数据段对所有的进程也必须相同。这样您就必须把数据段设成共享

的:

 一般来说, 目标文件有三个段, 分别是 text/data/bss 段.

.text 段放置代码, 是只读且可运行段 

.data 段放置静态数据, 这些数据会被放置入 exe 文件. 这个段是可读写, 但是不能运行的. 

.bss 段放置动态数据, 这些数据不被放入 exe 文件, 在exe文件被加载入内存后才分配的空间.

你可以通过在链接开关中指定段的属性来实现:

/SECTION:name,[E][R][W][S][D][K][L][P][X]

其中S表示共享,已初期化的段名是.data,未初始化的段名是.bss。假如您想要写一个包含钩子函数的

DLL,而且想使它的未初始化的数据段在所有进程间共享,您必须这么做:
 
link /section:.bss[S]  /DLL  /SUBSYSTEM:WINDOWS ..........

否则,您的全局勾子将不能正常工作!

=====[ 5. 结束语 ]================================================

    我欢迎任何人提出更多的这里没有提到的挂钩方法,我肯定那会有很多。同样欢迎补充我介绍得不

是很详细的方法。也可以把我懒得写的其它方法完成,把源代码发给我。这篇文档的目的是演示挂钩技

术的细节,我希望我做到了。
    
============================[ End ]========================

转载于:https://www.cnblogs.com/milantgh/p/3873191.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/407483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS之window的视图属性

1. innerWidth 属性和innerHeight 属性 innerWidth获取浏览器窗口宽度&#xff0c;注意不带浏览器的边框宽度&#xff0c;但是包括下拉按钮的宽度 innerHeight获取浏览器窗口高度&#xff0c;从浏览器底部到工具栏的距离&#xff0c;注意不包括工具栏的高度 innerWidth和inner…

[Linux C]递归遍历指定目录,以目录树形式展示

/*功能&#xff1a;演示了利用C语言递归遍历指定目录下的子目录(不含隐藏目录)和文件&#xff0c;并以目录树形式展示!其中编译命令为&#xff1a;gcc -o travel travel.c -stdc99*/#include <stdio.h>#include <dirent.h>#include <stdlib.h>#include <s…

在SQL Server2005中进行错误捕捉

任何程序都可能出现错误&#xff0c;在SQL Server中执行Transact-SQL也不例外。如果在Transact-SQL中发生了错误&#xff0c;一般有两种捕捉错误的方法&#xff0c;一种是在客户端代码&#xff08;如 c#、delphi等)中使用类似try...catch的语句进行捕捉&#xff1b;另外一种就是…

ListT用法

List<T>和List的用法是一模一样的,不同的是,List支持任何类型而List<T>只支持T类型,T是在定义时指定的类型. 如: List<string> listnew List<string>; 这样的话,每个list里的元素都是字符串. 同理, List<int> listnew List<int>; 这样的话…

reduce

reduce reduce(callback,initiaValue)会传入两个变量&#xff0c;回调函数(callback)和初始值(initiaValue)。1.只传回调函数 prev只取数组的第一个元素 next从数组的第二个元素开始一直往下取var arr ["a", "b", "c", "d", "e&…

CSS之Screen视图属性

1.availWidth和availHeight availWidth和availHeight返回的是显示器可用宽高&#xff0c;注意不包括开始菜单栏这种东东的高度和宽度 2.colorDepth 表示显示器的颜色深度 3.width和height 表示显示器屏幕的宽高&#xff0c;和availWidth和availHeight的区别是返回的宽高包含…

[Linux C]递归遍历指定目录下的子目录和文件

/*功能&#xff1a;演示了在Linux下利用C语言递归遍历指定目录下的子目录(不含隐藏目录)和文件*/#include <stdio.h>#include <dirent.h>#include <string.h>void List(char *path){struct dirent *ent NULL;DIR *pDir;if((pDir opendir(path)) ! NULL){wh…

Java的多进程运行模式分析

本文曾发表于天极网&#xff1a;http://dev.yesky.com/284/2659284.shtml一般我们在java中运行其它类中的方法时&#xff0c;无论是静态调用&#xff0c;还是动态调用&#xff0c;都是在当前的进程中执行的&#xff0c;也就是说&#xff0c;只有一个java虚拟机实例在运行。而有…

HDU 2594 Simpsons’ Hidden Talents (字符串-KMP)

Simpsons’ Hidden Talents Problem DescriptionHomer: Marge, I just figured out a way to discover some of the talents we weren’t aware we had.Marge: Yeah, what is it?Homer: Take me for example. I want to find out if I have a talent in politics, OK?Marge: …

微信小程序几种常用弹窗提示

第一种&#xff1a;弹出提示框&#xff0c;可以选择确定或者取消。 代码&#xff1a;wx.showModal({title: 提示,content: 这是一个模态弹窗,success: function (res) {if (res.confirm) {//这里是点击了确定以后console.log(用户点击确定)} else {//这里是点击了取消以后conso…

[bash]删除文件中含特定字符串的行

]删除文件中含特定字符串的行[bash]: sed -e /abc/d a.txt // 删除a.txt中含"abc"的行&#xff0c;但不改变a.txt文件本身&#xff0c;操作之后的结果在终端显示 sed -e /abc/d a.txt > a.log // 删除a.txt中含"abc"的行&#xff0c;将操作之后的…

CSS之文档视图(DocumentView)和元素视图(ElementView)方法

1.elementFromPoint() 返回给定坐标处的元素。是个在目前而言&#xff0c;兼容性不错的方法 将给定位置处(100,100)的元素的字体颜色设置为红色 2.getBoundingClientRect() 返回的是返回的是一个对象&#xff0c;包含 top, left, right, 和 bottom四个属性值&#xff0c;大小…

谈CRM产品设计的指导思想

客户关系管理&#xff08;CRM&#xff09;其本意强调的是对客户“关系”进行有效管理&#xff0c;从而达到维持较高的客户占有率&#xff08;customershare&#xff09;的目的。所谓关系&#xff0c;是指两个事物之间其中的一方对另一方的行为方式以及感受状态。所以&#xff0…

try catch finally

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>try catch finally</title><script>// 异常最大的特征,就是一旦出现异常,后面的代码将不会再执行// 那为了保证后面的代码在出现异常之后…

Java笔记-IO流的运用

--如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3877386.html "谢谢-- 1.InputStream和System.in&#xff08;Scanner&#xff09; InputStream 输出流以字节为单位来获取数据&#xff0c;且需要复杂的判断并创建字节数组作为缓冲 另外字节转…

[Bash]kill指定的进程名

通过bash来kill指定的进程名&#xff0c;bash文件名为&#xff1a;/home/zcm/bin/d.sh&#xff0c;内容如下&#xff1a; #!/bin/shif [ "$1" "" ]; thenecho "Usage: sh $0 <processname>"exit 0fi#s1ps -ef|grep $1|grep -v grep|awk …

CSS之元素视图属性

1.clientLeft和clientTop 返回的是内容区域的左上角相对于整个元素左上角的位置&#xff08;包括边框&#xff09; 2.clientWidth和clientHeight 表示内容区域的高度和宽度&#xff0c;包括padding大小&#xff0c;但是不包括边框和滚动条 3.offsetLeft和offsetTop offsetLe…