一个想法

zerray | 六月 23rd, 2005 - 22:29

网络防火墙是监控主机接收和发送的数据包,设定各种各样的rule,进而保证系统的安全。

病毒防火墙(实时监控)是根据病毒特征码来检查是否有病毒正要运行。

那么可不可以把网络防火墙的思想用在防病毒上呢?

例如实现一个 API 防火墙,对于每个正在运行的程序,监控它要调用的 API ,对于一些有可能造成破坏的 API 调用弹出提示,由用户决定是否允许调用。这样,一些像 RegCreateKey,WriteProcessMemory,CreateRemoteThread 等病毒常用的函数需要经过用户允许才能调用,对于病毒的发现和预防都很有用。实现的话用 Hook API 就可以吧?不知道对系统资源消耗有多大。

这只是我自己的空想,可能没什么价值,也可能已经有这样的软件了。欢迎感兴趣的朋友们跟我讨论:)

如何实现防火墙钩子驱动

zerray | 六月 20th, 2005 - 21:33

介绍

也许,防火墙钩子驱动是在 Windows 系统下开发数据包过滤程序的方法中最没有文档可查的一个了。微软没有给出关于它的任何文档,你唯一能够找到点儿什么的地方是 DDK 头文件(ipFirewall.h)。事实上,当我安装了 Windows 2000 DDK,我对于发现这个 .h 文件(和它的内容)非常惊奇,因为没有文档提到防火墙钩子的存在。在下一个版本的 DDK 中,微软加了一些关于它的文档:“这个方法存在但不建议使用”。

然而,因为它是个实现防火墙的简单方法,我认为了解防火墙钩子驱动如何工作将是很有趣的。

防火墙钩子驱动

我不明白为什么微软不建议使用防火墙钩子驱动的开发。对于开发一个完整的防火墙方案我的确不建议用它,但对于小程序,它可是个好的选择。基本上,防 火墙钩子驱动能够做过滤钩子驱动(见我的文章《在 Windows 2000/XP下开发防火墙》)所做的相同工作,但限制更少。

可能你还记得,过滤钩子函数仅允许在系统中安装一个过滤函数。如果有程序已经使用了这个功能,你的程序就没用了。使用防火墙钩子驱动,你不会遇到这 个问题。你可以安装你需要的所有过滤函数。每个过滤函数被赋予一个优先级,于是系统会一个接一个的(以优先级顺序)调用过滤函数知道一个函数返回 “DROP PACKET(丢弃包)”。如果所有的函数都返回“ALLOW PACKET(允许包)”,这个包就被允许通过了。你可以把它想象称一个过滤函数链。当其中一个返回了“DROP PACKET”链就被切断了。链中每个函数的顺序是由它的优先级值数给出的。

在上图中,我表示了以下过程:

  1. 你的主机收到了一个包。IP 驱动拥有以优先级排列的过滤函数列表(具有更高优先级的函数是 Filter Function 1)。
  2. 首先,IP 驱动将包传递给优先级最高的过滤函数并等待返回值。
  3. Filter function 1 返回“ALLOW PACKET”。
  4. 因为 Filter function 1 允许了包,IP 驱动将包传递给了下一个过滤函数:Filter function 2。
  5. 在图示情况下,Filter function 2 返回了“DROP PACKET”。所以,IP 驱动丢弃了包而不在继续调用下一个过滤函数。

你会发现的过滤钩子驱动的另一个问题是对于包的发送,你不能访问包中的数据内容。但是,用防火墙钩子驱动你可以访问所有的数据。防火墙钩子过滤函数 接收到的数据结构比过滤钩子驱动的更复杂。它更接近于 NDIS 驱动中包的结构,整个的包是由一个缓冲链组成的。但是请耐心点,你将在后面看到更多。

像过滤钩子驱动一样,防火墙钩子驱动只是个用来安装回调函数的内核模式驱动(但防火墙钩子驱动 在 IP 驱动中安装回调函数)。实际上,安装防火墙钩子驱动的过程与安装过滤钩子驱动类似。在 ipFirewall.h 文件中,你会看到下面的内容:

typedef struct _IP_SET_FIREWALL_HOOK_INFO
{
    // Packet filter callout. 
    IPPacketFirewallPtr FirewallPtr;

    // Priority of the hook
    UINT Priority;

       // if TRUE then ADD else DELETE 
       BOOLEAN Add;
} IP_SET_FIREWALL_HOOK_INFO, *PIP_SET_FIREWALL_HOOK_INFO;   

#define DD_IP_DEVICE_NAME L\\Device\\Ip
#define _IP_CTL_CODE(function, method, access) \
        CTL_CODE(FSCTL_IP_BASE, function, method, access)
#define IOCTL_IP_SET_FIREWALL_HOOK \
        _IP_CTL_CODE(12, METHOD_BUFFERED, FILE_WRITE_ACCESS)

这几行代码告诉你了安装回调函数的大致方法。你只需用你的回调函数的数据填写 IP_SET_FIREWALL_HOOK_INFO 结构并安装它发送 IOCTL IOCTL_IP_SET_FIREWALL_HOOK 到 IP 设备。简单,如果你与驱动程序打过交道或是曾做过那个有文档的过滤钩子函数。这个结构的一个重要参数是优先级域。每个优先级域包含了该过滤函数的优先级, 其值越来越大。

PDEVICE_OBJECT ipDeviceObject=NULL;
IP_SET_FIREWALL_HOOK_INFO filterData; 

//..... 

// Init structure filterData.
FirewallPtr = filterFunction;
filterData.Priority = 1;
filterData.Add = TRUE; 

//.... 

// Send the commando to ip driver
IoCallDriver(ipDeviceObject, irp);
如果你要卸载过滤函数,可以用同样的代码,只要把 filterData.Add 的值改成 FALSE 就行了。

过滤函数

防火墙钩子驱动的过滤函数比过滤钩子驱动的更复杂。因为没有关于该函数及其参数的文档,所以复 杂度增加了。该函数具有以下特征:

FORWARD_ACTION cbFilterFunction(VOID **pData,
                                     UINT RecvInterfaceIndex,
                                     UINT *pSendInterfaceIndex,
                                     UCHAR *pDestinationType,
                                     VOID *pContext, 
                                     UINT ContextLength,
                                     struct IPRcvBuf **pRcvBuf);

用耐心和调试方法(还有参数名字的解释:)),我得出了以下关于参数的信息:

pData *pData 指向一个带有包缓冲的 (struct IPRcvBuf *) 结构。
RecvInterfaceIndex 数据接收的接口。
pSendInterfaceIndex 指向无符号整型的指针,包含数据发送的索引值。虽然它是个指针,改变它的值并不能让包改变路径:(。
pDestinationType 指向无符号整型的指针,指出目标类型:本地网络,远程,广播,多播,等。
pContext 指向 FIREWALL_CONTEXT_T 结构的指针,使你可以像流入和流出的包一样取得包的信息。
ContextLength 指向的缓冲区的大小。其值总是 sizeof(FIREWALL_CONTEXT_T)。
pRcvBuf *pRcvBuf 总是指向 NULL。

这些信息可能会在将来的 Windows 版本中改变,因为没有官方的文档可用。我只能保证这是我在 Windows 2000 和 Windows XP 下测试得到的这些域的意义。

对于每个包,我们的函数会被调用,并且取决于它的返回值,包会被丢弃或是放行。在过滤函数中你可以返回的值有:

FORWARD 包被允许。
DROP 包被丢弃。
ICMP_ON_DROP 包被丢弃并有一个 ICMP 包被发送到远程主机。

释放缓冲

在防火墙钩子过滤函数中,你并不是像在过滤钩子驱动中那样直接接收带有包头部和内容的缓冲区。 在一些测试后,我弄清了缓冲的内部结构。如我前面所说,发送/接收 包是被传递给 pData 参数的。*pData 指向一个 IPRcvBuf 结构:

struct IPRcvBuf
{
    // Point to the next buffer in the chain
    struct IPRcvBuf *ipr_next;

    // Always 0 
    UINT ipr_owner;

    // Buffer data
    UCHAR *ipr_buffer;

    // Buffer data size
    UINT ipr_size;

    // In my tests always a pointer to NULL.
    // Maybe the system could use MDLs instead of IPRcvBuf structures (but
    // i never have seen it).
    PMDL ipr_pMdl;

    // Always a pointer to NULL.
    UINT *ipr_pClientCnt;

    // Always a pointer to NULL.
    UCHAR *ipr_RcvContext;

    // Always 0. I suppose this field is a offset into buffer data
    // but because I haven't a value different from 0, I can affirm it.
    UINT ipr_RcvOffset;

    // In Windows 2003 DDK the name of this field have changed to flags.
    // In my tests I always get 0 value for local traffic and 2 for remote.
    // It's the only thing I can tell you about this field.
       ULONG ipr_promiscuous;
};

按照我们的意图,只需要知道域 ipr_next, ipr_buffer 和 ipr_size。ipr_buffer 域包含包的 ipr_size 字节。然而,整个包不必在一个缓冲里,系统能够链接多个缓冲。正因如此,ipr_next 域被使用了。这个域指向了下一个带有包数据的结构。当数据结构中的 ipr_next 指向 NULL 时我们就得到了整个的数据包。所以,我们在防火墙钩子驱动中发现了链接缓冲结构正如在 NDIS 驱动中所见。在我的测试中,对于所有接收的包,函数只接收到了一个结构,在其缓冲中带有全部的数据。对发送的包,我发现若干个链接的缓冲,每个包含一种协 议的信息。我的意思是,例如我发送一个 ICMP 包,就会有三个链接的缓冲:一个带有 IP 头部,一个带有 ICMP 头部,而另一个带有数据。然而,就像我们对 NDIS 驱动做的一样,我们不能依赖于系统如何填写这些缓冲。

在下面的图中,你可以看到在防火墙钩子驱动中如何构建数据包的例子:

简单的,你可以由下面的代码看到如何从链接缓冲中得到一个带有数据包内容的正统缓冲:

char *pPacket = NULL;
int iBufferSize;
struct IPRcvBuf *pBuffer = (struct IPRcvBuf *) *pData;

// First, I calculate the total size of the packet
iBufferSize = buffer->ipr_size;
while(pBuffer->ipr_next != NULL)
{
    pBuffer = pBuffer->ipr_next;
    iBufferSize += pBuffer->ipr_size;
}

// Reserve memory to the lineal buffer.
pPacket = (char *) ExAllocatePool(NonPagedPool, iBufferSize);
if(pPacket != NULL)
{
    unsigned int iOffset = 0;
    pBuffer = (struct IPRcvBuf *) *pData;

    // we are going to copy each buffer of the chain in the lineal buffer.
    memcpy(pPacket, pBuffer->ipr_buffer, pBuffer->ipr_size);
    while(pBuffer->ipr_next != NULL)
    {
        iOffset += pBuffer->ipr_size;
        pBuffer = pBbuffer->ipr_next;
        memcpy(pPacket + iOffset, pBuffer->ipr_buffer,
                                  pBbuffer->ipr_size);
    }
}

还有,对于所有好奇的人(在你问到我之前:P),你可以修改包的数据,风险自己承担。没有任何工具来做这类的软件,要做类似的东西,我建议你实现一 个 NDIS IM 驱动或者 TDI 过滤驱动。我没有做太多测试但我不很信赖防火墙钩子驱动改变数据包内容的稳定性。为什么?因为我们不知道 IP 驱动怎样管理这些缓冲和修改它们要冒多大的风险。一句话,我建议:只许看,不许摸。

结合的时候到了!

好了,现在我们知道了过滤函数的语法和传给它的包的格式。现在,我们要知道的就剩如何把这两样 东西结合成一个数据包过滤程序了。我用的方法是试着定义一个类似于过滤钩子驱动中用到的过滤函数,因为这样更容易理解。由于传给过滤函数的参数在这两个驱 动中是不一样的,对于防火墙钩子,我实现了一个中间函数(实际的防火墙钩子过滤函数)来包裹过滤函数。我的意思是,在防火墙钩子过滤函数中,我用了一个函 数来处理包,将它拷贝到一个正统的缓冲中,然后把它传递给过滤函数。有下面的代码,我想你能更好的理解它:

FORWARD_ACTION cbFilterFunction(VOID **pData,
                                     UINT RecvInterfaceIndex,
                                     UINT *pSendInterfaceIndex,
                                     UCHAR *pDestinationType,
                                     VOID *pContext,
                                     UINT ContextLength,
                                     struct IPRcvBuf **pRcvBuf)
{
    FORWARD_ACTION result = FORWARD;
    char *pPacket = NULL;
    int iBufferSize;
    struct IPRcvBuf *pBbuffer =(struct IPRcvBuf *) *pData;
    PFIREWALL_CONTEXT_T fwContext = (PFIREWALL_CONTEXT_T)pContext;

    IPHeader *pIpHeader;

    // Convert chained buffer to lineal buffer as we see before.
    // This won't be the fastest code but
    // will help us to understand better the method.

    // ...........

    pIpHeader = (IPHeader *)pPacket;

    // Call the real filter function and return result     
    result = FilterPacket(pPacket,
                          // length in bytes = ipp->headerLength * (32 bits/8)
                          pPacket + (pIpHeader ->headerLength * 4),
                          iBufferSize - (pIpHeader ->headerLength * 4),
                          (fwContext != NULL) ? fwContext->Direction: 0,
                          RecvInterfaceIndex,
                          (pSendInterfaceIndex != NULL) ? *pSendInterfaceIndex : 0);

    return result;
}

代码

你能很快认出这篇文章的程序。是的,图形界面和我在过滤钩子驱动中用的完全一样。为什么?因为我写了个简单的数据包过滤程序用来测试我写的所有防火 墙方法。这样,我有了一个通用的图形界面给它们,来提供相同的功能,但在底层,它们工作的不一样。我有这个程序的不同版本(只做最小的改动)来测试我的过 滤钩子驱动,防火墙钩子驱动,LSP DLL,TDI 过滤驱动,NDIS 驱动……所以,我想这个方法是好理解的。几乎不更改图形界面,这样你只想了解使用的新方法。

像我其它的文章一样,这个程序只实现了包的过滤。很多人问我关于加入一些更多的功能,像包记录,安装为服务……但我想要按照提供方法而不是答案的思 想。如果你想要加入一些这样的功能,我很高兴:)。你可以联系我,问所有你想要的问题。

结论

好了,一旦我写完了这篇文章,它就是你的了。我希望你能从中学到与我一样多的东西。Enjoy it!!



在Windows 2000/XP下开发防火墙

zerray | 六月 20th, 2005 - 21:24

介绍

如果你决定在 Linux 下开发一个防火墙,你可以找到很多资料和源代码,全部免费。但对 Windows 平台下的防火墙感兴趣的人们就有些困难了,不仅是找资料难,找免费的源代码更是个不可能的任务!

所以,我决定写这片文章来简述一下在 Windows 2000/XP 下开发防火墙的简单方法,借以帮助那些对此感兴趣的人们。

背景

在 Windows 2000 DDK 中,微软包含了一种新的网络驱动叫做过滤钩子驱动( Filter-Hook Driver )。使用它,你可以建立一个函数来过滤所有到达或离开接口的数据包。

因为关于这个主题的文档很少,并且没有例子,所以我写了这篇文章来介绍成功使用它所需的步骤。我希望这篇文章能帮你理解这个简单的方法。

过滤钩子驱动

正如我前面所说,过滤钩子驱动是微软在 Windows 2000 DDK 中引入的。实际上,它不是一个新的网络驱动类,它只是扩展 IP 过滤驱动(包含在 Windows 2000 及以后的版本中)功能的一种方法。

事实上,过滤钩子驱动不是网络驱动,而是内核模式驱动。基本的,我们在过滤钩子驱动中实现一个回调函数,然后把这个回调函数与 IP 过滤驱动注册到一起。这样做了之后,IP 过滤驱动会在一个数据包被发送或接受时调用我们的回调函数。那么……这么做主要的步骤有哪些呢?

我总结了以下五个步骤:

  1. 建立一个过滤器钩子驱动。这一步,你必须建立内核模式驱动,选择一个名字,DOS 名和其它驱动字符,没什么特别的要求但我建议你用描述性的名字。
  2. 如果我们要安装过滤函数,首先必须取得一个指向 IP 过滤驱动的指针。所以,这是第二个步骤。
  3. 我们已经有了指针,现在可以安装过滤函数了。我们可以通过发送指定的 IRP 来做这件事。在传递的“消息”数据中包含指向过滤函数的指针。
  4. 过滤数据包!!!
  5. 当我们决定停止过滤时,必须注销过滤函数。我们可以通过注册过滤函数到空指针来实现注销。

哦,只有五步,而且看起来很简单,但是……要怎样建立内核模式驱动呢?怎么取得指向 IP 过滤驱动的指针?怎么……是的,请稍等,我现在就来解释这些步骤:P,展示源码例子。

建立内核模式驱动

过滤钩子驱动是内核模式驱动,所以如果我们想做,就得做个内核模式驱动。这篇文章并不是“5分钟学会怎样开发内核模式驱动”,所以我假设读者们已经 有了这方面的知识。

过滤钩子驱动的结构是典型的内核模式驱动结构:

  1. 建立设备的驱动入口(注:建立设备是驱动入口的定语),设置标准例程来处理 IRP(分派,加载,卸载,创建……)和建立与其它应用程序通讯的符号连接。
  2. 管理 IRP 的标准例程。在你开始编写代码之前,我建议,考虑你要从设备驱动引出到应用程序哪些 IOCTL。在我的例子中,我实现了四个 IOCTL 代码:START_IP_HOOK(注册过滤函数)、STOP_IP_HOOK(注销过滤函数)、ADD_FILTER(安装新规则)和 CLEAR_FILTER(清除所有的规则)。
  3. 还必须为我们的驱动实现另一个函数:过滤函数。

我建议你使用程序生成内核模式驱动的结构,然后你只要把添加代码到生成的函数里就行了。例如,我在这个项目中使用的 QuickSYS

你可以看我自己实现的驱动结构,代码如下:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
                IN PUNICODE_STRING RegistryPath)
{

    //....

    dprintf("DrvFltIp.SYS: entering DriverEntry\n");

    //we have to create the device
    RtlInitUnicodeString(&deviceNameUnicodeString, NT_DEVICE_NAME);

    ntStatus = IoCreateDevice(DriverObject,
                0,
                &deviceNameUnicodeString,
                FILE_DEVICE_DRVFLTIP,
                0,
                FALSE,
                &deviceObject);

    if ( NT_SUCCESS(ntStatus) )
    {
        // Create a symbolic link that Win32 apps can specify to gain access
        // to this driver/device
        RtlInitUnicodeString(&deviceLinkUnicodeString, DOS_DEVICE_NAME);

        ntStatus = IoCreateSymbolicLink(&deviceLinkUnicodeString,
                                        &deviceNameUnicodeString);

        //....

        // Create dispatch points for device control, create, close.

        DriverObject->MajorFunction[IRP_MJ_CREATE]         =
        DriverObject->MajorFunction[IRP_MJ_CLOSE]          =
        DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvDispatch;
        DriverObject->DriverUnload                         = DrvUnload;
    }

    if ( !NT_SUCCESS(ntStatus) )
    {
        dprintf("Error in initialization. Unloading...");

        DrvUnload(DriverObject);
    }

    return ntStatus;
}

NTSTATUS DrvDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{

    // ....

    switch (irpStack->MajorFunction)
    {
    case IRP_MJ_CREATE:

        dprintf("DrvFltIp.SYS: IRP_MJ_CREATE\n");

        break;

    case IRP_MJ_CLOSE:

        dprintf("DrvFltIp.SYS: IRP_MJ_CLOSE\n");

        break;

    case IRP_MJ_DEVICE_CONTROL:

        dprintf("DrvFltIp.SYS: IRP_MJ_DEVICE_CONTROL\n");

        ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;

        switch (ioControlCode)
        {
        // ioctl code to start filtering
        case START_IP_HOOK:
        {
            SetFilterFunction(cbFilterFunction);

            break;
        }

        // ioctl to stop filtering
        case STOP_IP_HOOK:
        {
            SetFilterFunction(NULL);

            break;
        }

        // ioctl to add a filter rule
        case ADD_FILTER:
        {
            if(inputBufferLength == sizeof(IPFilter))
            {
                IPFilter *nf;

                nf = (IPFilter *)ioBuffer;

                AddFilterToList(nf);
            }

            break;
        }

        // ioctl to free filter rule list
        case CLEAR_FILTER:
        {
            ClearFilterList();

            break;
        }

        default:
            Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;

            dprintf("DrvFltIp.SYS: unknown IRP_MJ_DEVICE_CONTROL\n");

            break;
    }

        break;
    }

    ntStatus = Irp->IoStatus.Status;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    // We never have pending operation so always return the status code.
    return ntStatus;
}

VOID DrvUnload(IN PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING deviceLinkUnicodeString;

    dprintf("DrvFltIp.SYS: Unloading\n");

    SetFilterFunction(NULL);

    // Free any resources
    ClearFilterList();

    // Delete the symbolic link
    RtlInitUnicodeString(&deviceLinkUnicodeString, DOS_DEVICE_NAME);
    IoDeleteSymbolicLink(&deviceLinkUnicodeString);

    // Delete the device object
    IoDeleteDevice(DriverObject->DeviceObject);
}
我们已经做好了驱动的主要代码,接下来是过滤钩子驱动的代码。

注册过滤函数

在上面的代码中,你已经看到了一个叫做 SetFilterFunction(..) 的函数。实现这个函数是用来注册函数到 IP 过滤驱动的。有下面的几步:

  1. 首先,我们必须取得一个指向 IP 过滤驱动的指针。这要求 IP 过滤驱动已经安装并运行了。我的用户应用程序,在加载这个驱动之前就加载并启动了 IP 过滤驱动,来保证这一点。
  2. 第二,我们必须建立一个 IRP 指定 IOCTL_PF_SET_EXTENSION_POINTER 作为 IO 控制代码。我们必须以参数的形式传递一个 PF_SET_EXTENSION_HOOK_INFO 结构,其中包含了指向过滤函数的指针。如果你要卸载这个函数,你必须按照同样的步骤并用 NULL 代替指向过滤函数的指针。
  3. 发送建立的 IRP 到设备驱动。

这里,对于这个驱动有一个更大的问题。只有一个过滤函数可以被安装,所以如果其它的应用程序安装了一个,那你就不能安装你的函数了。

下面是这个函数的代码:

NTSTATUS SetFilterFunction
            (PacketFilterExtensionPtr filterFunction)
{
    NTSTATUS status = STATUS_SUCCESS, waitStatus=STATUS_SUCCESS;
    UNICODE_STRING filterName;
    PDEVICE_OBJECT ipDeviceObject=NULL;
    PFILE_OBJECT ipFileObject=NULL;

    PF_SET_EXTENSION_HOOK_INFO filterData;

    KEVENT event;
    IO_STATUS_BLOCK ioStatus;
    PIRP irp;

    dprintf("Getting pointer to IpFilterDriver\n");

    //first of all, we have to get a pointer to IpFilterDriver Device
    RtlInitUnicodeString(&filterName, DD_IPFLTRDRVR_DEVICE_NAME);
    status = IoGetDeviceObjectPointer(&filterName,STANDARD_RIGHTS_ALL,
                                    &ipFileObject, &ipDeviceObject);

    if(NT_SUCCESS(status))
    {
        //initialize the struct with functions parameters
        filterData.ExtensionPointer = filterFunction;

        //we need initialize the event used later by
        //the IpFilterDriver to signal us
        //when it finished its work
        KeInitializeEvent(&event, NotificationEvent, FALSE);

        //we build the irp needed to establish fitler function
        irp = IoBuildDeviceIoControlRequest(IOCTL_PF_SET_EXTENSION_POINTER,
                                                            ipDeviceObject,
        if(irp != NULL)
        {
            // we send the IRP
            status = IoCallDriver(ipDeviceObject, irp);

            //and finally, we wait for
            //"acknowledge" of IpFilter Driver
            if (status == STATUS_PENDING)
            {
                waitStatus = KeWaitForSingleObject(&event,
                                Executive, KernelMode, FALSE, NULL);

                if (waitStatus != STATUS_SUCCESS )
                    dprintf("Error waiting for IpFilterDriver response.");
            }

            status = ioStatus.Status;

            if(!NT_SUCCESS(status))
                dprintf("Error, IO error with ipFilterDriver\n");
        }

        else
        {
            //if we cant allocate the space,
            //we return the corresponding code error
            status = STATUS_INSUFFICIENT_RESOURCES;

            dprintf("Error building IpFilterDriver IRP\n");
        }

        if(ipFileObject != NULL)
            ObDereferenceObject(ipFileObject);

        ipFileObject = NULL;
        ipDeviceObject = NULL;
    }

    else
        dprintf("Error while getting the pointer\n");

    return status;
}

你会发现当我们完成了建立过滤函数的处理,我们必须废除( de-reference )在我们取得指向设备驱动的指针时获得的文件对象。我用了一个将在 IP 过滤驱动完成对 IRP 的处理时提示的事件。

过滤函数

我们已经看到了如何开发驱动和如何安装过滤函数,但我们还不知道过滤函数是什么样的。

我已经说过了这个函数总是在主机接受或发送一个数据包时被调用。根据这个函数的返回值,系统来决定对这个包作些什么。

这个函数的原型必须是这样的:

typedef  PF_FORWARD_ACTION
(*PacketFilterExtensionPtr)(
  // Ip Packet Header
  IN unsigned char *PacketHeader,
  // Packet. Don't include Header
  IN unsigned char *Packet,
  // Packet length. Don't Include length of ip header
  IN unsigned int PacketLength,
  // Index number for the interface adapter
  //over which the packet arrived
  IN unsigned int RecvInterfaceIndex,
  // Index number for the interface adapter
  //over which the packet will be transmitted
  IN unsigned int SendInterfaceIndex,
  //IP address for the interface
  //adapter that received the packet
  IN IPAddr RecvLinkNextHop,
  //IP address for the interface adapter
  //that will transmit the packet
  IN IPAddr SendLinkNextHop
  );

PF_FORWARD_ACTION 是一个枚举类型,可以取值为:

  • PF_FORWARD指定 IP 过滤驱动立即向 IP 栈返回 forward 响应。对于本地包,IP 将他们直接入栈。如果包的目的地是另一台计算机并且允许路由,IP 将它们相应的路由。
  • PF_DROP指定 IP 过滤驱动立即向 IP 栈返回 drop 响应。IP 应该丢弃数据包。
  • PF_PASS指定 IP 过滤驱动过滤包并向 IP 栈返回结果响应。IP 过滤驱动如何处理过滤数据包取决于数据包过滤 API 的设置。如果过滤钩子决定不处理这个包而交给 IP 过滤驱动来过滤这个包,返回这个 pass 响应。

虽然 DDK 文档只包含了这三个值,但你查看 pfhook.h(过滤钩子驱动需要包含)就会看到另一个值。这个值是 PF_ICMP_ON_DROP 。我估计这个值对应的是丢弃包并以一个 ICMP 包反馈错误信息。

正如你在过滤函数的定义中看到的,数据包及它的报头是以指针传递的。所以,你可以修改报头或负载然后传递该包。这是很有用的,例如做网络地址转换( NAT )。如果你改变了目标地址,IP 会路由该包。

在我的实现中,过滤函数将每个包和一个由用户应用程序引入的规则列表比较。这个列表是用链表实现的,由每个 START_IP_HOOK IOCTL 在运行时建立。你可以在我的代码中看到这些。

代码

在这篇文章的第一个版本中包含了一个简单的例子,但由于有人想让我帮他开发实际的程序,我把它更新成了一个更复杂的例子。新的例子时一个小的数据包 过滤程序。用这个新程序你可以实现你自己的过滤规则,就像你在一些商业防火墙软件中做的那样。

在第一个版本中,程序包含两个组件:

  • 用户应用程序:一个 MFC 应用程序用来管理过滤规则。这个应用程序发送规则到驱动程序并决定驱动什么时候开始过滤。过滤数据分三步:
    • 定义你需要的规则。用添加和删除命令你可以添加或删除过滤规则。
    • 安装规则。当你定义好了规则,点击安装按钮,发送它们到驱动程序。
    • 开始过滤。你只需要点击开始按钮来开始过滤。
  • 过滤钩子驱动:根据由用户应用程序接收到的过滤规则来过滤 IP 数据包的驱动。

过滤钩子驱动必须和用户应用程序的可执行文件处在同一个目录中。

为什么用这种方法开发防火墙?

这不是在 Windows 开发防火墙的唯一方法,还有其它的像 NDIS 防火墙,TDI 防火墙,Winsock 层的防火墙,数据包过滤 API,……所以我总结了一些过滤钩子驱动的优点和缺点,以供你在将来需要用到时参考。

  • 用这种方法有很大的灵活性。你可以过滤所有的 IP 数据包(以及上层的数据包)。但你不能过滤更底层的报头,例如,你不能过滤以太网帧。你需要用 NDIS 过滤来实现,更复杂也更灵活。
  • 这是一个简单的方法。安装防火墙和实现过滤函数用这种方法都是很简单的。但数据包过滤 API 更为简单,虽然没有它灵活。你不能访问数据包的内容,也不能用数据包过滤 API 修改它。

结果:过滤钩子驱动并不是最好的,但它也没有什么坏特性。可是为什么这个方法没有用在商业产品中呢?

答案很简单。虽然这个驱动没有不好的特性,但它有一个很大的缺点。正如我之前提到的,每次只能安装一个过滤函数。我们可以开发一个强大的防火墙,它 可能被上千的用户下载并安装,但如果其它的应用程序使用了过滤(并在之前安装了过滤函数),我们的程序就什么都做不了了。

这个方法还有另一个缺点没有被微软的文档提到。虽然 DDK 文档说你可以在过滤函数中访问数据包内容,但那不是真的。你可以访问收到的包的内容,但对于发送的包,你只能读 IP 和 TCP,UDP 或 ICMP 报头。我不明白为什么……

微软在 Windows XP 中引入了另一种没有这个限制的驱动:防火墙钩子驱动。它的安装很类似,但微软并不建议使用它,以为“它消耗太多的网络栈”。也许这个驱动会在以后的 Windows 版本中消失。

结论

好了,到结尾了。我只到这并不是开发防火墙的最好方法(我在前面提到了它的缺点)。但我认为这对正在查找资料和对此感兴趣的人们是个好的开始。

我希望你能得到些帮助,并开始想要开发个强大的防火墙了。

用dll注入的方式隐藏进程

zerray | 六月 16th, 2005 - 16:33

上次那个改变键盘布局 的程序,被同学很容易的就在任务管理器里找出来杀掉了,不爽!想个办法把它藏起来。

google了一下,发现隐藏进程的方法有很 多。可以用rundll,但那样任务管理器里还是会多出个进程,引起怀疑。还可以写注册表里AppInit_Dlls一项,但我试了一下,结果一改就开不 了机,可能是我的dll没写好吧。再有就是注入了,代码注入很隐蔽,但还要遇到代码定位,API定位等问题,麻烦,还是dll注入好了。决定了,说干就 干!

先把原来的那个程序稍做修改,然后build成dll。即加一个DllEntry就行了。

DllEntry proc hInst: HINSTANCE, reason: DWORD, reserved1: DWORD
LOCAL @dwThreadID
.IF reason == DLL_PROCESS_ATTACH
push hInst
pop inst
invoke CreateThread, NULL, 0, addr WinMain, NULL, NULL, addr @dwThreadID
invoke CloseHandle, eax
.ENDIF
mov eax, TRUE
ret
DllEntry endp

然后
ml /c /coff /Cp test.asm
link /DLL /SUBSYSTEM:WINDOWS test.obj

这样我 就有了一个test.dll,把它注入到别的进程里就行啦。为了做成一个程序,我把这个dll作为资源放到另一个程序里,在用到的时候把它释放出来。编辑 test.rc,写入一行:

1000 RCDATA hookx.dll

然后rc test.rc就得到了test.res,一会儿link的时候用。

下面就是写主程序了。程序中首先找到资源,把它释放到windows的 temp文件夹下,并指定隐藏属性(为了隐蔽嘛)。然后就是FindWindow,找到资源管理器(找它是因为几乎所有系统中都会开这个进程嘛),打开, 申请空间,写入LoadLibraryA的地址,CreateRemoteThread建立远程线程就OK了。

这里有一点要注意的就是,建 立了远程线程之后要让该线程马上返回,否则被注入的程序就会一直等待,这也就是为什么我在DllEntry中又用了一个CreateThread的原因。

下 面就是源程序了,就是连续调用一堆函数,也没注释,没做返回值的检查,崩溃了就不管喽!

.386
.model flat, stdcall
option casemap: none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

rcID equ 1000

.const
szKerdll db ‘kernel32.dll’, 0
szHookxdll db ‘hookx.dll’, 0
szDesktopClass db ‘Progman’, 0
szDesktopWindow db ‘Program Manager’, 0
szLoadlib db ‘LoadLibraryA’, 0

.data
szBuf db 256 dup(0)
szTempPath db 256 dup(0)
szFmt db ‘%s%s’, 0

.data?
hInstance dd ?
hResInfo dd ?
hResData dd ?
lpResData dd ?
dwResDataLen dd ?
hHookxdll dd ?
dwWrote dd ?
dwProcessID dd ?
hProcess dd ?
lpCodeRemote dd ?

.code
start:
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke FindResource, hInstance, rcID, RT_RCDATA
mov hResInfo, eax
invoke LoadResource, hInstance, hResInfo
mov hResData, eax
invoke LockResource, hResData
mov lpResData, eax
invoke SizeofResource, hInstance, hResInfo
mov dwResDataLen, eax
invoke GetTempPath, SIZEOF szTempPath, OFFSET szTempPath
invoke wsprintf, OFFSET szBuf, OFFSET szFmt, OFFSET szTempPath, OFFSET szHookxdll
invoke CreateFile, OFFSET szBuf, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_ATTRIBUTE_HIDDEN, 0
mov hHookxdll, eax
invoke WriteFile, hHookxdll, lpResData, dwResDataLen, OFFSET dwWrote, NULL
invoke FindWindow, OFFSET szDesktopClass, OFFSET szDesktopWindow
invoke GetWindowThreadProcessId, eax, OFFSET dwProcessID
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, dwProcessID
mov hProcess, eax
invoke VirtualAllocEx, hProcess, 0, SIZEOF szBuf, MEM_COMMIT, PAGE_EXECUTE_READWRITE
mov lpCodeRemote, eax
invoke WriteProcessMemory, hProcess, lpCodeRemote, OFFSET szBuf, SIZEOF szBuf, NULL
invoke LoadLibrary, OFFSET szKerdll
invoke GetProcAddress, eax, OFFSET szLoadlib
invoke CreateRemoteThread, hProcess, 0, 0, eax, lpCodeRemote, 0, 0
invoke CloseHandle, eax
invoke CloseHandle, hProcess
invoke ExitProcess, 0

end start

好了,编译连接一下,这回再运行,按alt+ctrl+del看一下,哈哈,没有任何迹象,成功!这回他该纳闷了,我的键盘怎么 又不好使了??

关于stdcall

zerray | 六月 15th, 2005 - 17:49

记得看win32asm教程的时候,里面提到win32只使用stdcall约定,即参数由右向左压入堆栈,恢复堆栈的工作交给被调用者。但同时还 提到有一个函数例外,wsprintf,因为它的参数个数是不确定的。原来一直没有用过这个函数,就没注意了。今天用了一下,发现也不用自己恢复堆栈啊, 只要简单的

invoke wsprintf, OFFSET szBuf, OFFSET szFmt, …

就OK了, 并不用在后面加上add esp, xx,为什么呢?把程序反汇编了一下,恍然大悟!原来是编译器帮我搞定了,自动的加上了add esp, xx一句。如果自己再写add esp, xx就是画蛇添足了。

[转]Gcc使用的内嵌汇编语法格式小教程

zerray | 六月 13th, 2005 - 19:49

本文对内嵌汇编语法,从基本语法、内嵌汇编的格式介绍、和扩展的内嵌汇编格式进行了详细说明,需要说明的是gcc采用的是at&t的汇编格式.

一  基本语法
语法上主要有以下几个不同.
★ 寄存器命名原则
at&t: %eax intel: eax
★ 源/目的操作数顺序
at&t: movl %eax,%ebx intel: mov ebx,eax
★常数/立即数的格式
at&t: movl $_value,%ebx intel: mov eax,_value
把_value的地址放入eax寄存器
at&t: movl $0xd00d,%ebx intel: mov ebx,0xd00d
★ 操作数长度标识
at&t: movw %ax,%bx intel: mov bx,ax
★寻址方式
at&t: immed32(basepointer,indexpointer,indexscale)
intel: [basepointer indexpointer*indexscale imm32)
linux工作于保护模式下,用的是32位线性地址,所以在计算地址时不用考虑 segment:offset的问题.上式中的地址应为:
imm32 basepointer indexpointer*indexscale
下面是一些例子:
★直接寻址
at&t: _booga ;
_booga 是一个全局的c变量注意加上$是表示地址引用,不加是表示值引用.
注:对于局部变量,可以通过堆栈指针引用.
intel: [_booga]
★寄存器间接寻址
at&t: (%eax)
intel: [eax]
★变址寻址
at&t: _variable(%eax)
intel: [eax _variable]
at&t: _array(,%eax,4)
intel: [eax*4 _array]
at&t: _array(%ebx,%eax,8)
intel: [ebx eax*8 _array]

二 基本的内嵌汇编
基本的内嵌汇编很简单,一般是按照下面的格式
asm(statements);
例 如:asm(nop); asm(cli);
asm 和 __asm__是完全一样的.
如果有多行汇编,则每一行都要加上 nt例 如:
asm( pushl %eaxnt
movl $0,%eaxnt
popl %eax);
实际上gcc在处 理汇编时,是要把asm(…)的内容打印到汇编文件中,所以格式控制字符是必要的.再例如:
asm(movl %eax,%ebx);
asm(xorl %ebx,%edx);
asm(movl $0,_booga);
在上面的例子中,由于我们在行内汇编中改变了edx和ebx的 值,但是由于gcc的特殊的处理方法,即先形成汇编文件,再交给gas去汇编,所以gas并不知道我们已经改变了edx和ebx的值,如果程序的上下文需 要edx或ebx作暂存,这样就会引起严重的后果.对于变量_booga也存在一样的问题.为了解决这个问题,就要用到扩展的行内汇编语法.

三  扩展的行内汇编
扩展的行内汇编类似于watcom.
基本的格式是:
asm ( statements : output_regs : input_regs : clobbered_regs);
clobbered_regs指的是被改变的寄存 器.
下面是一个例子(为方便起见,我使用全局变量):
int count=1;
int value=1;
int buf[10];
void main()
{
asm(
cld nt
rep nt
stosl
:
: c (count), a (value) , d (buf[0])
: %ecx,%edi );
}
得 到的主要汇编代码为:
movl count,%ecx
movl value,%eax
movl buf,%edi
#app
cld
rep
stosl
#no_app
cld,rep,stos就不用多解释了.
这几条语 句的功能是向buf中写上count个value值.
冒号后的语句指明输入,输出和被改变的寄存器.
通过冒号以后的语句,编译器就知 道你的指令需要和改变哪些寄存器,
从而可以优化寄存器的分配.
其中符号c(count)指示要把count的值放入ecx寄存器
类 似的还有:
a eax
b ebx
c ecx
d edx
s esi
d edi
i 常数值,(0 – 31)
q,r 动态分配的寄存器
g eax,ebx,ecx,edx或内存变量
a 把eax和edx合成一个64位的寄存器(use long longs)
我们也可以让gcc自己选择合适的寄存器.
如下面的例子:
asm(leal (%1,%1,4),%0
: =r (x)
: 0 (x) );
这段代码实现5*x的快速乘 法.
得到的主要汇编代码为:
movl x,%eax
#app
leal (%eax,%eax,4),%eax
#no_app
movl %eax,x
几点说明:
1.使用q指示编译器从eax,ebx,ecx,edx分配寄存器.使用r指示编译器从 eax,ebx,ecx,edx,esi,edi分配寄存器.
2.我们不必把编译器分配的寄存器放入改变的寄存器列表,因为寄存器已经记住了它 们.
3.=是标示输出寄存器,必须这样用.
4.数字%n的用法:数字表示的寄存器是按照出现和从左到右的顺序映射到用r或q请求
的 寄存器.如果我们要重用r或q请求的寄存器的话,就可以使用它们.
5.如果强制使用固定的寄存器的话,如不用%1,而用ebx,则
asm(leal (%%ebx,%%ebx,4),%0
: =r (x)
: 0 (x) );
注意要使用两个%,因为一个%的语法已经 被%n用掉了.

下面可以来解释letter 4854-4855的问题:
1、变量加下划线和双下划线有什么特殊含义吗?加下划 线是指全局变量,但我的gcc中加不加都无所谓.
2、以上定义用如下调用时展开会是什么意思?
#define _syscall1(type,name,type1,arg1)
type name(type1 arg1)
{
long __res;
/* __res应该是一个全局变量 */
__asm__ volatile (int $0×80
/* volatile 的意思是不允许优化,使编译器严格按照你的汇编代码汇编*/
: =a (__res)
/* 产生代码 movl %eax, __res */
: 0 (__nr_##name),b ((long)(arg1)));
/* 如果我没记错的话,这里##指的是两次宏展开.
即用实际的系统调用名字代替name,然后再把__nr_…展开.
接着把展开的常 数放入eax,把arg1放入ebx */
if (__res >= 0)
return (type) __res;
errno = -__res;
return -1;
}

win32asm 写的红警2的修改器

zerray | 六月 12th, 2005 - 19:29

由于红警2中保存金钱的位置每次载入都不一样,所以不能简单的 WriteProcessMemory 就行了。不过办法当然还是有的。查到一次的地址,然后在地址处下个断点,动态跟踪一下就会发现,代码中有一处 push eax 是用来给出保存金钱的地址的。在该处下断点,每次 eax 的值加 2E8h 刚好是保存金钱的地址。嘿嘿,那只要在这里做一些手脚不就搞定了?在进程的空间中找一处空闲的地方,例如地址A和其不远出的B,然后把push eax 的地方改成 jmp B 在B那里写上 mov A, eax 这样就把这个值保存在了一个固定的地方,然后再在B后面写上被破坏的几条指令,并最后jmp回原来的地址。OK了,现在我们只要去A处取地址,加上 2E8h 就能得到金钱的地址了,就又能改钱了,哈哈!至于改别的,我没有试,大概方法都差不多。

下面是代码,只适用于winxp,并且是 针对尤里的复仇的。每次按下alt+1加50000的money。

.386
.model flat, stdcall
option casemap: none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\shell32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\shell32.lib

WinMain proto :D WORD, :D WORD, :D WORD, :D WORD

WM_SHELLNOTIFY equ WM_USER + 5
IDI_TRAY equ 0
YuriIcon equ 10000
HotKeyID equ 0ABC0h

.const
AppMutex db ‘FixYuriMutex’, 0
AlreadyRun db ‘FixYuri is already running!’, 0
AppName db ‘FixYuri’, 0
ClassName db ‘FixYuriClass’, 0
TargetTitle db ‘Yuri’’s Revenge’, 0
HintText db ‘press Alt+1 add 50000$’, 0
YuriNotFound db ‘Yuri’’s Revenge is not running!’, 0
Addr1 dd 004A2593h ; push eax 处的地址,别的机器上可能不同
Addr2 dd 00B78F10h ; 保存代码的位置
Addr3 dd 00B78F00h ; 保存地址的位置
Data1 db 0E9h, 078h, 069h, 06Dh, 000h, 090h ; jmp 00B78F10h 的机器代码
Data2 db 0A3h, 000h, 08Fh, 0B7h, 000h, 050h, 0FFh, 051h, 018h, 033h, 0D2h, 0E9h, 079h, 096h, 092h, 0FFh ; mov [00B78F10h], eax; push eax; call dword ptr ds:[ecx+18]; xor edx, edx; jmp 004A2599h 的机器代码

.data
pid dd 0
hd dd 0

.data?
inst HINSTANCE ?
cmd LPSTR ?
note NOTIFYICONDATA <?>
ico dd ?
tmp dd ?
Addr4 dd ?
dwFlag dd ?

.code
start:
invoke CreateMutex, NULL, FALSE, addr AppMutex
invoke GetLastError
.IF eax == ERROR_ALREADY_EXISTS
invoke MessageBox, NULL, addr AlreadyRun, addr AppName, MB_OK or MB_ICONWARNING
invoke ExitProcess, 0
.ENDIF
invoke GetModuleHandle, NULL
mov inst, eax
invoke GetCommandLine
mov cmd, eax
invoke WinMain, inst, NULL, cmd, SW_MINIMIZE
invoke ExitProcess, eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND

mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW + 1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
invoke LoadIcon, hInst, YuriIcon
mov wc.hIcon, eax
mov wc.hIconSm, eax
mov ico, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, NULL,\
addr ClassName,\
addr AppName,\
WS_OVERLAPPEDWINDOW and not WS_MAXIMIZEBOX and not WS_SIZEBOX,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
200,\
50,\
NULL,\
NULL,\
hInst,\
NULL
mov hwnd, eax
invoke ShowWindow, hwnd, CmdShow
invoke UpdateWindow, hwnd

.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc: HDC
LOCAL ps: PAINTSTRUCT
LOCAL rect: RECT
.IF uMsg == WM_CREATE
invoke RegisterHotKey, hWnd, HotKeyID, MOD_ALT, VK_1
.ELSEIF uMsg == WM_PAINT
invoke BeginPaint, hWnd, addr ps
mov hdc, eax
invoke GetClientRect, hWnd, addr rect
invoke DrawText, hdc, addr HintText, -1, addr rect,\
DT_CENTER or DT_VCENTER or DT_SINGLELINE
.ELSEIF uMsg == WM_SIZE
.IF wParam == SIZE_MINIMIZED
mov note.cbSize, SIZEOF NOTIFYICONDATA
push hWnd
pop note.hwnd
mov note.uID, IDI_TRAY
mov note.uFlags, NIF_ICON or NIF_MESSAGE or NIF_TIP
mov note.uCallbackMessage, WM_SHELLNOTIFY
push ico
pop note.hIcon
invoke lstrcpy, addr note.szTip, addr AppName
invoke ShowWindow, hWnd, SW_HIDE
invoke Shell_NotifyIcon, NIM_ADD, addr note
.ENDIF
.ELSEIF uMsg == WM_SHELLNOTIFY
.IF wParam == IDI_TRAY
.IF lParam == WM_LBUTTONDOWN
invoke Shell_NotifyIcon, NIM_DELETE, addr note
invoke ShowWindow, hWnd, SW_RESTORE
invoke SetForegroundWindow, hWnd
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_HOTKEY
.IF wParam == HotKeyID
.IF hd == 0
invoke FindWindow, 0, addr TargetTitle
.IF eax != 0
invoke GetWindowThreadProcessId, eax, addr pid
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, pid
mov hd, eax
.ENDIF
.IF hd != 0
invoke WriteProcessMemory, hd, Addr1, addr Data1, SIZEOF Data1, NULL
invoke WriteProcessMemory, hd, Addr2, addr Data2, SIZEOF Data2, NULL
invoke Sleep, 1000
.ELSE
invoke SetForegroundWindow, hWnd
invoke MessageBox, hWnd, addr YuriNotFound, addr AppName, MB_OK or MB_ICONWARNING
.ENDIF
.ELSE
invoke ReadProcessMemory, hd, Addr3, addr tmp, SIZEOF tmp, NULL
.IF eax
push tmp
pop Addr4
add Addr4, 02e8h
invoke ReadProcessMemory, hd, Addr4, addr tmp, SIZEOF tmp, NULL
add tmp, 50000
invoke WriteProcessMemory, hd, Addr4, addr tmp, SIZEOF tmp, NULL
.ELSE
mov hd, 0
.ENDIF
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_DESTROY
invoke UnregisterHotKey, hWnd, HotKeyID
invoke PostQuitMessage, NULL
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp

end start

win32asm 写的红警98修改器

zerray | 六月 12th, 2005 - 18:58

前段时间写的,感觉比较完善了。能锁定金钱,电量和用电量,并能增加建造速度。并可以工作在win98和winxp两种平台上。

.386
.model flat, stdcall
option casemap: none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\shell32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\shell32.lib

WinMain proto :D WORD, :D WORD, :D WORD, :D WORD
KeyProc proto :D WORD, :D WORD, :D WORD
GetOsVersion proto

WM_SHELLNOTIFY equ WM_USER + 5
IDI_TRAY equ 0
RaIcon equ 10000
TimerID equ 1
ButtonID1 equ 1
ButtonID2 equ 2
ButtonID3 equ 3
ButtonID4 equ 4
HotKeyID1 equ 0ABC1h
HotKeyID2 equ 0ABC2h
HotKeyID3 equ 0ABC3h
HotKeyID4 equ 0ABC4h

.const
AppMutex db ‘racheatmutex’, 0
AlreadyRun db ‘RA cheater is already running!’, 0
AppName db ‘RA cheater’, 0
ClassName db ‘racheaterclass’, 0
TargetTitle db ‘Red Alert’, 0
ButtonClass db ‘Button’, 0
Button1 db ‘lock money(Alt+1)’, 0
Button2 db ‘lock used power(Alt+2)’, 0
Button3 db ‘lock power(Alt+3)’, 0
Button4 db ’speed up(Alt+4)’, 0
Value1 dd 30000
Value2 dd 0
Value3 dd 500
Value4 dd 0F0h

.data
flag1 db 0
flag2 db 0
flag3 db 0
pid dd 0
hd dd 0

.data?
inst HINSTANCE ?
cmd LPSTR ?
note NOTIFYICONDATA <?>
ico dd ?
OsVer dd ?
hHook dd ?
Addr1 dd ?
Addr2 dd ?
Addr3 dd ?
Addr4 dd ?
hwnd HWND ?

.code
start:
invoke CreateMutex, NULL, FALSE, addr AppMutex
invoke GetLastError
.IF eax == ERROR_ALREADY_EXISTS
invoke MessageBox, NULL, addr AlreadyRun, addr AppName, MB_OK or MB_ICONWARNING
invoke ExitProcess, 0
.ENDIF
invoke GetOsVersion ; 判断系统类型
mov OsVer, eax
.IF OsVer == VER_PLATFORM_WIN32_NT ; 根据系统类型赋不同的地址,也许在你的机器上和我的不一样,FPE搜一下就知道了。
mov Addr1, 0A431D17h
mov Addr2, 0A431D67h
mov Addr3, 0A431D63h
mov Addr4, 0A431D53h
.ELSE
mov Addr1, 01C31D17h
mov Addr2, 01C31D67h
mov Addr3, 01C31D63h
mov Addr4, 0A431D53h
.ENDIF
invoke GetModuleHandle, NULL
mov inst, eax
invoke GetCommandLine
mov cmd, eax
invoke WinMain, inst, NULL, cmd, SW_MINIMIZE
invoke ExitProcess, eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG

mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
invoke LoadIcon, hInst, RaIcon
mov wc.hIcon, eax
mov wc.hIconSm, eax
mov ico, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, NULL,\
addr ClassName,\
addr AppName,\
WS_OVERLAPPEDWINDOW and not WS_MAXIMIZEBOX and not WS_SIZEBOX,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
200,\
225,\
NULL,\
NULL,\
hInst,\
NULL
mov hwnd, eax
invoke ShowWindow, hwnd, CmdShow
invoke UpdateWindow, hwnd

.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg == WM_CREATE
invoke SetTimer, hWnd, TimerID, 500, NULL
.IF OsVer == VER_PLATFORM_WIN32_NT
invoke RegisterHotKey, hWnd, HotKeyID1, MOD_ALT, VK_1
invoke RegisterHotKey, hWnd, HotKeyID2, MOD_ALT, VK_2
invoke RegisterHotKey, hWnd, HotKeyID3, MOD_ALT, VK_3
invoke RegisterHotKey, hWnd, HotKeyID4, MOD_ALT, VK_4
.ELSE ; 由于win98下注册的快捷键进游戏后就失效了,只好用钩子实现快捷键。
invoke SetWindowsHookEx, WH_JOURNALRECORD, addr KeyProc, inst, NULL
mov hHook, eax
.ENDIF
invoke CreateWindowEx, NULL, addr ButtonClass, addr Button1,\
BS_PUSHBUTTON or WS_VISIBLE or WS_CHILD, 0, 0, 195, 50,\
hWnd, ButtonID1, inst, NULL
invoke CreateWindowEx, NULL, addr ButtonClass, addr Button2,\
BS_PUSHBUTTON or WS_VISIBLE or WS_CHILD, 0, 50, 195, 50,\
hWnd, ButtonID2, inst, NULL
invoke CreateWindowEx, NULL, addr ButtonClass, addr Button3,\
BS_PUSHBUTTON or WS_VISIBLE or WS_CHILD, 0, 100, 195, 50,\
hWnd, ButtonID3, inst, NULL
invoke CreateWindowEx, NULL, addr ButtonClass, addr Button4,\
BS_PUSHBUTTON or WS_VISIBLE or WS_CHILD, 0, 150, 195, 50,\
hWnd, ButtonID4, inst, NULL
.ELSEIF uMsg == WM_SIZE
.IF wParam == SIZE_MINIMIZED
mov note.cbSize, SIZEOF NOTIFYICONDATA
push hWnd
pop note.hwnd
mov note.uID, IDI_TRAY
mov note.uFlags, NIF_ICON or NIF_MESSAGE or NIF_TIP
mov note.uCallbackMessage, WM_SHELLNOTIFY
push ico
pop note.hIcon
invoke lstrcpy, addr note.szTip, addr AppName
invoke ShowWindow, hWnd, SW_HIDE
invoke Shell_NotifyIcon, NIM_ADD, addr note
.ENDIF
.ELSEIF uMsg == WM_SHELLNOTIFY
.IF wParam == IDI_TRAY
.IF lParam == WM_LBUTTONDOWN
invoke Shell_NotifyIcon, NIM_DELETE, addr note
invoke ShowWindow, hWnd, SW_RESTORE
invoke SetForegroundWindow, hWnd
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_TIMER
.IF hd == 0
invoke FindWindow, 0, addr TargetTitle
.IF eax != 0
invoke GetWindowThreadProcessId, eax, addr pid
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, pid
mov hd, eax
.ENDIF
.ENDIF
.IF flag1 == 1
invoke WriteProcessMemory, hd, Addr1, addr Value1, SIZEOF Value1, NULL
.IF eax == 0
mov hd, 0
.ENDIF
.ENDIF
.IF flag2 == 1
invoke WriteProcessMemory, hd, Addr2, addr Value2, SIZEOF Value2, NULL
.IF !eax
mov hd, 0
.ENDIF
.ENDIF
.IF flag3 == 1
invoke WriteProcessMemory, hd, Addr3, addr Value3, SIZEOF Value3, NULL
.IF !eax
mov hd, 0
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_COMMAND
mov eax, wParam
.IF ax == ButtonID1
xor flag1, 1
.ELSEIF ax == ButtonID2
xor flag2, 1
.ELSEIF ax == ButtonID3
xor flag3, 1
.ELSEIF ax == ButtonID4
.IF hd
mov edx, Addr4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_HOTKEY
.IF wParam == HotKeyID1
xor flag1, 1
.ELSEIF wParam == HotKeyID2
xor flag2, 1
.ELSEIF wParam == HotKeyID3
xor flag3, 1
.ELSEIF
.IF hd
mov edx, Addr4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_SYSKEYDOWN
.IF lParam == VK_1
xor flag1, 1
.ELSEIF lParam == VK_2
xor flag2, 1
.ELSEIF lParam == VK_3
xor flag3, 1
.ENDIF
.ELSEIF uMsg == WM_DESTROY
.IF OsVer == VER_PLATFORM_WIN32_NT
invoke UnregisterHotKey, hWnd, HotKeyID1
invoke UnregisterHotKey, hWnd, HotKeyID2
invoke UnregisterHotKey, hWnd, HotKeyID3
.ELSE
invoke UnhookWindowsHookEx, hHook
.ENDIF
invoke PostQuitMessage, NULL
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
.IF nCode == HC_ACTION
mov edx, lParam
assume edx: PTR EVENTMSG
.IF [edx].message == WM_SYSKEYDOWN
mov eax, [edx].paramL
.IF al == VK_1
invoke PostMessage, hwnd, WM_SYSKEYDOWN, NULL, VK_1
.ELSEIF al == VK_2
invoke PostMessage, hwnd, WM_SYSKEYDOWN, NULL, VK_2
.ELSEIF al == VK_3
invoke PostMessage, hwnd, WM_SYSKEYDOWN, NULL, VK_3
.ENDIF
.ENDIF
.ENDIF
invoke CallNextHookEx, hHook, nCode, wParam, lParam
ret
KeyProc endp

GetOsVersion proc
LOCAL ovi:OSVERSIONINFO
mov ovi.dwOSVersionInfoSize, SIZEOF OSVERSIONINFO
invoke GetVersionEx, addr ovi
mov eax, ovi.dwPlatformId
ret
GetOsVersion endp

end start

由 于红警98中金钱等信息的地址在每次载入时都是固定的,所以这个程序可以得逞,要是换红警2,就得用另一种方法了。

用底层键盘钩子改变键盘布局

zerray | 六月 11th, 2005 - 22:28

看win32汇编看到钩子部分,突发奇想,打算写一个改变键盘布局的整人程序

查 了查资料,发现底层键盘钩子(WH_KEYBOARD_LL)可以实现。首先是安装和卸载钩子:

InstallHook proc hIns: DWORD
invoke SetWindowsHookEx, WH_KEYBOARD_LL, addr KeyProc, hIns, NULL
mov hHook, eax
ret
InstallHook endp

UninstallHook proc
invoke UnhookWindowsHookEx, hHook
ret
UninstallHook endp

这些与别的钩子一样,在回调函数 KeyProc 中 nCode 值为 HC_ACTION,wParam 为按键消息(WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP),lParam 为指向 KBDLLHOOKSTRUCT 结构的指针。

首先,我想捕获按键x,为了知道是否捕获到了,我让程序在有x按下时弹出一个对话框。

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
.IF nCode == HC_ACTION
.IF (wParam == WM_KEYDOWN)
mov edx, lParam
assume edx: PTR KBDLLHOOKSTRUCT
.IF ([edx].vkCode == VK_X)
invoke MessageBox, NULL, addr szMsg, addr szTit, MB_OK
.ENDIF
.ENDIF
.ENDIF
invoke CallNextHookEx, hHook, nCode, wParam, lParam
ret
KeyProc endp

程序很简单,当有x按下时弹出一个对话框,按一下试试,OK!看来我们已经捕获到按键了。

那现在要怎样才能把x按 键改成其它的按键呢?先试试其他程序是不是在我们的钩子函数之后处理按键消息:把 KeyProc 中的 invoke 一句改成 ret。运行程序,按x,呵呵,x键被屏蔽了,看来钩子函数处理按键消息的优先级是很高的。那把这个消息改成别的按键传下去就能起到改变按键的作用了吧? 试试看喽!

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
.IF nCode == HC_ACTION
.IF (wParam == WM_KEYDOWN)
mov edx, lParam
assume edx: PTR KBDLLHOOKSTRUCT
.IF ([edx].vkCode == VK_X)
mov [edx].vkCode, VK_Y
.ENDIF
.ENDIF
.ENDIF
invoke CallNextHookEx, hHook, nCode, wParam, lParam
ret
KeyProc endp

我把改 vkCode 改成了 VK_Y,现在其它程序得到的消息应该是按键y了吧?运行,按x,奇怪,还是x??为什么啊?难道 scanCode 也要改??查了一下 VK_Y 的 scanCode ,也改了,还是不行。难道我们的程序没有修改那段内存的权限?提升程序的权限?太麻烦了,还是想想别的办法吧!既然现在能够屏蔽掉一个按键了,那我们不是 可以再用 keybd_event 模拟按键吗?对,就这么干!

我们现在屏蔽x和y按键,然后在x按键时模拟y按键,在y按键时模拟x按 键,这样就调换了x和y的键盘位置。

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
.IF nCode == HC_ACTION
.IF (wParam == WM_KEYDOWN)
mov edx, lParam
assume edx: PTR KBDLLHOOKSTRUCT
.IF ([edx].vkCode == VK_X)
invoke keybd_event, VK_Y, 0, 0, 0
ret
.ELSEIF ([edx].vkCode == VK_Y)
invoke keybd_event, VK_X, 0, 0, 0
ret
.ENDIF
.ENDIF
.ENDIF
invoke CallNextHookEx, hHook, nCode, wParam, lParam
ret
KeyProc endp

这回应该没问题了吧?运行一下试试!哦,NO,又出问题了,按下x 或y后,x和y不停的交替出现,消息循环了?是啊,连用 keybd_event 模拟的按键都被底层键盘钩子捕获了!怎么办呢?其实多判断一下 scanCode 就好了,模拟的按键和实际的按键 scanCode 是不同的。好了,现在问题完全解决了!在加上判断 WM_SYSKEYDOWN 的判断,这样就换的更彻底了。下面是完整的程序:

.386
.model flat, stdcall
option casemap: none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

WinMain proto :D WORD, :D WORD, :D WORD, :D WORD
InstallHook proto :D WORD
UninstallHook proto

HotKeyID1 equ 1
HotKeyID2 equ 2

KBDLLHOOKSTRUCT STRUCT
vkCode DWORD ?
scanCode DWORD ?
flags DWORD ?
time DWORD ?
dwExtraInfo ULONG ?
KBDLLHOOKSTRUCT ENDS

.const
ClassName db ‘HookxClass’, 0
AppName db ‘Hookx’, 0
Running db ‘i am running…’, 0

.data?
inst dd ?
hHook dd ?
cmd dd ?

.code

start:
invoke GetModuleHandle, NULL
mov inst, eax
invoke GetCommandLine
mov cmd, eax
invoke WinMain, inst, NULL, cmd, SW_HIDE ;整人的程序,当然不能把窗口显示出来了!
invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND

mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW + 1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
invoke LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, NULL,\
addr ClassName,\
addr AppName,\
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
NULL,\
NULL,\
hInst,\
NULL
mov hwnd, eax
invoke ShowWindow, hwnd, CmdShow
invoke UpdateWindow, hwnd

.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg == WM_CREATE
invoke InstallHook, inst
invoke RegisterHotKey, hWnd, HotKeyID1, MOD_ALT, VK_8 ;注册两个快捷键用来查看程序时候在运行和关闭程序
invoke RegisterHotKey, hWnd, HotKeyID2, MOD_ALT, VK_9
.ELSEIF uMsg == WM_HOTKEY
.IF wParam == HotKeyID1
invoke MessageBox, NULL, addr Running, addr AppName, MB_OK
.ELSEIF wParam == HotKeyID2
invoke UninstallHook
invoke PostMessage, hWnd, WM_DESTROY, NULL, NULL
.ENDIF
.ELSEIF uMsg == WM_DESTROY
invoke UnregisterHotKey, hWnd, HotKeyID2
invoke UnregisterHotKey, hWnd, HotKeyID1
invoke UninstallHook
invoke PostQuitMessage, NULL
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp

KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
.IF nCode == HC_ACTION
.IF (wParam == WM_KEYDOWN) || (wParam == WM_SYSKEYDOWN)
mov edx, lParam
assume edx: PTR KBDLLHOOKSTRUCT
.IF ([edx].vkCode == VK_X) && ([edx].scanCode != 0)
invoke keybd_event, VK_Y, 0, 0, 0
ret
.ELSEIF ([edx].vkCode == VK_Y) && ([edx].scanCode != 0)
invoke keybd_event, VK_X, 0, 0, 0
ret
.ENDIF
.ENDIF
.ENDIF
invoke CallNextHookEx, hHook, nCode, wParam, lParam
ret
KeyProc endp

InstallHook proc hIns: DWORD
invoke SetWindowsHookEx, WH_KEYBOARD_LL, addr KeyProc, hIns, NULL
mov hHook, eax
ret
InstallHook endp

UninstallHook proc
invoke UnhookWindowsHookEx, hHook
ret
UninstallHook endp

end start

现在我可以把这个程序偷偷的放到同学的机器上去运行了, 咦?我的键盘怎么了?

[转]远程dll注入与代码注入

zerray | 六月 11th, 2005 - 14:40

程序说明:找到计算器进程把c盘下的modll.dll注入.
DLL编绎参数: ml /c /coff modll.asm
link /dll /subsystem:windows /def:modll.def modll.obj
def 文件:只有一行(无导出函数哈):LIBRARY modll.dll
dll的作用:显示被注入进程的PID号.

masm6.11+win2k pro调试通过.
程序下载:http://www.cnwill.com/soft/czy/modll.dll

http://www.cnwill.com/soft/czy/mo.exe

如 果是代码注入:作用弹一个msgbox出来

———————————rthread.asm——————
.386
.model flat,stdcall
option casemap:none

include ../include/user32.inc
includelib ../lib/user32.lib
include ../include/kernel32.inc
includelib ../lib/kernel32.lib
include ../include/windows.inc

.data
hello db ‘2K下建远程线程’,0
tit db ‘计算器’,0
szFormat db ‘PID是:%d’,0
szBuffer dd 20 dup(0),0
pid dd 0
hProcess dd 0
hThread dd 0
pCodeRemote dd 0
dllname db ‘c:\modll.dll’,0

.const
szmsg db ‘MessageBoxA’,0
userdll db ‘User32.dll’,0
szloadlib db ‘LoadLibraryA’,0 ;注意和LoadLibraryW的区别哟
kerdll db ‘kernel32.dll’,0

.code
codebegin:
dispdata db “iam remote thread”,0
szTit db “nsfocus.czy”,0
datalen =$-codebegin
Rproc proc msgbox ;MessageBoxA的地址为参数
CALL @F ;push esi
@@:
POP EBX
SUB EBX,OFFSET @B
LEA ECX,[EBX+dispdata]
LEA EDX,[EBX+szTit]
push 1
push edx
push ecx
push 0
call msgbox
ret ;重要
Rproc endp
codelen =$-codebegin ;代码长度13字节

start:
invoke FindWindow,0,offset tit ;返回计算器窗口句柄
invoke GetWindowThreadProcessId,eax,offset pid ;计算机器程序的进程PID号
;invoke wsprintf,offset szBuffer,offset szFormat,pid ;把PID用十进制显示
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,pid ;打开进程,得到进程句柄
mov hProcess,eax ;保存进程句柄

;——————————————- 下面是把程序代码注入
;invoke VirtualAllocEx,hProcess,0, codelen, MEM_COMMIT, PAGE_EXECUTE_READWRITE
;mov pCodeRemote,eax
;invoke WriteProcessMemory,hProcess,pCodeRemote,offset codebegin,codelen,NULL

;mov esi,pCodeRemote
;add esi,datalen
;push esi
;invoke LoadLibrary,offset userdll
;invoke GetProcAddress,eax,offset szmsg
;pop esi
;invoke CreateRemoteThread,hProcess,0,0,esi,eax,0,0
;——————————————– 下面是DLL注入
invoke VirtualAllocEx,hProcess,0, sizeof dllname, MEM_COMMIT, PAGE_EXECUTE_READWRITE
mov pCodeRemote,eax
invoke WriteProcessMemory,hProcess,pCodeRemote,offset dllname,sizeof dllname,NULL
invoke LoadLibrary,offset kerdll
invoke GetProcAddress,eax,offset szloadlib
invoke CreateRemoteThread,hProcess,0,0,
eax, ;这个参数在代码注入中为代码起始地址,现在成了LoadLibraryA的起始地址了
pCodeRemote, ;要加载的DLL的名字
0,0
;——————————————– 没有用FreeLibrary所以只能加载一次
mov hThread,eax ; 返回线程句柄
.if hThread
invoke WaitForSingleObject,hThread, INFINITE ;等待线程结束
invoke CloseHandle,hThread ;关闭线程句柄
.endif

invoke VirtualFreeEx,hProcess,pCodeRemote,codelen,MEM_RELEASE ;释放空间
invoke CloseHandle,hProcess ;关闭进程句柄
;invoke MessageBoxA,0,offset szBuffer,offset szBuffer,1
invoke ExitProcess,0
end start

———————————-end———————

———————-modll.asm——————–
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
pid dd 0
szFormat db ‘PID是:%d’,0
szBuffer dd 20 dup(0),0
tit db ‘显示被注入进程的PID’,0

.code
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD

.if reason==DLL_PROCESS_ATTACH ;dll加载时
invoke GetCurrentProcessId
mov pid,eax
invoke wsprintf,offset szBuffer,offset szFormat,pid
invoke MessageBoxA,0,offset szBuffer,offset tit,0
.endif
mov eax,TRUE
ret
DllEntry Endp

End DllEntry
———————-end————————–