置顶随笔
#
还是怀念上上周末一个人走香巴拉的感觉,那天因为头一日走了香山,脚上本已起了水泡,但是因为看着天气自己还是忍不住想找点事情做,于是选择了走这条自己并不怎么认路的香巴拉。
其实喜欢的不是路,是路上的感觉。总觉得自己和世人走的很远,总觉得很多时候即使在和人一起喝酒其实自己也是寂寞的,总觉得有时候也很享受这种寂寞的感觉,总觉得有时候自己很无聊很无耻喜欢这样子一个人虐待自己,总觉得也许有一个港湾或许是最后的归宿可以一睡不醒,总觉得现在我不过是尘世的一个过客也许明日又将上路,总觉得,总觉得,总觉得有很多东西是这辈子就跟定我了,比如这种杀死人般的淡淡伤感。
听歌,没人知道你的感觉,你可以和很多人分享,但是一种心情却只能是自己的。在路上听歌,没人知道你在听歌,寂寞的人会说有风知道云知道,其实那不过是在自欺罢了,何处知道风云就了解你在听歌呢?没处只到。只是正因为无处知晓,所以我寂寞的觉得幸福。
还记得上上周末走向巴拉时在岔路口想的事情,还记得在坡上看到一对情侣的舞蹈在我当时的眼里如何之一如天人,还记得如何的清风中享受那《Nightingale》带来的空谷回响。
没事情是一如往常,我只是在享受这种感觉。享受这种一个人在路上,在路上寻觅,在路上踟蹰。
有时候想想,人生也许不要答案或者不要结果真的很好,我可以永远只想现在的如何,永远只需要知晓我现在的感觉,永远只需要在一次舞蹈之后擦拭自己受伤的灵魂无需知道有否另外一个观众。
可是人生偏偏的如此需要结果。
2010年9月15日
#
控制流图对编译优化和程序静态分析很有用处。编译优化根据待编译程序的控制流减少其中不必要的跳转,使得代码结构更加紧凑。静态分析工具根据程序的控制流来分析得到程序的复杂度等等一系列统计信息,Visual Studio自带的代码度量(Code Metrics)就是静态分析中控制流图应用的一个例子。
控制流图(Control Flow Graph)是在计算机科学中用图的方式来表现程序执行过程中所有的可能执行路径的一种方式。在控制流图中,每一个节点表示表示一个基本的代码块,这些基本的代码块用箭头连接起来用以描述这些代码块之间的执行顺序。
在控制流图中,有两类特殊的代码块,入口代码块和出口代码块;所有的执行路径都从入口块开始,并且结束于一个出口块。
一个控制流图本身是一个图(Graph),可以用计算机图论中的术语来加以描述,诸如edge,vertex,back edge等等。
下面的图片可以作为一个基本的控制流图的样本:
2009年11月6日
#
Core-Man System Design Document
Version 1.0
9/29/2009
Contents
简介 4
系统总体架构介绍 4
系统具体设计 6
后记 6
简介
本文档用来描述Core-Man这个用来为汽配行业做货物、订单、客户关系管理的系统功能和设计。本文档基于该系统的需求分析和前期市场调研,关于需求分析和市场调研的结果,可以查看文档XXXX.
1. 系统设计目标
为了使得系统能最大程度的满足用户的需求,尽可能的减少以后再定制维护过程中的代价,Core-Man系统应该达到以下几个目标:
可定制性:虽然本系统是为了汽配行业的客户管理而设计的,但是本系统需要达到在特定的需求下经过适当的配置(数据库配置,配置文件更改)而达到满足其他行业需求的目的。
可维护性:本系统的设计应该达到可维护的目的,因为用户的需求可能会变化(企业规模扩大,经营业务增多)。在用户变化需求的时候,我们的系统就需要升级,那么在升级的过程,至少需要满足两方面的可重用,一是数据上的可重用。客户在一定的经营过程中积累了一定数目的用户资源和经营经验数据,这部分数据对于客户以后继续经营,扩大业务规模都是有重要作用的。在我们的系统升级的过程,应该使得客户可以重用这部分数据,即使得升级后的系统能够识别维护旧的数据。另一方面的可重用性是指代码上的可重用性,系统的升级不能使推到系统重来。新系统应该基于旧系统已有的代码,这样可以最大程度上的节省成本。
安全性:虽然在系统初期,客户对于安全性上的要求不会很高,但是随着企业业务规模的扩大,必然涉及到很多的商业保密信息。企业的客户数据,订单数据,管理模式都有很重要的商业价值,此时对于我们系统的安全性就提出了要求。Core-Man系统的安全性主要体现在一下几个方面:
用户认证:系统应该区分登录的会话中用户的身份,以此来判断用户可访问哪些数据,可更改哪些数据,这个对于一个中大型企业里面,各个管理层面(老板,总经理,经理,员工)应用同一个系统式很重要的。
权限管理:验证用户后,需要判断已验证用户是否被允许访问其所请求的数据,在判断通过的情况下,系统返回正确的响应;否则,系统告知客户端无权访问。
数据加密:企业的客户数据,员工数据,订单数据都涉及到商业秘密。这些数据都需要经过加密后存储在系统的文件系统或者数据库中。
系统总体架构介绍
很明显的Core-Man系统是一个以数据库为中心,以多终端进行数据处理维护的分布式系统,一个Core-Man系统的典型的应用场景可以用下图来描述:
在上面的应用场景中,各个角色的分工如下:
中心数据库:负责提供整个系统的数据的存贮,这里包含两部分内容
由数据库管理系统托管的数据库表,视图,存储过程,数据等信息。
由文件系统托管的图片,文件等信息。
请求监听/代理服务器:负责作为终端用户和数据库之间通信的一个代理人的角色。所有客户端的操作如果涉及到数据库查询/更改请求,由请求监听/代理服务器将其转换为对相应数据库项目的查询/更新操作。这样的设计能使得终端用户和数据库的耦合性降到最低,使得在需要更换数据库的时候,我们只需要更新请求监听/代理服务器里面与数据库交互的那部分代码。同时,加进一层请求监听/代理服务器,可以让我们的系统在这一层进行安全性上的检查更改等操作,比如,在这一层做终端用户身份的鉴别,只有终端用户是授权用户的时候,我们才允许客户端进行后续操作。
超级管理终端:这个终端有最大的管理权限,用于进行数据的备份,紧急模式下的更改等操作。
分布式终端1:本系统众多分布式终端的其中一个,用户为普通用户(经理,员工,客户)提供访问我们系统的功能。每一个分布式终端在发起连接请求监听/代理服务器的时候,都需要提供用户身份的信息,请求监听/代理服务器用这些信息来判断发起连接的用户是否是一个经过授权的用户。
下图描述了我们的数据库设计的基本轮廓:
下图描述了我们的请求监听/代理服务器的设计模块以及模块之间的联系:
自上至下,三个大的模块分别是:
请求监听层:负责监听客户端的操作请求,检查客户端的身份,以及客户端的权限。如果客户端是经授权的用户并且其请求操作被策略允许,请求监听层会将该请求放进该层维护的请求队列中。放进请求队列中的请求包含请求的客户端信息,请求的优先级,请求所要进行的具体操作等等。请求监听层会有一个请求分发线程,从请求队列中按照一定的策略找出一个请求,将该请求送往逻辑处理层。
逻辑处理层:逻辑处理层的主要功能是维护抽象的数据模型,使得请求层不必与具体的数据库访问器接口方法做交互。逻辑处理层将请求监听层分发过来的请求转换为对特定抽象数据的操作,这就使得在这一层能尽最大可能地应用面向对象设计的好处,为以后的升级,定制提供方便。逻辑处理层由请求监听模块,逻辑处理模块和逻辑对象管理器组成。请求监听模块将请求监听层分发过来的请求转换为对特定逻辑对象的操作。逻辑处理模块是一个特定的线程,负责调用逻辑对象管理器的方法来进行逻辑多项生命周期的维护。逻辑对象管理器负责提供逻辑对象的管理接口,比如逻辑对象的创建,删除等操作。
数据库访问层:数据库访问层主要由数据库访问器接口和数据库访问器实现来组成。这一层提供具体的数据访问操作给逻辑处理层使用。数据库访问器接口和数据库访问器实现分属于不同的程序集,这样就使得数据库访问器实现能很方便的被替换,使系统能迁移到使用新的数据库系统,只要我们提供新数据库系统的数据库访问器实现,并且更改数据库访问层的程序配置文件指向新的数据库访问器实现。
系统具体设计
本章节按照“系统总体架构介绍”里面描述的模块顺序逐个介绍这些模块的具体实现策略。
请求监听层:
请求监听模块:
设计请求监听模块涉及到设计我们所使用的具体的网络协议的格式,在初步的设计里面,我们使用 .Net对于 WinSocket的封装来实现网络数据的传输,使用TCP/IP协议。在TCP的payload里面传输我们的请求消息和响应消息。消息格式如下:
其中各个字段的含义如下:
Command Value: 用于指示操作码,如果系统最高位是0,表明这是系统定义的操作码;如果最高位是1,则表明是用户定义的操作码。对于每一个特定的操作码,系统应该提供一个该操作码下的Request Data的解析函数;所以,如果用户需要定义自己的操作码,那么他也应该提供该操作码下的Request Data的解析函数实现。解析函数有特定的格式如下:
public Dictionary
RequestDataParser(ushort commandValue, ushort flags, Guid sessionId, byte[] requestData);
其输入为一个完整的包结构,输出为解析出来的Request Data中包含的有意义的字段对应C#中的数据类型,以及这些数据的值。这样子就是得我们能在我们的请求监听层初始化的时候根据配置文件中的解析函数配置注册合适的解析函数,在运行中能够调用注册好的配置函数解析对应的网络数据将其转换为有意义的逻辑数据。
在这种设计之下,用户如果需要定义自己的Command Value,假设其值为0xF000,那么用户提供一个叫做CustomeRequestDataParser.dll的程序集,其中包含了对于Command Value0xF0000下的Request Data的解析函数实现,其实现如下:
public class CustomeRequestDataParser
{
public Dictionary RequestDataParser(ushort commandValue, ushort flags, Guid sessionId, byte[] requestData)
{
Dictionary result = new Dictionary();
if (requestData.Length != 8)
{
throw new ArgumentException("Invalid network data received.");
}
byte[] field1 = new byte[4];
byte[] field2 = new byte[4];
Array.Copy(requestData, field1, 4);
Array.Copy(requestData, field2, 4, 4);
result.Add(
typeof(uint),
BitConverter.ToUInt32(field1, 0));
result.Add(typeof(uint), BitConverter.ToUInt32(field2, 0));
return result;
}
}
在我们的请求监听层的程序配置文件中存在这么一项:
在我们的请求监听层的初始化代码中存在这么几行代码:
public void RegisterRequestDataParsers()
{
System.Collections.Specialized.NameObjectCollectionBase.KeysCollection keysCOllection = ConfigurationSettings.AppSettings.Keys;
List requestDataParserKeys = new List();
foreach (string keyValue in keysCOllection)
{
if (keyValue.Contains("CommandValueParser"))
{
requestDataParserKeys.Add(keyValue);
}
}
foreach (string keyValue in requestDataParserKeys)
{
string assemblyPath = GetAssemblyPath(ConfigurationSettings.AppSettings[keyValue]);
if (!IsAssemblyLoaded(assemblyPath))
{
AppDomain.CurrentDomain.Load(assemblyPath);
}
string typeName = GetTypeName(ConfigurationSettings.AppSettings[keyValue]);
ushort commandValue=GetCommandValue(ConfigurationSettings.AppSettings[keyValue]);
if (!registeredRequestDataParsers.ContainsKey(commandValue))
{
registeredRequestDataParsers.Add(commandValue, Type.GetType(GetTypeFullName(typeName)));
}
}
}
当请求监听模块收到一个请求之后,它会将其转换为一个事件,将其放进事件队列中,一个事件应该包含以下信息:
public class Event
{
private DateTime timeStamp;
private ushort commandValue;
private ushort flags;
private Guid sessionId;
Dictionary requestData;
}
一个时间队列的可能定义如下:
public class EventQueue
{
private List eventPool;
private uint eventCount;
private Event mostPrioritizedEvent;
}
请求调度模块不断的查询事件队列中的时间,按照特定的调度策略找出一个合适的事件交给逻辑层处理:
public abstract class EventSelectionPolicy
{
}
public class EventQueueProcessor
{
private EventQueue eventQueue;
private EventSelectionPolicy eventSelectionPolicy;
public void RegisterEventQueue(EventQueue queue)
{
if (this.eventQueue != null)
{
throw new ArgumentException("Unable to register another event queue when a queue is still in processing.");
}
this.eventQueue = queue;
}
public EventQueue UnregisterEventQueue()
{
if (this.eventQueue == null)
{
throw new InvalidOperationException("Unable to unregister an event queue when the queue is not intialized.");
}
EventQueue queue = this.eventQueue;
this.eventQueue = null;
return queue;
}
public void RegisterEventSelectionPolicy(EventSelectionPolicy policy)
{
this.eventSelectionPolicy = policy;
}
public Event SelectEvent()
{
//
//return ...;
}
}
EventQueueProcessor调用SelectEvent方法按照注册过的调度策略选择一个event交给逻辑处理层处理。
后记
2009年7月24日
#
string assemblyName = this.txtAssemblyName.Text;
string className = this.txtClassName.Text;
System.Diagnostics.Debug.Assert(assemblyName != null && assemblyName != string.Empty);
System.Diagnostics.Debug.Assert(className != null && className != string.Empty);
System.Reflection.AssemblyName asmName = new System.Reflection.AssemblyName(assemblyName);
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("MyModule", true);
TypeBuilder typeBuilder = modBuilder.DefineType(className);
typeBuilder.DefineField("myfield",typeof(string), System.Reflection.FieldAttributes.Public);
MethodBuilder methodBuilder = typeBuilder.DefineMethod("CallMethod", System.Reflection.MethodAttributes.Public| System.Reflection.MethodAttributes.SpecialName | System.Reflection.MethodAttributes.HideBySig
, null, new Type[] { typeof(string) });
methodBuilder.DefineParameter(0, System.Reflection.ParameterAttributes.Retval, null);
methodBuilder.DefineParameter(1, System.Reflection.ParameterAttributes.In, "name");
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_1);
//System.Reflection.Assembly asmSystem = System.Reflection.Assembly.GetAssembly(typeof(MessageBox));
//Type typeMessageBox = asmSystem.GetType("MessageBox");
Type typeMessageBox = typeof(MessageBox);
System.Reflection.MethodInfo mInfoShow = typeMessageBox.GetMethod("Show",new Type[]{typeof(string)}
//System.Reflection.BindingFlags.Public| System.Reflection.BindingFlags.Static,
);
ilGenerator.EmitCall(OpCodes.Call, mInfoShow, null);
ilGenerator.Emit(OpCodes.Ret);
Type customType = typeBuilder.CreateType();
object customeObj = Activator.CreateInstance(customType); ;
System.Reflection.MethodInfo methodInfo = customType.GetMethod("CallMethod");
//customType.InvokeMember("CallMethod", System.Reflection.BindingFlags.Public, null, customeObj, new object[] { "123" });
//methodBuilder.Invoke(customeObj, new object[] { "123" });
methodInfo.Invoke(customeObj, new object[] { "11" });
Console.ReadLine();
2009年6月22日
#
原文地址:http://hi.baidu.com/niiuniu1127/blog/item/ba6e07fb28248e126d22eb8c.html
什么叫滤波:用白话讲就是,一个电信号中有若干种成分,把其中一部分交流信号过滤掉就叫滤波。
卷积和滤波的区别:在数字信号处理的理论中,卷给可以说是一种数学运算,而滤波是一种信号处理的方法。卷积就像加权乘法一样,你能说滤波和加权乘法是一样的吗,显然不行;但是滤波最终是有乘法来实现的。
自适应滤波就是滤波所用的模板系数会根据图像不同位置自动调整。
中值滤波(median filter)简单的说就是:一个窗(window)中心的象素值就是这个窗包含的象素中处于中间位置的象素值。
均值滤波(mean filter)就是一个窗中心的象素值就是这个窗包含的象素的平均值。
空间频率主要是指图像的平滑或粗糙程度。一般可认为,高空间频率区域称“粗糙”,即图像的亮度值在小范围内变化很大,而“平滑”区,图像的亮度值变化相对较小,如平滑的水体表面等。低通滤波主要用于加强图像中的低频成分,减弱高频成分,而高通滤波则正好相反,加强高频细节,减弱低频细节,简单地讲:高通滤波处理过的图像更加“粗糙”。高通滤波顾名思义就是让频率高的通过,使图像具有锐化效果;低通滤波则恰好相反了,它是使低频通过,使图像具有平滑的效果。
•模板的定义 –所谓模板就是一个系数矩阵 –模板大小:经常是奇数,如: 3x3 5x5 7x7 –模板系数: 矩阵的元素 w1 w2 w3 w4 w5 w6 w7 w8 w9
空域过滤及过滤器的定义:使用空域模板进行的图像处理,被称为空域过滤。
w1 w2 w3
w4 w5 w6
w7 w8 w9
模板本身被称为空域过滤器
空域过滤器的分类
按效果分:钝化过滤器,锐化过滤器
按数学形态分类
1,线性过滤器:使用乘积和的计算,例如:R = w1z1 + w2z2 + … + wnzn
高通:边缘增强、边缘提取
低通:钝化图像、去除噪音
带通:删除特定频率、增强中很少用
2,非线性过滤器:结果值直接取决于像素邻域的值
最大值:寻找最亮点,亮化图片
最小值:寻找最暗点,暗化图片
中值:钝化图像、去除噪音
钝化过滤器的主要用途
1,对大图像处理前,删去无用的细小细节
2,连接中断的线段和曲线
3,降低噪音
4,钝化处理,恢复过分锐化的图像
5,图像创艺(阴影、软边、朦胧效果)
缺点:
如果图像处理的目的是去除噪音,那么,低通滤波在去除噪音的同时也钝化了边和尖锐的细节,但是中值滤波算法的特点:在去除噪音的同时,可以比较好地保留边的锐度和图像的细节锐化过滤器的主要用途
1,印刷中的细微层次强调。弥补扫描、挂网对图像的钝化
2,超声探测成象,分辨率低,边缘模糊,通过锐化来改善
3,图像识别中,分割前的边缘提取
4,锐化处理恢复过度钝化、暴光不足的图像
5,图像创艺(只剩下边界的特殊图像)
6,尖端武器的目标识别、定位
过滤器效果的分析
1,常数或变化平缓的区域,结果为0或很小,图像很暗,亮度被降低了
2,在暗的背景上边缘被增强了
3,图像的整体对比度降低了
4,计算时会出现负值,归0处理为常见
基本高通空域滤波的缺点和问题
高通滤波在增强了边的同时,丢失了图像的层次和亮度
2009年6月4日
#
原文地址:http://dev.csdn.net/article/74/74935.shtm
WM_PAINT是Windows窗口系统中一条重要的消息,应用程序通过处理该消息实现在窗口上的绘制工作。
1. 系统何时发送WM_PAINT消息?
系统会在多个不同的时机发送WM_PAINT消息:当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,这些动作都是由 系统管理的,应用只是被动地接收该消息,在消息处理函数中进行绘制操作;大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。
系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢?这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽 可能地推后做。不过这样也有利于提高绘制的效率:两个WM_PAINT消息之间通过InvalidateRect和InvaliateRgn使之失效的区域就会被累加起来,然后在一个WM_PAINT消息中一次得到 更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效,依赖于系统在合适的时机发送WM_PAINT消息的机 制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以在无效化窗口区域后利用
SendMessage 发送一条WM_PAINT消息来强制立即重画
【注解:SendMessage会block到被发送的消息被处理完才返回,但是WM_PAINT消息的处理时间又是用户不可控制的:“GetMessage returns the WM_PAINT message when there are no other messages in the application's message queue, and DispatchMessage sends the message to the appropriate window procedure. ”(MSDN原文),那么也就是说,你调用SendMessage之后,这个方法需要等待多长时间才能返回是不可控制的。所以MSDN不推荐用户直接发送WM_PAINT消息:“The WM_PAINT message is generated by the system and should not be sent by an application”】
,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送WM_PAINT消息而不管Update Region是否为空等。
2. BeginPaint
BeginPaint和WM_PAINT消息紧密相关。试一试在WM_PAINT处理函数中不写BeginPaint会怎样?程序会像进入了一个死循环一样达到惊人的CPU占用率,你会发现程序总在处理一个接 一个的WM_PAINT消息。这是因为在通常情况下,当应用收到WM_PAINT消息时,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了),BeginPaint的一个作用就是把该Update Region置为空,这样如果不调用BeginPaint,窗口的Update Region就一直不为空,如前所述,系统就会一直发送WM_PAINT消息。
BeginPaint和WM_ERASEBKGND消息也有关系。当窗口的Update Region被标志为需要擦除背景时,BeginPaint会发送WM_ERASEBKGND消息来重画背景,同时在其返回信息里有一个标志表明窗口背景是否被重画过。当我们用InvalidateRect和InvalidateRgn来把指定区域加到Update Region中时,可以设置该区域是否需要被擦除背景,这样下一个BeginPaint就知道是否需要发送WM_ERASEBKGND消息了。
另外要注意的一点是,BeginPaint只能在WM_PAINT处理函数中使用。
2009年5月28日
#
将一副灰度图像转换为只包含为黑白两色的二值图像称之为图像的阈值处理;这种处理有多种方式,最简单的就是设定一个静态的阈值,色素值大于该阈值的为白,否则该值的为黑色。但是在很多时候,这种简单的方法并不能得到很好的效果。在这篇文章里面我们介绍一种很简单的局部自适应性图像阈值化处理方法,该方法根据图像局部特点动态的调整其阈值,能够得到比较理想的阈值化处理效果。
该方法的思想是首先将图像分为一定大小的区块,这个可以根据情况选用,比如你可以选择3*3的矩阵,或者5*5的矩阵。选定好这些小块之后,我们计算这个小块中包围了中心像素点的其他像素点(或者用户也可以加其他的限定条件选择像素点)的灰度值的某种统计信息,比如灰度均值,或者灰度中间值,或者最大灰度与最小灰度之间的均值作为中间像素点的阈值。如果中间像素点的灰度大于该阈值,则中间像素被转换为白色点;反之,则转换为黑色点。
以ROUND(x)代表像素点x周围的所有像素点,以函数Statistic(ROUND(x))表示对上述的这些像素点的灰度值做统计运算,那么我们的算法思想可以描述为:
1(x>Statistic(ROUND(x)))
x={
0(x<=Statistic(ROUND(x)))
在实际情况中,选用3*3的包围矩阵和取均值的统计算法可以得到比较理想的处理效果(处理效果的图片对比如下:
原图:
处理后图片:
)。
2009年5月22日
#
原文地址:
http://www.xfocus.net/articles/200503/782.html创建时间:2005-03-09
文章属性:转载
文章提交:
cisocker (cisocker_at_163.com)
by sunwear [E.S.T]
2004/10/02
shellcoder@163.com
此文只能说是一篇笔记,是关于本机API的.本机API是除了Win32 API,NT平台开放了另一个基本接口。本机API也被很多人所熟悉,因为内核模式模块位于更低的系统级别,在那个级别上环境子系统是不可见的。尽管如此,并不需要驱动级别去访问这个接口,普通的Win32程序可以在任何时候向下调用本机API。并没有任何技术上的限制,只不过微软不支持这种应用开发方法。
User32.dll,kernel32.dll,shell32.dll,gdi32.dll,rpcrt4.dll,comctl32.dll,advapi32.dll,version.dll等dll代表了Win32 API的基本提供者。Win32 API中的所有调用最终都转向了ntdll.dll,再由它转发至ntoskrnl.exe。ntdll.dll是本机 API用户模式的终端。真正的接口在ntoskrnl.exe里完成。事实上,内核模式的驱动大部分时间调用这个模块,如果它们请求系统服务。Ntdll.dll的主要作用就是让内核函数的特定子集可以被用户模式下运行的程序调用。Ntdll.dll通过软件中断int 2Eh进入ntoskrnl.exe,就是通过中断门切换CPU特权级。比如kernel32.dll导出的函数DeviceIoControl()实际上调用ntdll.dll中导出的NtDeviceIoControlFile(),反汇编一下这个函数可以看到,EAX载入magic数0x38,实际上是系统调用号,然后EDX指向堆栈。目标地址是当前堆栈指针ESP+4,所以EDX指向返回地址后面一个,也就是指向在进入NtDeviceIoControlFile()之前存入堆栈的东西。事实上就是函数的参数。下一个指令是int 2Eh,转到中断描述符表IDT位置0x2E处的中断处理程序。
反编汇这个函数得到:
mov eax, 38h
lea edx, [esp+4]
int 2Eh
ret 28h
当然int 2E接口不仅仅是简单的API调用调度员,他是从用户模式进入内核模式的main gate。
W2k Native API由248个这么处理的函数组成,比NT 4.0多了37个。可以从ntdll.dll的导出列表中很容易认出来:前缀Nt。Ntdll.dll中导出了249个,原因在于NtCurrentTeb()为一个纯用户模式函数,所以不需要传给内核。令人惊奇的是,仅仅Native API的一个子集能够从内核模式调用。而另一方面,ntoskrnl.exe导出了两个Nt*符号,它们不存在于ntdll.dll中: NtBuildNumber, NtGlobalFlag。它们不指向函数,事实上,是指向ntoskrnl.exe的变量,可以被使用C编译器extern关键字的驱动模块导入。Ntdll.dll和ntoskrnl.exe中都有两种前缀Nt*,Zw*。事实上ntdll.dll中反汇编结果两者是一样的。而在ntoskrnl.exe中,nt前缀指向真正的代码,而zw还是一个int 2Eh的stub。也就是说zw*函数集通过用户模式到内核模式门传递的,而Nt*符号直接指向模式切换以后的代码。Ntdll.dll中的NtCurrentTeb()没有相对应的zw函数。Ntoskrnl并不导出配对的Nt/zw函数。有些函数只以一种方式出现。
2Eh中断处理程序把EAX里的值作为查找表中的索引,去找到最终的目标函数。这个表就是系统服务表SST,C的结构SYSTEM_SERVICE_TABLE的定义如下:清单也包含了结构SERVICE_DESCRIPTOR_TABLE中的定义,为SST数组第四个成员,前两个有着特别的用途。
typedef NTSTATUS (NTAPI *NTPROC) ( ) ;
typedef NTPROC *PNTPROC;
#define NTPROC_ sizeof (NTPROC)
typedef struct _SYSTEM_SERVICE_TABLE
{ PNTPROC ServiceTable; // 这里是入口指针数组
PDWORD CounterTable; // 此处是调用次数计数数组
DWORD ServiceLimit ; // 服务入口的个数
PBYTE ArgumentTable; // 服务参数字节数的数组
) SYSTEM_SERVICE_TABLE ,
* PSYSTEM_SERVICE_TABLE ,
* * PPSYSTEM_SERVICE_TABLE ;
/ / _ _ _ _ _ _ _ _ _ _ _ _
typedef struct _SERVICE_DESCRIPTOR_TABLE
{ SYSTEM_SERVICE_TABLE ntoskrnl ; // ntoskrnl所实现的系统服务,本机的API}
SYSTEM_SERVICE_TABLE win32k; // win32k所实现的系统服务
SYSTEM_SERVICE_TABLE Table3; // 未使用
SYSTEM_SERVICE_TABLE Table4; // 未使用
} SERVICE_DESCRIPTOR_TABLE ,
* PSERVICE_DESCRIPTOR_TABLE,
* PPSERVICE_DESCRIPTOR_TABLE ;
ntoskrnl通过KeServiceDescriptorTable符号,导出了主要SDT的一个指针。内核维护另外的一个SDT,就是KeServiceDescriptorTableShadow。但这个符号没有导出。要想在内核模式组件中存取主要SDT很简单,只需两行C语言的代码:
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;
NTPROC为本机 API的方便的占位符,他类似于Win32编程中的PROC。Native API正常的返回应该是一个NTSTATUS代码,他使用NTAPI调用约定,它和_stdcall一样。ServiceLimit成员有在ServiceTable数组里找到的入口数目。在2000下,默认值是248。ArgumentTable为BYTEs的数组,每一个对应于ServiceTable的位置并显示了在调用者堆栈里的参数比特数。这个信息与EDX结合,这是内核从调用者堆栈copy参数到自己的堆栈所需的。CounterTable成员在free buid的2000中并没有使用到,在debug build中,这个成员指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。
可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有前四行是最重要的,对应那四个SDT成员。
运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为内核维护的第二个SDT。主要的区别在于后一个包含了win32k.sys的入口,前一个却没有。在这两个表中,Table3与Table4都是空的。Ntoskrnl.exe提供了一个方便的API函数。这个函数的名字为:
KeAddSystemServiceTable
此函数去填充这些位置。
2Eh的中断处理标记是KisystemService()。这也是ntoskrnl.exe没有导出的内部的符号,但包含在2k符号文件中。关于KisystemService的操作如下:
1 从当前的线程控制块检索SDT指针
2 决定使用SDT中4个SST的其中一个。通过测试EAX中递送ID的第12和13位来决定。ID在0x0000-0x0fff的映射至ntoskrnl表格,ID在
0x1000与0x1ffff的分配给win32k表格。剩下的0x2000-0x2ffff与
0x3000-0x3ffff则是Table3和Table4保留。
3 通过选定SST中的ServiceLimit成员检查EAX的0-11位。如果ID超过了范围,返回错误代码为STATUS_INVALID_SYSTEM_SERVICE。
4 检查EAX中的参数堆栈指针与MmUserProbeAddress。这是一个ntoskrnl导出的全局变量。通常等于0x7FFF0000,如果参数指针不在这个地址之下,返回STATUS_ACCESS_VIOLATION。
5 查找ArgumentTable中的参数堆栈的字节数,从调用者的堆栈copy所有的参数至当前内核模式堆栈。
6 搜索serviceTable中的服务函数指针,并调用这个函数。
7 控制转到内部的函数KiserviceExit,在此次服务调用返回之后。
从对SDT的讨论可以看到与本机API一起还有第二个内核模式接口。这个接口把Win32子系统的图形设备接口和窗口管理器和内核模式组件Win32k连接起来。Win32k接口一样是基于int 2eh。本机API的服务号是从0x0000到0x0fff,win32k的服务号是从0x1000到0x1fff。(ddW32pServiceTable认定win32k.sys的符号可用。)win32k总共包含639个系统服务。
2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。
而是一个与线程相关的指针。显然,线程可以有不同得SDT相关到自身。线程初试化的时候,KeInitializeThread()把KeServiceDescriptorTable写到线程的控制块。尽管这样,这个默认设置之后可能被改变为其它值,例如KeServiceDescriptorTableShadow。
Windows 2000运行时库
Ntdll.dll至少导出了不少于1179个符号。其中的249/248是属于Nt*/zw*集合。所以还有682个函数不是通过int 2eh门中转。很显然,这么多的函数不依靠2k的内核。
其中一些是和c运行时库几乎一样的函数。其实ntoskrnl也实现了一些类似C运行时库的一些函数。可以通过ddk里的ntdll.lib来链接和使用这些函数。反汇编ntdll.dll与ntoskrnl.exe的C运行时函数能发现,ntdll.dll并不是依赖ntoskrnl.exe。这两个模块各自实现了这些函数。
除了C运行时库外,2000还提供了一个扩展的运行时函数集合。再一次,ntdll.dll与ntoskrnl.exe各自实现了它们。同样,实现集合有重复,但是并不完全匹配。这个集合的函数都是以Rtl开头的。2000运行时库包括一些辅助函数用于C运行时候无法完成的任务。例如有些处理安全事务,另外的操纵2000专用的数据结构,还有些支持内存管理。微软仅仅在DDK中记录了很有用的406个函数中的115个函数。
Ntdll.dll还提供了另外一个函数集合,以__e前缀开头。实际上它们用于浮点数模拟器。
还有很多的函数集合,所有这些函数的前缀如下:
__e(浮点模拟),Cc(Cache管理),Csr(c/s运行时库),Dbg(调试支持),Ex(执行支持),FsRtl(文件系统运行时),Hal(硬件抽象层),Inbv(系统初试化/vga启动驱动程序bootvid.dll),Init(系统初试化),Interlocked(线程安全变量操作),Io(IO管理器),Kd(内核调试器支持),Ke(内核例程),Ki(内核中断处理),Ldr(映象装载器),Lpc(本地过程调用),Lsa(本地安全授权),Mm(内存管理),Nls(国际化语言支持),Nt(NT本机API),Ob(对象管理器),Pfx(前缀处理),Po(电源管理),Ps(进程支持),READ_REGISTER_(从寄存器地址读),Rtl(2k运行时库),Se(安全处理),WRITE_REGISTER_(写寄存器地址),Zw(本机API的替换叫法),<其它>(辅助函数和C运行时库)。
当编写从用户模式通过ntdll.dll或内核模式通过ntoskrnl.exe和2000内核交互的软件的时候,需要处理很多基本的数据结构,这些结构在Win32世界中很少见到。
常用数据结构
l 整数
ANSI字符是有符号的,而Unicode WCHAR是无符号的
MASM的TBYTE是80位的浮点数,用于高精度浮点运算单元操作,注意它与Win32的TBYTE(text byte)完全不同。
TABLE 2-3. Equivalent Integral Data Types
BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED
8 BYTE unsigned char UCHAR CHAR
16 WORD unsigned short USHORT WCHAR SHORT
32 DWORD unsigned long ULONG LONG
32 DWORD unsigned int UINT INT
64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG
80 TBYTE N/A
typedef union _LARGE_INTEGER
{ struct{
ULONG LowPart;
LONG HighPart;};
LONGLONG QuadPart;
}
LARGE_INTEGER , * PULARGE_INTEGER ;
typedef union _ULARGE_INTEGER{
struct{
ULONG LowPart;
ULONG HighPart;}
ULONGLONG QuadPart;
}ULARGE_INTEGER, *PULARGE_INTEGER;
l 字符
Win32编程中PSTR用户CHAR*,PWSTR用于WCHAR*。取决于是否定义了UNICODE,PTSTR解释为PSTR或者PWSTR。在2k内核模式下,常用的数据类型是UNICODE_STRING,而STRING用来表示ANSI字符串:
typedef struct _UNICODE_STRING{
USHORT Length; //当前字节长度,不是字符!!!
USHORT MaximumLength; //Buffer的最大字节长度
PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;
typedef struct _STRING{
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;}STRING, *PSTRING;
typedef STRING ANSI_STRING, *PANSI_STRING;
typedef STRING OEM_STRING, *POEM_STRING;
操纵函数:RtlCreatUnicodeString(),RtlInitUnicodeString(),
RtlCopyUnicodeString()等等
l 结构
许多内核API函数需要一个固定大小的OBJECT_ATTRIBUTES结构,比如NtOpenFile()。对象的属性是OBJ_*值的组合,可以从ntdef.h中查到。
IO_STATUS_BLOCK结构提供了所请求操作结果的信息,很简单,status成员包含一个NTSTATUS代码, 如果操作成功 information成员提供特定请求的信息。
还有一个结构是LIST_ENTRY,这是一个双向环链表。
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;
typedef struct _IO_STATUS_BLOCK
{
NTSTATDS Status;
ULONG Information;
}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;
typedef struct _LIST_ENTRY
{
Struct _LIST_ENTRY *Flink;
Struct _LIST_ENTRY *Blink;
}LIST_ENTRY, *PLIST_ENTRY;
双向链表的典型例子就是进程和线程链。内部变量PsActiveProcessHead是一个LIST_ENTRY结构,在ntoskrnl.exe的数据段中,指定了系统进程列表的第一个成员。
CLIENT_ID结构由进程和线程ID组成。
typedef struct _CLIENT_ID
{ HANDLE UniqueProcess;
HANDLE UniqueThread;
)CLIENT_ID, *PCLIENT_ID;
想要从用户模式调用ntdll.dll中的API函数,必须考虑到以下四点:
1 SDK头文件没有包括这些函数的原型
2 这些函数使用的若干基本数据类型没有包括在SDK文件中
3 SDK和DDK头文件不兼容,不能在win32的c源文件包含ntddk.h中
4 ntdll.lib没有包括在VC的默认导入库列表中。
第4个很容易解决:#progma comment(linker,“/defaultlib:ntdll.lib”)
缺失的定义比较难解决,最简单的方法是写一个自定义的头文件,刚刚包含需要调用ntdll.dll中函数的定义。幸运的是,已经在光盘的w2k_def.h文件中做了这个工作。因为这个头文件将用于用户模式和内核模式程序,所以必须在用户模式代码中,#include<w2k_def.h>之前#define _USER_MODE_,使得DDK中出现而SDK中没有的定义可用。
本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的指导
2009年5月21日
#
原文地址:
http://club.it.sohu.com/read_art_sub.new.php?b=os&a=114021&NoCache=1打开NTDLL.dll,惊奇的发现原来CRT的许多基本函数居然都是在这里实现的!甚至包括qsort,ceil这样的函数,还有臭名昭著的strcpy(严格来讲,这只能怪使用者不当心)。堆的释放,进城管理,似乎都是在这。于是,我决定,仔细察看以下它,这1410个函数是做什么的
用户模式的代码在调用系统内核函数的时候,首先把一个叫做system call number的数放在EAX中,把参数放在其它的寄存器中了。然后调用INT 2E中断。但是大部分应用程序并不需要直接这么做。通常都是在调用kernel32.dll等时由他们来调用INT 2E.
内核模式的代码,做法稍微有点不同。他们通常调用由NTOSKRNL.EXE导出的NTDLL.dll中Zw开头的那一系列函数,比如ZwWaitForSingleObject, 反之,如果是用户级代码需要调用内核,就会利用INT 2E调用WaitForSingleObject.对于许多函数的批量调用,你会明显发现 Zw族要比Rtl族效率高很多。
可惜ntdll.dll中的大部分函数都是undocumented.
对于一部分得知其定义形式的函数,可以这样调用:
1.先将NTDLL.DLL读入 LoadLibrary(TEXT("NTDLL.dll"))
2.利用GetProcAddress 获取其函数入口地址
3.利用得到的函数指针调用
但是可以大致的分为几类吧
1 PropertyLengthAsVariant 它被排在了第一号,但是我就是不明白它是做什么的
2 Csr(configuration status register? Command and Status Register?)系列
CsrAllocateCaptureBuffer CsrAllocateMessagePointer CsrCaptureMessageBuffer CsrCaptureMessageMultiUnicodeStringsInPlace
CsrCaptureMessageString CsrCaptureTimeout CsrClientCallServer CsrClientConnectToServer CsrFreeCaptureBuffer
CsrGetProcessId CsrIdentifyAlertableThread CsrNewThread CsrProbeForRead CsrProbeForWrite CsrSetPriorityClass
3 Dbg系列 调试函数
DbgBreakPoint DbgPrint DbgPrintEx DbgPrintReturnControlC DbgPrompt DbgQueryDebugFilterState DbgSetDebugFilterState
DbgUiConnectToDbg DbgUiContinue DbgUiConvertStateChangeStructure DbgUiDebugActiveProcess DbgUiGetThreadDebugObject
DbgUiIssueRemoteBreakin DbgUiRemoteBreakin DbgUiSetThreadDebugObject DbgUiStopDebugging DbgUiWaitStateChange DbgUserBreakPoint
4 ki系列
KiRaiseUserExceptionDispatcher
KiUserApcDispatcher
KiUserCallbackDispatcher
KiUserExceptionDispatcher
5 Ldr系列 Loader APIs,共34个
API
NTDLL APIs
LoadResource
LdrAccessResource
LdrAlternateResourcesEnabled
DisableThreadLibraryCalls
LdrDisableThreadCalloutsForDll
LdrEnumResources
LdrFindAppCompatVariableInfo
LdrFindEntryForAddress
EnumResourceTypesW
LdrFindResourceDirectory_U
FindResourceExA
LdrFindResource_U
LdrFlushAlternateResourceModules
LdrGetAlternateResourceModuleHandle
GetModuleHandleForUnicodeString
LdrGetDllHandle
GetProcAddress
LdrGetProcedureAddress
LdrInitializeThunk
LoadLibraryEx (LOAD_LIBRARY_AS_DATAFILE)
LdrLoadAlternateResourceModule
LoadLibrary
LdrLoadDll
LdrProcessRelocationBlock
LdrQueryApplicationCompatibilityGoo
LdrQueryImageFileExecutionOptions
LdrQueryProcessModuleInformation
LdrRelocateImage
ExitProcess
LdrShutdownProcess
ExitThread
LdrShutdownThread
LdrUnloadAlternateResourceModule
FreeLibrary
LdrUnloadDll
LdrVerifyImageMatchesChecksum
LdrVerifyMappedImageMatchesChecksum
6 Nls(National Language Support)系列 代码页管理
NlsAnsiCodePage
NlsMbCodePageTag
NlsMbOemCodePageTag
7 Nt系列 共285个,大部分都是kernel32.dll,user32.dll等的核心实现
NtCreateFile, NtOpenFile, NtClose, NtWaitForSingleObject 是kernel32.dll中许多用户级代码的核心实现。
NTSTATUS NtClose( HANDLE Handle);
竟然是CloseHandle 的原身!唯一的缺点是该函数并没有导出库,如果要调用,就必须使用GetProcAddress 来获得其函数指针然后调用。
NtCreateFile 可以说是DDK的核心
RtlUnwind initiates an unwind of procedure call frames
结构化异常(Structured Exception Handling, SEH )的 核心。
NTSTATUS NtWaitForSingleObject( HANDLE Handle, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
Waits until the specified object attains a state of signaled
我想,信号同步等,应该与之有莫大的联系吧
8 pfx 不明白
PfxFindPrefix
PfxInitialize
PfxInsertPrefix
PfxRemovePrefix
9 RestoreEm87Context SaveEm87Context
10 rtl系列 共506个。我想,rtl应该是runtime library的缩写吧。一个很庞大的函数族,
里面包含像 RtlCreateUserProcess 这样的一些很基本的函数,通常供内核模式的driver等调用
下面是一部分示例
APIs Forwarded to NTDLL
API
Destination
DeleteCriticalSection
Forwarded to NTDLL.RtlDeleteCriticalSection
EnterCriticalSection
Forwarded to NTDLL.RtlEnterCriticalSection
HeapAlloc
Forwarded to NTDLL.RtlAllocateHeap
HeapFree
Forwarded to NTDLL.RtlFreeHeap
HeapReAlloc
Forwarded to NTDLL.RtlReAllocateHeap
HeapSize
Forwarded to NTDLL.RtlSizeHeap
LeaveCriticalSection
Forwarded to NTDLL.RtlLeaveCriticalSection
RtlFillMemory
Forwarded to NTDLL.RtlFillMemory
RtlMoveMemory
Forwarded to NTDLL.RtlMoveMemory
RtlUnwind
Forwarded to NTDLL.RtlUnwind
RtlZeroMemory
Forwarded to NTDLL.RtlZeroMemory
SetCriticalSectionSpinCount
Forwarded to NTDLL.RtlSetCriticalSection- SpinCount
TryEnterCriticalSection
Forwarded to NTDLL.RtlTryEnterCriticalSection
VerSetConditionMask
Forwarded to NTDLL.VerSetConditionMask
11 VerSetConditionMask 用于确认系统信息
The VerSetConditionMask function sets the bits of a 64-bit value to indicate the comparison operator to use for a specified operating system version attribute.
This function is used to build the dwlConditionMask parameter of the VerifyVersionInfo function.
12 Zw系列 共284个。前面已经说过,为可执行性系统服务提供内核模式的入口, 为NTOSKRNL.EXE 提供实现。由于是内核模式,所以在执行的时候并不检查用户是否有执行权限
13 内部函数 共116个。具体作用不明,很底层的东西。无法查到任何相关资料。无法得知与其相关的任何信息。
_CIcos
_CIlog
_CIpow
_CIsin
_CIsqrt
__eCommonExceptions
__eEmulatorInit
__eF2XM1
__eFABS
__eFADD32
__eFADD64
__eFADDPreg
__eFADDreg
__eFADDtop
__eFCHS
__eFCOM
__eFCOM32
__eFCOM64
__eFCOMP
__eFCOMP32
__eFCOMP64
__eFCOMPP
__eFCOS
__eFDECSTP
__eFDIV32
__eFDIV64
__eFDIVPreg
__eFDIVR32
__eFDIVR64
__eFDIVRPreg
__eFDIVRreg
__eFDIVRtop
__eFDIVreg
__eFDIVtop
__eFFREE
__eFIADD16
__eFIADD32
__eFICOM16
__eFICOM32
__eFICOMP16
__eFICOMP32
__eFIDIV16
__eFIDIV32
__eFIDIVR16
__eFIDIVR32
__eFILD16
__eFILD32
__eFILD64
__eFIMUL16
__eFIMUL32
__eFINCSTP
__eFINIT
__eFIST16
__eFIST32
__eFISTP16
__eFISTP32
__eFISTP64
__eFISUB16
__eFISUB32
__eFISUBR16
__eFISUBR32
__eFLD1
__eFLD32
__eFLD64
__eFLD80
__eFLDCW
__eFLDENV
__eFLDL2E
__eFLDLN2
__eFLDPI
__eFLDZ
__eFMUL32
__eFMUL64
__eFMULPreg
__eFMULreg
__eFMULtop
__eFPATAN
__eFPREM
__eFPREM1
__eFPTAN
__eFRNDINT
__eFRSTOR
__eFSAVE __eFSCALE __eFSIN __eFSQRT __eFST __eFST32 __eFST64 __eFSTCW __eFSTENV __eFSTP __eFSTP32 __eFSTP64 __eFSTP80
__eFSTSW __eFSUB32 __eFSUB64 __eFSUBPreg __eFSUBR32 __eFSUBR64 __eFSUBRPreg __eFSUBRreg __eFSUBRtop __eFSUBreg __eFSUBtop
__eFTST __eFUCOM __eFUCOMP __eFUCOMPP __eFXAM __eFXCH __eFXTRACT __eFYL2X __eFYL2XP1 __eGetStatusWord
14 一些CRT的基本函数 共131个 主要是字符串管理,还有些基本的数学函数
__isascii __iscsym __iscsymf __toascii _alldiv _alldvrm _allmul _alloca_probe _allrem _allshl _allshr _atoi64 _aulldiv _aulldvrm _aullrem _aullshr _chkstk _fltused
_ftol _i64toa _i64tow _itoa _itow _lfind _ltoa _ltow _memccpy _memicmp _snprintf _snwprintf _splitpath _strcmpi _stricmp _strlwr _strnicmp _strupr _tolower
_toupper _ui64toa _ui64tow _ultoa _ultow _vsnprintf _vsnwprintf _wcsicmp _wcslwr _wcsnicmp _wcsupr _wtoi _wtoi64 _wtol abs atan atoi atol bsearch ceil
cos fabs floor isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper iswalpha iswctype iswdigit iswlower iswspace iswxdigit isxdigit labs log
mbstowcs memchr memcmp memcpy memmove memset pow qsort sin sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk
strrchr strspn strstr strtol strtoul swprintf tan tolower toupper towlower towupper vDbgPrintEx vDbgPrintExWithPrefix vsprintf wcscat wcschr wcscmp wcscpy
wcscspn wcslen wcsncat wcsncmp wcsncpy wcspbrk wcsrchr wcsspn wcsstr wcstol wcstombs wcstoul