在Visual C#中调用API的基本过程:
首先,在调用API之前,你必须先导入SystemRuntimeInteropServices这个名称空间。该名称空间包含了在Visual C#中调用API的一些必要集合,具体的方法如下:
using SystemRuntimeInteropServices;
在导入了名称空间后,我们要声明在程序中所要用到的API函数。我们的程序主要是获取系统的相关信息,所以用到的API函数都是返回系统信息的。先给出在Visual C#中声明API的方法:
[DllImport("kernel32")]
public static extern void GetWindowsDirectory(StringBuilder WinDir,int count);
其中,"DllImport"属性用来从不可控代码中调用一个方法,它指定了DLL的位置,该DLL中包含调用的外部方法;"kernel32"设定了类库名;"public"指明函数的访问类型为公有的;"static"修饰符声明一个静态元素,而该元素属于类型本身而不是指定的对象;"extern"表示该方法将在工程外部执行,同时使用DllImport导入的方法必须使用"extern"修饰符;最后GetWindowsDirectory函数包含了两个参数,一个为StringBuilder类型的,另一个为int类型的,该方法返回的内容存在于StringBuilder类型的参数中。同时,因为我们在这里使用到了StringBuilder类,所以在程序的开始处,我们还得添加SystemText这个名称空间,方法同上。
其他几个API函数的声明如下:
[DllImport("kernel32")]
public static extern void GetSystemDirectory(StringBuilder SysDir,int count);
[DllImport("kernel32")]
public static extern void GetSystemInfo(ref CPU_INFO cpuinfo);
[DllImport("kernel32")]
public static extern void GlobalMemoryStatus(ref MEMORY_INFO meminfo);
[DllImport("kernel32")]
public static extern void GetSystemTime(ref SYSTEMTIME_INFO stinfo);
以上几个API的作用分别是获取系统路径,获得CPU相关信息,获得内存的相关信息,获得系统时间等。
在声明完所有的API函数后,我们发现后三个函数分别用到了CPU_INFO、MEMORY_INFO、SYSTEMTIME_INFO等结构,这些结构并非是Net内部的,它们从何而来?其实,我们在用到以上API调用时均需用到以上结构,我们将函数调用获得的信息存放在以上的结构体中,最后返回给程序输出。这些结构体比较复杂,但是如果开发者能够熟练运用,那么整个API世界将尽在开发者的掌握之中。以下就是上述结构体的声明:
//定义以下各结构
//定义CPU的信息结构
[StructLayout(LayoutKindSequential)]
public struct CPU_INFO
{
public uint dwOemId;
public uint dwPageSize;
public uint lpMinimumApplicationAddress;
public uint lpMaximumApplicationAddress;
public uint dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public uint dwProcessorLevel;
public uint dwProcessorRevision;
}
//定义内存的信息结构
[StructLayout(LayoutKindSequential)]
public struct MEMORY_INFO
{
public uint dwLength;
public uint dwMemoryLoad;
public uint dwTotalPhys;
public uint dwAvailPhys;
public uint dwTotalPageFile;
public uint dwAvailPageFile;
public uint dwTotalVirtual;
public uint dwAvailVirtual;
}
//定义系统时间的信息结构
[StructLayout(LayoutKindSequential)]
public struct SYSTEMTIME_INFO
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
结构体定义的主体部分和C++中的没多大差别,具体每个结构体内部成员的定义可参考联机帮助中的SDK文档。同时,我们还发现在每个结构体定义的上面都有一句用中括号括起来的说明性文字。这些说明都是有关结构体成员的布局的,共有三种选项,分别说明如下:
LayoutKindAutomatic:为了提高效率允许运行态对类型成员重新排序。
注意:永远不要使用这个选项来调用不受管辖的动态链接库函数。
LayoutKindExplicit:对每个域按照FieldOffset属性对类型成员排序
LayoutKindSequential:对出现在受管辖类型定义地方的不受管辖内存中的类型成员进行排序。
在上面的程序中,为了方便起见我们都用到了第三种方式所有的API函数以及相关的结构体声明完毕后,我们就运用这些API来实现我们的程序功能――获取系统的相关信息。
界面可按如下方式布置,不过有兴趣的读者自然可以发挥自己的想象,将界面布局做得更好。
简单的界面布置好后,我们添加一个按钮("获取信息"按钮)的消息处理函数如下:
private void GetInfo_Click(object sender, SystemEventArgs e)
{
//调用GetWindowsDirectory和GetSystemDirectory函数分别取得Windows路径和系统路径
const int nChars = 128;
StringBuilder Buff = new StringBuilder(nChars);
GetWindowsDirectory(Buff,nChars);
WindowsDirectoryText = "Windows路径:"+BuffToString();
GetSystemDirectory(Buff,nChars);
SystemDirectoryText = "系统路径:"+BuffToString();
//调用GetSystemInfo函数获取CPU的相关信息
CPU_INFO CpuInfo;
CpuInfo = new CPU_INFO();
GetSystemInfo(ref CpuInfo);
NumberOfProcessorsText = "本计算机中有"+CpuInfodwNumberOfProcessorsToString()+"个CPU";
ProcessorTypeText = "CPU的类型为"+CpuInfodwProcessorTypeToString();
ProcessorLevelText = "CPU等级为"+CpuInfodwProcessorLevelToString();
OemIdText = "CPU的OEM ID为"+CpuInfodwOemIdToString();
PageSizeText = "CPU中的页面大小为"+CpuInfodwPageSizeToString();
//调用GlobalMemoryStatus函数获取内存的相关信息
MEMORY_INFO MemInfo;
MemInfo = new MEMORY_INFO();
GlobalMemoryStatus(ref MemInfo);
MemoryLoadText = MemInfodwMemoryLoadToString()+"%的内存正在使用";
TotalPhysText = "物理内存共有"+MemInfodwTotalPhysToString()+"字节";
AvailPhysText = "可使用的物理内存有"+MemInfodwAvailPhysToString()+"字节";
TotalPageFileText = "交换文件总大小为"+MemInfodwTotalPageFileToString()+"字节";
AvailPageFileText = "尚可交换文件大小为"+MemInfodwAvailPageFileToString()+"字节";
TotalVirtualText = "总虚拟内存有"+MemInfodwTotalVirtualToString()+"字节";
AvailVirtualText = "未用虚拟内存有"+MemInfodwAvailVirtualToString()+"字节";
//调用GetSystemTime函数获取系统时间信息
SYSTEMTIME_INFO StInfo;
StInfo = new SYSTEMTIME_INFO();
GetSystemTime(ref StInfo);
DateText = StInfowYearToString()+"年"+StInfowMonthToString()+"月"+StInfowDayToString()+"日";
TimeText = (StInfowHour+8)ToString()+"点"+StInfowMinuteToString()+"分"+StInfowSecondToString()+"秒";
}
在上面的消息处理函数中,我们运用了在程序开始处声明的各个API函数获取了系统的相关信息,并最终在界面上以文本标签的方式显示结果。各个文本标签的命名方式可以参见文后附带的源代码,此处暂略。
最后,运行程序如下:
结束语:
通过本文的学习,我相信稍有API使用基础的开发者可以马上触类旁通,很快掌握Visual C#中对API的 *** 作。上面给出的实例仅仅是一个非常简单的示例程序,不过有兴趣的读者可以进一步完善其功能,做出更完美的系统信息检测程序。
批处理本身没有压缩功能的命令行,不过winrar提供了rarexe命令行工具具有压缩的功能。
cd /d "C:\Program Files\WinRAR"
rem C:\Program Files\WinRAR是我的winrar所在的文件夹
rar a "c:\%time::=%rar" "C:\Users\helloworld\Desktop\testtxt"
rem 前面的压缩包,后面是要压缩的文件
rar的帮助,非常多——意味着功能很强大
用法: rar <命令> -<开关 1> -<开关 N> <压缩文件> <文件>
<@列表文件> <解压路径\>
命令>
a 添加文件到压缩文件
c 添加压缩文件注释
cf 添加文件注释
ch 改变压缩文件参数
cw 写入压缩文件注释到文件
d 删除压缩文件中的文件
e 解压压缩文件到当前目录
f 刷新压缩文件中的文件
i[参数]=<串> 在压缩文件中查找字符串
k 锁定压缩文件
l[t,b] 列出压缩文件[技术信息,简洁]
m[f] 移动到压缩文件[仅对文件]
p 打印文件到标准输出设备
r 修复压缩文件
rc 重建丢失的卷
rn 重命名压缩文件
rr[N] 添加数据恢复记录
rv[N] 创建恢复卷
s[名字|-] 转换压缩文件为自解压格式或转换回压缩文件
t 测试压缩文件
u 更新压缩文件中的文件
v[t,b] 详细列出压缩文件[技术信息,简洁]
x 用绝对路径解压文件
开关>
- 停止扫描
ac 压缩或解压后清除存档属性
ad 添加压缩文件名到目标路径
ag[格式] 使用当前日期生成压缩文件名
ai 忽略文件属性
ao 添加具有压缩属性的文件
ap<格式> 添加路径到压缩文件中
as 同步压缩文件内容
av 添加用户身份校验(仅注册版本可用)
av- 禁用用户身份校验
c- 禁用注释显示
cfg- 禁用读取配置
cl 名称转换为小写
cu 名称转换为大写
df 压缩后删除文件
dh 打开共享文件
dr 删除文件到回收站
ds 对固实压缩文件禁用名称排序
dw 档案处理后清除文件
e[+]<属性> 设置文件排除和包括属性
ed 不添加空目录
en 不添加"压缩文件结束"标志
ep 从名称中排除路径
ep1 从名称中排除基本目录
ep2 展开为完整路径
ep3 扩展路径为包含盘符的完全路径
f 刷新文件
hp[密码] 同时加密文件数据和文件头
id[c,d,p,q] 禁用消息
ieml[属性] 用 E-mail 发送压缩文件
ierr 发送所有消息到标准错误设备
ilog[名称] 把错误写到日志文件(只有注册版本可用)
inul 禁用所有消息
ioff 完成一个 *** 作后关闭 PC 电源
isnd 启用声音
k 锁定压缩文件
kb 保留损坏的已解压文件
m<05> 设置压缩级别(0-存储3-默认5-最大)
mc<参数> 设置高级压缩参数
md<大小> 以KB为单位的字典大小(64,128,256,512,1024,2048
ms[ext;ext] 指定存储的文件类型
mt<线程> 设置线程数
n<文件> 仅包含指定的文件
n@ 从标准输入设备读取文件名到包括
n@<列表> 包含在指定的列表文件中列出的文件
o[+|-] 设置覆盖模式
oc 设置 NTFS 压缩属性
or 自动重命名文件
os 保存 NTFS 流
ow 保存或恢复文件所有者和组
p[密码] 设置密码
p- 不询问密码
r 递归子目录
r- 禁用递归
r0 仅递归通配符名称的子目录
ri<P>[:<S>] 设置优先级(0-默认,1-最小15-最大)和以毫秒为单
rr[N] 添加数据恢复记录
rv[N] 创建恢复卷
s[<N>,v[-],e] 创建固实压缩文件
s- 禁用固实压缩文件
sc<chr>[obj] 指定字符集
sfx[名称] 创建自解压压缩文件
st[名称] 从标准输入设备读取数据(stdin)
sl<大小> 处理小于指定大小的文件
sm<大小> 处理超过指定大小的文件
t 压缩后测试文件
ta<日期> 添加日期 <日期> 后修改的文件,日期格式 YYYYMMD
tb<日期> 添加日期 <日期> 前修改的文件,日期格式 YYYYMMD
tk 保留原始压缩文件时间
tl 设置压缩文件时间为最新文件时间
tn<时间> 添加 <时间> 以后的文件
to<时间> 添加 <时间> 以前的文件
ts<m,c,a>[N] 保存或恢复文件时间(修改,创建,访问)
u 更新文件
v 自动检测创建卷的大小或者列出所有的卷
v<大小>[k,b] 创建卷大小=<大小>1000 [1024, 1]
vd 创建容量前清除磁盘内容
ver[n] 文件版本控制
vn 使用旧风格的卷命名方案
vp 每卷之前暂停
w<路径> 指定工作目录
x<文件> 排除指定的文件
x@ 从标准输入设备读取要排除的文件名
x@<列表> 排除在指定的列表文件中列出的文件
y 假设对全部询问都回答是
z[文件] 从文件读取压缩文件注释
调用windowsAPI函数应该可以做到
1、首先申明using SystemRuntimeInteropServices命名空间
2、函数主体定义等:
/// <summary>
/// "DllImport"属性用来从不可控代码中调用一个方法,它指定了DLL的位置,该DLL中包含调用的外部方法;
/// "kernel32"设定了类库名;"public"指明函数的访问类型为公有的;"static"修饰符声明一个静态元素,
/// 而该元素属于类型本身而不是指定的对象;"extern"表示该方法将在工程外部执行,同时使用DllImport导
/// 入的方法必须使用"extern"修饰符;最后GetWindowsDirectory函数包含了两个参数,一个为StringBuilder类型的,
/// 另一个为int类型的,该方法返回的内容存在于StringBuilder类型的参数中。同时,因为我们在这里使用
/// 到了StringBuilder类,所以在程序的开始处,我们还得添加SystemText这个名称空间,方法同上。
/// </summary>
/// <param name="WinDir"></param>
/// <param name="count"></param>
[DllImport("kernel32")] ///获取Windows路径
public static extern void GetWindowsDirectory(StringBuilder WinDir, int count);
[DllImport("kernel32")] ///获取系统路径
public static extern void GetSystemDirectory(StringBuilder SysDir,int count);
[DllImport("kernel32")] ///获取CPU相关信息
public static extern void GetSystemInfo(ref CPU_INFO cpuinfo);
[DllImport("kernel32")] ///获取内存信息
public static extern void GlobalMemoryStatus(ref MEMORY_INFO meminfo);
[DllImport("kernel32")] ///获取系统时间
public static extern void GetSystemTime(ref SYSTEMTIME_INFO stinfo);
///定义CPU结构信息
[StructLayout(LayoutKindSequential)]
public struct CPU_INFO
{
public uint dwOemId;
public uint dwPageSize;
public uint lpMinimumApplicationAddress;
public uint lpMaximumApplicationAddress;
public uint dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public uint dwProcessorLevel;
public uint dwProcessorRevision;
}
///定义内存结构信息
[StructLayout(LayoutKindSequential)]
public struct MEMORY_INFO
{
public uint dwLength;
public uint dwMemoryLoad;
public uint dwTotalPhys;
public uint dwAvailPhys;
public uint dwTotalPageFile;
public uint dwAvailPageFile;
public uint dwTotalVirtual;
public uint dwAvailVirtual;
}
///定义系统时间结构信息
[StructLayout(LayoutKindSequential)]
public struct SYSTEMTIME_INFO
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
private void button1_Click(object sender, EventArgs e)
{
//调用GetWindowsDirectory和GetSystemDirectory函数分别取得Windows路径和系统路径
const int nChars = 128;
StringBuilder Buff = new StringBuilder(nChars);
GetWindowsDirectory(Buff, nChars);
WindowsDirectoryText = "Windows路径:" + BuffToString();
GetSystemDirectory(Buff, nChars);
SystemDirectoryText = "系统路径:" + BuffToString();
//调用GetSystemInfo函数获取CPU的相关信息
CPU_INFO CpuInfo;
CpuInfo = new CPU_INFO();
GetSystemInfo(ref CpuInfo);
NumberOfProcessorsText = "本计算机中有" + CpuInfodwNumberOfProcessorsToString() + "个CPU";
ProcessorLevelText = "CPU等级为" + CpuInfodwProcessorLevelToString();
OemIdText = "CPU的OEM ID为" + CpuInfodwOemIdToString();
PageSizeText = "CPU中的页面大小为" + CpuInfodwPageSizeToString();
//调用GlobalMemoryStatus函数获取内存的相关信息
MEMORY_INFO MemInfo;
MemInfo = new MEMORY_INFO();
GlobalMemoryStatus(ref MemInfo);
MemoryLoadText = MemInfodwMemoryLoadToString() + "%的内存正在使用";
TotalPhysText = "物理内存共有" + MemInfodwTotalPhysToString() + "字节";
AvailPhysText = "可使用的物理内存有" + MemInfodwAvailPhysToString() + "字节";
TotalPageFileText = "交换文件总大小为" + MemInfodwTotalPageFileToString() + "字节";
AvailPageFileText = "尚可交换文件大小为" + MemInfodwAvailPageFileToString() + "字节";
TotalVirtualText = "总虚拟内存有" + MemInfodwTotalVirtualToString() + "字节";
AvailVirtualText = "未用虚拟内存有" + MemInfodwAvailVirtualToString() + "字节";
//调用GetSystemTime函数获取系统时间信息
SYSTEMTIME_INFO StInfo;
StInfo = new SYSTEMTIME_INFO();
GetSystemTime(ref StInfo);
DateText = StInfowYearToString() + "年" + StInfowMonthToString() + "月" + StInfowDayToString() + "日";
TimeText = (StInfowHour + 8)ToString() + "点" + StInfowMinuteToString() + "分" + StInfowSecondToString() + "秒";
}
分两步
①首先用宏判断OS类型:
#ifdef _WIN32
#endif
#ifdef _UNIX
#endif
#ifdef _LINUX
#endif
②判断属于windows系列的话就可以使用下面的代码判断是那种类型了。(注释中给了结构体介绍)
代码:
#include <stdioh>
#include <windowsh>
//OSVERSIONINFO结构体的定义:
//
//typedef OSVERSIONINFOW OSVERSIONINFO;
//typedef struct _OSVERSIONINFOW {
// DWORD dwOSVersionInfoSize; //指定该数据结构的字节大小
// DWORD dwMajorVersion; // *** 作系统的主版本号 5代表2000以上版本
// DWORD dwMinorVersion; // *** 作系统的副版本号 0代表win2000 1代表winxp
// DWORD dwBuildNumber; // *** 作系统的创建号
// DWORD dwPlatformId; // *** 作系统ID号
// WCHAR szCSDVersion[ 128 ]; // Maintenance string for PSS usage 关于 *** 作系统的一些附加信息
//} OSVERSIONINFOW, POSVERSIONINFOW, LPOSVERSIONINFOW, RTL_OSVERSIONINFOW, PRTL_OSVERSIONINFOW;
int GetOSVer()
{
OSVERSIONINFO osver;
osverdwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osver);
if(osverdwPlatformId == 2)
{
if(osverdwMajorVersion == 5 && osverdwMinorVersion == 1)
{
printf("xp\n");
return(2);
}
if(osverdwMajorVersion == 5 && osverdwMinorVersion == 2)
{
printf("windows 2003\n");
return(3);
}
if(osverdwMajorVersion == 6 && osverdwMinorVersion == 0)
{
printf("vista and 2008\n");
return(4);
}
if(osverdwMajorVersion == 6 && osverdwMinorVersion == 1)
{
printf("2008 R2 and Windows 7\n");
return(5);
}
}
return 0;
}
int main()
{
GetOSVer();
}
客户机端 服务器端
======== ========
监听套接字 连接套接字
========== ==========
socket() socket()
bind() bind()
listen()
connect()----->accept()------>创建连接套接字
send()----------------------->recv()
recv()<-----------------------send()
closesocket() closesocket() closesocket()
我们可以看出,是客户机端的connect()执行实际的连接请求动作,我们再来看看connect函数的参数:
int connect(
SOCKET s, // 指定对哪个套接字进行 *** 作
const struct sockaddr FAR name, // 这是一个描述服务器IP地址的结构
int namelen // 指明上面这个结构的大小
);
对于name参数,由于sockaddr结构内容依赖于具体的协议,所以对于TCP/IP协议,我们传递sockaddr_in这个结构,再来看看这个结构:
struct sockaddr_in{
short sin_family; // 必须为AF_INET
unsigned short sin_port; // IP端口号
struct in_addr sin_addr; // 标识IP地址的一个结构体
char sin_zero[8]; // 为了兼容sockaddr而设置的占位空间
};
到这儿,我们可以看出,对于一次连接请求的目的地信息,已经全部在传入的参数中描述清楚了,接下来要做的就设置一个全局API钩子,钩住所有程序的connect()调用,在进行实际的connect() *** 作之前,我们先分析传入的参数,如果发现连接目的地是我们不允许访问的,就不进行连接 *** 作,仅返回一个错误码就可以了。就这么简单,就能实现一夫当关,万夫莫开的效果。
同样的道理,也可以Hook其它函数而实现监控整个网络通讯各方面的内容,比如说截取发送和接收的数据包进行分析等等,这就取决于设计者的意图了,大家不妨动手试试看,感受一下Hook API的魅力。
Hook Win32 API 的应用研究之二:进程防杀
在WINDOWS *** 作系统下,当我们无法结束或者不知道怎样结束一个程序的时候,或者是懒得去找“退出”按钮的时候,通常会按“CTRL+ALT+DEL”呼出任务管理器,找到想结束的程序,点一下“结束任务”就了事了,呵呵,虽然有点粗鲁,但大多数情况下都很有效,不是吗?
设想一下,如果有这么一种软件,它所要做的工作就是对某个使用者在某台电脑上的活动作一定的限制,而又不能被使用者通过“结束任务”这种方式轻易地解除限制,那该怎么做?无非有这么三种方法:1屏蔽“CTRL+ALT+DEL”这个热键的组合;2让程序不出现在任务管理器的列表之中;3让任务管理器无法杀掉这个任务。对于第一种方法,这样未免也太残酷了,用惯了“结束任务”这种方法的人会很不习惯的;对于第二址椒ǎ赪INDOWS 9X下可以很轻易地使用注册服务进程的方法实现,但是对于WINDOWS NT架构的 *** 作系统没有这个方法了,进程很难藏身,虽然仍然可以实现隐藏,但实现机制较为复杂;对于第三种方法,实现起来比较简单,我的作品:IPGate 网址过滤器 就是采用的这种方式防杀的,接下来我就来介绍这种方法。
任务管理器的“结束任务”实际上就是强制终止进程,它所使用的杀手锏是一个叫做TerminateProcess()的Win32 API函数,我们来看看它的定义:
BOOL TerminateProcess(
HANDLE hProcess; // 将被结束进程的句柄
UINT uExitCode; // 指定进程的退出码
);
看到这里,是不是觉得不必往下看都知道接下来要做什么:Hook TerminateProcess()函数,每次TerminateProcess()被调用的时候先判断企图结束的进程是否是我的进程,如果是的话就简单地返回一个错误码就可以了。真的是这么简单吗?先提出一个问题,如何根据hProcess判断它是否是我的进程的句柄?答案是:在我的进程当中先获得我的进程的句柄,然后通过进程间通讯机制传递给钩子函数,与hProcess进行比较不就行了?错!因为句柄是一个进程相关的值,不同进程中得到的我的进程的句柄的值在进程间进行比较是无意义的。
怎么办?我们来考察一下我的hProcess它是如何得到的。一个进程只有它的进程ID是独一无二的, *** 作系统通过进程ID来标识一个进程,当某个程序要对这个进程进行访问的话,它首先得用OpenProcess这个函数并传入要访问的进程ID来获得进程的句柄,来看看它的参数:
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 希望获得的访问权限
BOOL bInheritHandle, // 指明是否希望所获得的句柄可以继承
DWORD dwProcessId // 要访问的进程ID
);
脉络渐渐显现:在调用TerminateProcess()之前,必先调用OpenProcess(),而OpenProcess()的参数表中的dwProcessId是在系统范围内唯一确定的。得出结论:要Hook的函数不是TerminateProcess()而是OpenProcess(),在每次调用OpenProcess()的时候,我们先检查dwProcessId是否为我的进程的ID(利用进程间通讯机制),如果是的话就简单地返回一个错误码就可以了,任务管理器拿不到我的进程的句柄,它如何结束我的进程呢?
至此,疑团全部揭开了。由Hook TerminateProcess()到Hook OpenProcess()的这个过程,体现了一个逆向思维的思想。其实我当初钻进了TerminateProcess()的死胡同里半天出也不来,但最终还是蹦出了灵感的火花,注意力转移到了OpenProcess()上面,实现了进程防杀。喜悦之余,将这心得体会拿出来与大家分享。
Hook Win32 API 的应用研究之三:变速控制 <<<
这是Hook Win32 API的一个比较另类和有趣的应用方面。
这里所指的变速控制,并不是说可以改变任何程序的运行速度,只能改变符合这些条件的程序的运行速度:程序的运行速度依赖于定时控制,也就是说,程序的执行单元执行的频率是人为的依靠定时机制控制的,不是依赖于CPU的快慢。比如说,某个程序每隔1秒钟发出“滴答”声,它在快的电脑上和慢的电脑上所表现出来的行为是一致的。这样的依赖于定时控制的程序才是我们的研究“变速”对象。
一个WINDOWS应用程序的定时机制有很多。像上面提到的例子程序可以采用WM_TIMER消息来实现,通过函数SetTimer()可以设定产生WM_TIMER消息的时间间隔。其它的方法还有通过GetTickCount()和timeGetTime()等函数得到系统时间,然后通过比较时间间隔来定时,还有timerSetEvent()设置时钟事件等等方式。先来看看这些函数的定义:
UINT_PTR SetTimer(
HWND hWnd, // 接收WM_TIMER消息的窗口句柄
UINT_PTR nIDEvent, // 定时器的ID号
UINT uElapse, // 发生WM_TIMER消息的时间间隔
TIMERPROC lpTimerProc // 处理定时发生时的回调函数入口地址
);
MMRESULT timeSetEvent(
UINT uDelay, // 时钟事件发生的时间间隔
UINT uResolution, // 设置时钟事件的分辨率
LPTIMERCALLBACK lpTimerProc, // 处理时钟事件发生时的回调函数入口地址
DWORD dwUser, // 没峁┑幕氐魇br /> UINT fuEvent // 设置事件的类型
);
DWORD GetTickCount(VOID) // 返回系统启动以来经过了多少毫秒了
DWORD timeGetTime(VOID) // 类似于GetTickCount(),但分辨率更高
那么我们来看,如果能控制SetTimer()的uElapse参数、timeSetEvent()的uDelay参数、GetTickCount()和timeGetTime()的返回值,就能实现变速控制,除非应用程序使用的是其它的定时机制,不过大多数应用程序采用的定时机制不外乎都是这些。
该轮到Hook大法出场了。因为我们一般只想改变某个程序的速度,比如是说某个游戏程序,所以我们不设置全局钩子。又因为我们不清楚那个应用程序到底使用的是那种定时机制,所以上述几个函数我们全部都要接管,然后把关于定时参数或返回值按比例缩放就可以了。
int 21 dos 系统功能调用:
1 从键盘输入一个字符并回显在屏幕上
2 显示一个字符
5 打印一个字符
6 读键盘字符
7 从键盘输入一个字符,不回显
8 从键盘输入一个字符,不回显
9 显示字符串
a 输入字符到缓冲区(ds:dx=缓冲区首址)
b 读键盘状态
c 清除键盘缓冲区,并调用一种键盘功能(al=键盘功能号)
f 打开文件
10 关闭文件
14 顺序读
15 顺序写
16 建立文件
1a 置dta地址
21 随机读
22 随机写
23 测文件大小
25 设置中断向量
27 随机分块读
28 随机分块写
2a 获取系统日期
2b 设置系统日期
2c 获取系统时间
2d 设置系统时间
35 取中断向量
子功能的 *** 作:
1号子功能:从键盘输入一个字符并回显在屏幕上
返回参数:(al)<=输入字符的ascii码
2号子功能:显示一个字符
设置参数:(dl)<=要显示的字符
5号子功能:打印一个字符
设置参数:(dl)<=要打印的字符
6号子功能:显示一个字符
设置参数:(dl)<=要显示的字符
9号子功能:显示字符串
设置参数:(ds:dx)<=要显示的字符串地址
待显字符串存放在一个数据缓冲区,以"$"作为结束标志,且所显示内容必须是可显示的ascii码,否则会产生不可预料的结果。
a号子功能:输入字符到缓冲区( 当用户输入回车键时,结束输入;调用该功能前,在内存中建立一个输入缓冲区)
入口参数:(ds:dx)<=输入缓冲区的首地址;
出口参数:(ds:dx+2)<=实际输入字符数。
f号子功能:打开文件
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=找到置00否则置ff
10号子功能:关闭文件
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=修改成功置00否则置ff
14号子功能:顺序读
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=0为读成功;1为记录中无数据;2为空间不够;3为记录不完整
15号子功能:顺序写
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=0为写成功,1盘满,2为空间不够
16号子功能:建立文件
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=建立成功置00否则置ff
1a号子功能:置dta地址
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
21号子功能:随机读
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=0为读成功;1为文件结束;2为dat太小,传输结束;3为读到部分记录
22号子功能:随机写
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=0为写成功;1盘满;2为dat太小,传输结束
23号子功能:测文件大小
设置参数:
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=找到置00否则置ff
25号子功能:设置中断向量
设置参数:
al<=中断类型号
ds:dx<=中断向量
27号子功能:随机分块读
设置参数:
cx<=要读取的记录数
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=0为全部记录读成功;1为文件结束,最后记录完整;2为dat太小,传输结束;3为文件结束,最后记录不完整
cx<=读取的实际记录数
28号子功能:随机分块写
设置参数:
cx<=要写取的记录数
ds<=fcb的段地址
dx<=fcb的偏移地址
返回参数:
al<=0为全部记录写成功;1为盘满;2为dat空间满
cx<=写取的实际记录数
2a号子功能:获取系统日期
返回参数:
(cx)<=年(1980-2099)
(dh)<=月(1-12)
(dl)<=日(1-31)
(al)<=星期(0-6)
2b号子功能:设置系统日期
设置参数:
(cx)<=年(1980-2099)
(dh)<=月(1-12)
(dl)<=日(1-31)
(al)<=星期(0-6)
返回参数:
al<=设置成功置00否则置ff
2c号子功能:获取系统时间
返回参数:
(ch)<-时(0-23)
(cl)<-分(0-59)
(dh)<-秒(0-59)
(dl)<-百分秒(0-9)
2d号子功能:设置系统时间
设置参数:
(ch)<-时(0-23)
(cl)<-分(0-59)
(dh)<-秒(0-59)
(dl)<-百分秒(0-9)
返回参数:
al<=设置成功置00否则置ff
35号子功能:取中断向量
设置参数:
al<=中断类型号
返回参数:
es:bx<=中断向量
用户定义的输入缓冲区格式:
aa,bb,cc,,dh。
aa为用户定义的最多可键入字符数;
bb用户实际输入的字符数,不包括回车符;
cc字符串开始的字符;
0d回车符
例如:
data1 segment
var1 db 5,0,5 dup(0);定义缓冲区
data1 ends
lea dx,var1 ;设置缓冲区的首地址
mov ah,0ah
int 21h
lea si,var1+2 ;加载字符串开始地址
mov ch,var1+1 ;获得字符数
文件控制块fcb:
fcb是用户程序和 *** 作系统之间传递有关磁盘文件信息的存储区,它一般定义在程序的数据段,共36个字分为10个信息。
例如:djtxt文件在数据段定义的fcb:
tab1 label byte
0 drive db 4 磁盘驱动器:0为默认驱动器,1为驱动器a,2为驱动器b,
1-8 name db 'dj' 文件名(不足8个字节时,用空格补足)
9-11 ext db 'txt' 扩展名(不足3个字节时,用空格补足)
12-13 curr_block dw 0 当前块号(一块有128个记录)
14-15 rec_size dw 0 记录大小
16-19 size dw 2 dup() 文件大小(记录号记录大小)
20-21 date dw 日期
22-31 position db 10dup() 由dos自动填入
32 curr_rec db 0 当前记录号
33-36 rel_rec dw 2dup() 随机记录号
#include<timeh>
#include<iostreamh>
int main(void)
{
time_t now;
struct tm timenow;
time(&now);
//time函数读取现在的时间(国际标准时间非北京时间),然后传值给now
timenow = localtime(&now);
//localtime函数把从time取得的时间now换算成你电脑中的时间(就是你设置的地区)
cout<<"Local time is "<<asctime(timenow)<<endl;
//上句中asctime函数把时间转换成字符输出
return 0;
}
NTSYSAPI
NTSTATUS
NTAPI
NtQuerySystemInformation (
IN UINT SystemInformationClass, // 信息类型
OUT PVOID SystemInformation, // 缓冲指针
IN ULONG SystemInformationLength, // 缓冲的字节大小
OUT PULONG ReturnLength OPTIONAL // 写入缓冲的字节数
);
第一个参数是请求的信息类型。这个参数可以有许多值。为了得到系统启动时间,我们只用其中的一个值:SystemTimeInformation(3)。
如果,第一个参数是SystemTimeInformation,则第二个参数必须是一个SYSTEM_TIME_INFORMATION结构指针。
typedef struct
{
LARGE_INTEGER liKeBootTime;
LARGE_INTEGER liKeSystemTime;
LARGE_INTEGER liExpTimeZoneBias;
ULONG uCurrentTimeZoneId;
DWORD dwReserved;
} SYSTEM_TIME_INFORMATION;
这个结构中第一个成员是liKeBootTime就是我们所要的系统被启动的时间(以毫秒计)。
以上就是关于c# + api取多核cpu使用率。分别取。全部的内容,包括:c# + api取多核cpu使用率。分别取。、bat文件中如何得到系统时间、C#中设计一个对系统和服务器进行监测的逻辑思路是什么,等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)