2007年8月26日

标 题: 【原创】ring0检测隐藏进程
作 者: 堕落天才
时 间: 2007-05-10,13:28
链 接: http://bbs.pediy.com/showthread.php?t=44243

//网上得到一篇好文章 Ring0下搜索内存枚举隐藏进程 ,但是拿里面的代码来使用的时候发现并没有太多效果
//于是修改之,终于实现了最初的目标
//由于直接搜索内存,跟系统调度没什么关系,所以能够枚举到各种方法隐藏的进程 包括断链、抹PspCidTable... 
//甚至能枚举到已经"死掉"的进程,本程序通过进程的ExitTime来判断进程是不是已经结束
//除非能够把EProcess结构修改掉,但这个实现难度可能比较大,不知有没有哪位大侠试过(PID我修改过),欢迎讨论
//
//作者:堕落天才
//时间:2007年5月10日
//参考: uty  Ring0下搜索内存枚举隐藏进程 http://www.cnxhacker.net/Article/show/3412.html
//下面代码在XP SP2测试通过


#include<ntddk.h>

///////////////////////////不同的windows版本下面的偏移值不同
#define  EPROCESS_SIZE       0x25C //EPROCESS结构大小

#define  PEB_OFFSET          0x1B0
#define  FILE_NAME_OFFSET    0x174
#define  PROCESS_LINK_OFFSET 0x088
#define  PROCESS_ID_OFFSET   0x084
#define  EXIT_TIME_OFFSET    0x078

#define  OBJECT_HEADER_SIZE  0x018
#define  OBJECT_TYPE_OFFSET  0x008

#define PDE_INVALID 2 
#define PTE_INVALID 1 
#define VALID 0 


ULONG     pebAddress;         //PEB地址的前半部分
PEPROCESS pSystem;            //system进程
ULONG     pObjectTypeProcess; //进程对象类型

ULONG   VALIDpage(ULONG addr) ;  //该函数直接复制自 Ring0下搜索内存枚举隐藏进程
BOOLEAN IsaRealProcess(ULONG i); //该函数复制自 Ring0下搜索内存枚举隐藏进程
VOID    WorkThread(IN PVOID pContext);
ULONG   GetPebAddress();          //得到PEB地址前半部分
VOID    EnumProcess();            //枚举进程
VOID    ShowProcess(ULONG pEProcess); //显示结果

VOID    OnUnload(IN PDRIVER_OBJECT DriverObject)
{
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
  HANDLE hThread; 

  DriverObject -> DriverUnload = OnUnload;

  pSystem    = PsGetCurrentProcess();
  pebAddress = GetPebAddress();
  pObjectTypeProcess = *(PULONG)((ULONG)pSystem - OBJECT_HEADER_SIZE +OBJECT_TYPE_OFFSET);  

  PsCreateSystemThread(&hThread, 
    (ACCESS_MASK)0, 
    NULL, 
    (HANDLE)0, 
    NULL, 
    WorkThread, 
    NULL ); 

  return STATUS_SUCCESS;
}
//////////////////////////////////////////////
VOID WorkThread(IN PVOID pContext) 

  EnumProcess();
  PsTerminateSystemThread(STATUS_SUCCESS);  
}
////////////////////////////////////////////////////////
ULONG  GetPebAddress()
{
  ULONG Address;
  PEPROCESS pEProcess;

        //由于system进程的peb总是零 我们只有到其他进程去找了
  pEProcess = (PEPROCESS)((ULONG)((PLIST_ENTRY)((ULONG)pSystem + PROCESS_LINK_OFFSET))->Flink - PROCESS_LINK_OFFSET);
  Address   = *(PULONG)((ULONG)pEProcess + PEB_OFFSET);

  return (Address & 0xFFFF0000);  
}
///////////////////////////////////////////////////////
VOID EnumProcess()
{
  ULONG  uSystemAddress = (ULONG)pSystem;
  ULONG  i;
  ULONG  Address;
  ULONG  ret;

  DbgPrint("-------------------------------------------");
  DbgPrint("EProcess    PID    ImageFileName");
  DbgPrint("---------------------------------");
  

  for(i = 0x80000000; i < uSystemAddress; i += 4){//system进程的EPROCESS地址就是最大值了
    ret = VALIDpage(i); 
    if (ret == VALID){ 
      Address = *(PULONG)i;
      if (( Address & 0xFFFF0000) == pebAddress){//每个进程的PEB地址都是在差不多的地方,地址前半部分是相同的       
        if(IsaRealProcess(i)){ 
          ShowProcess(i - PEB_OFFSET);  
           i += EPROCESS_SIZE;                
        } 
      } 
    }else if(ret == PTE_INVALID){ 
      i -=4; 
      i += 0x1000;//4k 
    }else{ 
      i-=4; 
      i+= 0x400000;//4mb 
    } 
  }

  ShowProcess(uSystemAddress);//system的PEB总是零 上面的方法是枚举不到的 不过我们用PsGetCurrentProcess就能得到了
  DbgPrint("-------------------------------------------");
  
}
/////////////////////////////////////////////////////////
VOID    ShowProcess(ULONG pEProcess)
{
  PLARGE_INTEGER ExitTime;
  ULONG PID;
  PUCHAR pFileName;
  
  ExitTime = (PLARGE_INTEGER)(pEProcess + EXIT_TIME_OFFSET);  
  if(ExitTime->QuadPart != 0) //已经结束的进程的ExitTime为非零
    return ;

  PID = *(PULONG)(pEProcess + PROCESS_ID_OFFSET);
  pFileName = (PUCHAR)(pEProcess + FILE_NAME_OFFSET);

  DbgPrint("0x%08X  %04d   %s",pEProcess,PID,pFileName);
}
/////////////////////////////////////////////////////////////
ULONG VALIDpage(ULONG addr) 

  ULONG pte; 
  ULONG pde; 
  
  pde = 0xc0300000 + (addr>>22)*4; 
  if((*(PULONG)pde & 0x1) != 0){ 
    //large page 
    if((*(PULONG)pde & 0x80) != 0){ 
      return VALID; 
    } 
    pte = 0xc0000000 + (addr>>12)*4; 
    if((*(PULONG)pte & 0x1) != 0){ 
      return VALID; 
    }else{ 
      return PTE_INVALID; 
    } 
  } 
  return PDE_INVALID; 

////////////////////////////////////////////////////////////////
BOOLEAN IsaRealProcess(ULONG i) 

  NTSTATUS STATUS; 
  PUNICODE_STRING pUnicode; 
  UNICODE_STRING Process; 
  ULONG pObjectType; 
  ULONG ObjectTypeAddress; 
  
  if (VALIDpage(i- PEB_OFFSET) != VALID){ 
    return FALSE; 
  } 

  ObjectTypeAddress = i - PEB_OFFSET - OBJECT_HEADER_SIZE + OBJECT_TYPE_OFFSET ;
  
  if (VALIDpage(ObjectTypeAddress) == VALID){ 
    pObjectType = *(PULONG)ObjectTypeAddress; 
  }else{ 
    return FALSE; 
  } 
  
  if(pObjectTypeProcess == pObjectType){ //确定ObjectType是Process类型
    return TRUE; 
  } 
  return FALSE; 


////////////////////////////////////////////////////////////////////

posted @ 2007-08-26 23:41 紫雨轩 C++ 阅读(1948) | 评论 (0)编辑 收藏

标 题: 【原创】SSDT Hook的妙用-对抗ring0 inline hook
作 者: 堕落天才
时 间: 2007-03-10,15:18
链 接: http://bbs.pediy.com/showthread.php?t=40832

*******************************************************
*标题:【原创】SSDT Hook的妙用-对抗ring0 inline hook  *
*作者:堕落天才                                        *
*日期:2007年3月10号                                   *
*声明:本文章的目的仅为技术交流讨论                    *
*******************************************************

1,SSDT
     SSDT即系统服务描述符表,它的结构如下(参考《Undocument Windows 2000 Secretes》第二章):
     typedef struct _SYSTEM_SERVICE_TABLE
     {
       PVOID   ServiceTableBase;        //这个指向系统服务函数地址表
       PULONG  ServiceCounterTableBase;
       ULONG   NumberOfService;         //服务函数的个数,NumberOfService*4 就是整个地址表的大小
       ULONG   ParamTableBase;
     }SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;   
     
     typedef struct _SERVICE_DESCRIPTOR_TABLE
     {
       SYSTEM_SERVICE_TABLE   ntoskrnel;  //ntoskrnl.exe的服务函数
       SYSTEM_SERVICE_TABLE   win32k;     //win32k.sys的服务函数,(gdi.dll/user.dll的内核支持)
       SYSTEM_SERVICE_TABLE   NotUsed1;
       SYSTEM_SERVICE_TABLE   NotUsed2;
     }SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
     
     内核中有两个系统服务描述符表,一个是KeServiceDescriptorTable(由ntoskrnl.exe导出),一个是KeServieDescriptorTableShadow(没有导出)。两者的区别是,KeServiceDescriptorTable仅有ntoskrnel一项,KeServieDescriptorTableShadow包含了ntoskrnel以及win32k。一般的Native API的服务地址由KeServiceDescriptorTable分派,gdi.dll/user.dll的内核API调用服务地址由KeServieDescriptorTableShadow分派。还有要清楚一点的是win32k.sys只有在GUI线程中才加载,一般情况下是不加载的,所以要Hook KeServieDescriptorTableShadow的话,一般是用一个GUI程序通过IoControlCode来触发(想当初不明白这点,蓝屏死机了N次都想不明白是怎么回事)。
 
 2,SSDT HOOK 
    SSDT HOOK 的原理其实非常简单,我们先实际看看KeServiceDescriptorTable是什么样的。    
    lkd> dd KeServiceDescriptorTable
    8055ab80  804e3d20 00000000 0000011c 804d9f48
    8055ab90  00000000 00000000 00000000 00000000
    8055aba0  00000000 00000000 00000000 00000000
    8055abb0  00000000 00000000 00000000 00000000   
    在windbg.exe中我们就看得比较清楚,KeServiceDescriptorTable中就只有第一项有数据,其他都是0。其中804e3d20就是
KeServiceDescriptorTable.ntoskrnel.ServiceTableBase,服务函数个数为0x11c个。我们再看看804e3d20地址里是什么东西:
    lkd> dd 804e3d20
    804e3d20  80587691 805716ef 8057ab71 80581b5c
    804e3d30  80599ff7 80637b80 80639d05 80639d4e
    804e3d40  8057741c 8064855b 80637347 80599539
    804e3d50  8062f4ec 8057a98c 8059155e 8062661f
    如上,80587691 805716ef 8057ab71 80581b5c 这些就是系统服务函数的地址了。比如当我们在ring3调用OpenProcess时,进入sysenter的ID是0x7A(XP SP2),然后系统查KeServiceDescriptorTable,大概是这样KeServiceDescriptorTable.ntoskrnel.ServiceTableBase(804e3d20) + 0x7A * 4 = 804E3F08,然后804E3F08 ->8057559e 这个就是OpenProcess系统服务函数所在,我们再跟踪看看:
    lkd> u 8057559e
    nt!NtOpenProcess:
    8057559e 68c4000000      push    0C4h
    805755a3 6860b54e80      push    offset nt!ObReferenceObjectByPointer+0x127 (804eb560)
    805755a8 e8e5e4f6ff      call    nt!InterlockedPushEntrySList+0x79 (804e3a92)
    805755ad 33f6            xor     esi,esi
    原来8057559e就是NtOpenProcess函数所在的起始地址。  
    嗯,如果我们把8057559e改为指向我们函数的地址呢?比如 MyNtOpenProcess,那么系统就会直接调用MyNtOpenProcess,而不是原来的NtOpenProcess了。这就是SSDT HOOK 原理所在。

  3, ring0 inline hook
     ring0 inline hook 跟ring3的没什么区别了,如果硬说有的话,那么就是ring3发生什么差错的话程序会挂掉,ring0发生什么差错的话系统就挂掉,所以一定要很小心。inline hook的基本思想就是在目标函数中JMP到自己的监视函数,做一些判断然后再JMP回去。一般都是修改函数头,不过再其他地方JMP也是可以的。下面我们来点实际的吧:
     lkd> u nt!NtOpenProcess
     nt!NtOpenProcess:
     8057559e e95d6f4271      jmp     f199c500
     805755a3 e93f953978      jmp     f890eae7
     805755a8 e8e5e4f6ff      call    nt!InterlockedPushEntrySList+0x79 (804e3a92)
     ...
     同时打开“冰刃”跟“Rootkit Unhooker”我们就能在NtOpenProcess函数头看到这样的“奇观”,第一个jmp是“冰刃”的,第二个jmp是“Rootkit Unhooker”的。他们这样是防止被恶意程序通过TerminateProcess关闭。当然“冰刃”还Hook了NtTerminateProcess等函数。

×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
    好了,道理就说完了,下面就进入本文正题。
    对付ring0 inline hook的基本思路是这样的,自己写一个替换的内核函数,以NtOpenProcess为例,就是MyNtOpenProcess。然后修改SSDT表,让系统服务进入自己的函数MyNtOpenProcess。而MyNtOpenProcess要做的事就是,实现NtOpenProcess前10字节指令,然后再JMP到原来的NtOpenProcess的十字节后。这样NtOpenProcess函数头写的JMP都失效了,在ring3直接调用OpenProcess再也毫无影响。
***************************************************************************************************************************
#include<ntddk.h>

typedef struct _SERVICE_DESCRIPTOR_TABLE
{
  PVOID   ServiceTableBase;
  PULONG  ServiceCounterTableBase;
  ULONG   NumberOfService;
  ULONG   ParamTableBase;
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; //由于KeServiceDescriptorTable只有一项,这里就简单点了
extern PSERVICE_DESCRIPTOR_TABLE    KeServiceDescriptorTable;//KeServiceDescriptorTable为导出函数

/////////////////////////////////////
VOID Hook();
VOID Unhook();
VOID OnUnload(IN PDRIVER_OBJECT DriverObject);
//////////////////////////////////////
ULONG JmpAddress;//跳转到NtOpenProcess里的地址
ULONG OldServiceAddress;//原来NtOpenProcess的服务地址
//////////////////////////////////////
__declspec(naked) NTSTATUS __stdcall MyNtOpenProcess(PHANDLE ProcessHandle,
               ACCESS_MASK DesiredAccess,
               POBJECT_ATTRIBUTES ObjectAttributes,
               PCLIENT_ID ClientId) 
{
  DbgPrint("NtOpenProcess() called");
  __asm{
    push    0C4h
    push    804eb560h  //共十个字节
    jmp     [JmpAddress]     
  }
}
///////////////////////////////////////////////////
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
  DriverObject->DriverUnload = OnUnload;
  DbgPrint("Unhooker load");
  Hook();
  return STATUS_SUCCESS;
}
/////////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
  DbgPrint("Unhooker unload!");
  Unhook();
}
/////////////////////////////////////////////////////
VOID Hook()
{
  ULONG  Address;
  Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//0x7A为NtOpenProcess服务ID
  DbgPrint("Address:0x%08X",Address);

  OldServiceAddress = *(ULONG*)Address;//保存原来NtOpenProcess的地址
  DbgPrint("OldServiceAddress:0x%08X",OldServiceAddress);

  DbgPrint("MyNtOpenProcess:0x%08X",MyNtOpenProcess);

  JmpAddress = (ULONG)NtOpenProcess + 10; //跳转到NtOpenProcess函数头+10的地方,这样在其前面写的JMP都失效了
  DbgPrint("JmpAddress:0x%08X",JmpAddress);
    
  __asm{//去掉内存保护
    cli
         mov  eax,cr0
    and  eax,not 10000h
    mov  cr0,eax
  }

  *((ULONG*)Address) = (ULONG)MyNtOpenProcess;//HOOK SSDT

  __asm{//恢复内存保护  
          mov  eax,cr0
    or   eax,10000h
    mov  cr0,eax
    sti
  }
}
//////////////////////////////////////////////////////
VOID Unhook()
{
  ULONG  Address;
  Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//查找SSDT

  __asm{
    cli
          mov  eax,cr0
    and  eax,not 10000h
    mov  cr0,eax
  }

  *((ULONG*)Address) = (ULONG)OldServiceAddress;//还原SSDT

  __asm{  
         mov  eax,cr0
    or   eax,10000h
    mov  cr0,eax
    sti
  }

  DbgPrint("Unhook");
}
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
    就这么多了,或许有人说,没必要那么复杂,直接恢复NtOpenProcess不就行了吗?对于象“冰刃”“Rookit Unhooker”这些“善良”之辈的话是没问题的,但是象NP这些“穷凶极恶”之流的话,它会不断检测NtOpenProcess是不是已经被写回去,是的话,嘿嘿,机器马上重启。这也是这种方法的一点点妙用。

posted @ 2007-08-26 23:39 紫雨轩 C++ 阅读(1363) | 评论 (1)编辑 收藏


2007年8月22日

void AdjustPrivilege(int pid, BOOL bEnable)
{
    HANDLE    hProcess;
  HANDLE    hToken=0;
    TOKEN_PRIVILEGES tkp;
  tkp.PrivilegeCount = 1;  
  tkp.Privileges[0].Attributes = 0;
  if (bEnable)
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  if (LookupPrivilegeValue(NULL, "SeDebugPrivilege", &tkp.Privileges[0].Luid))
  {
    if (hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))
    {
      if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken))
      {
        if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL))
        {
          CloseHandle(hToken);
        }
      }
      CloseHandle(hProcess);
    }
  }
}

posted @ 2007-08-22 00:04 紫雨轩 C++ 阅读(1364) | 评论 (0)编辑 收藏

#define PROTECTED_DACL_SECURITY_INFORMATION (0x80000000L)

BOOL Lock_CurrentProcess()
{
  HANDLE hProcess = ::GetCurrentProcess();
  SID_IDENTIFIER_AUTHORITY sia = SECURITY_WORLD_SID_AUTHORITY;
  PSID pSid;
  BOOL bSus = FALSE;
  bSus = ::AllocateAndInitializeSid(&sia,1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&pSid);
  if(!bSus) goto Cleanup;
  HANDLE hToken;
  bSus = ::OpenProcessToken(hProcess,TOKEN_QUERY,&hToken);
  if(!bSus) goto Cleanup;
  DWORD dwReturnLength;
  ::GetTokenInformation(hToken,TokenUser,NULL,NULL,&dwReturnLength);
  if(dwReturnLength > 0x400) goto Cleanup;
  LPVOID TokenInformation;
  TokenInformation = ::LocalAlloc(LPTR,0x400);//这里就引用SDK的函数不引用CRT的了
  DWORD dw;
  bSus = ::GetTokenInformation(hToken,TokenUser,TokenInformation,0x400,&dw);
  if(!bSus) goto Cleanup;
  PTOKEN_USER pTokenUser = (PTOKEN_USER)TokenInformation;
  BYTE Buf[0x200];
  PACL pAcl = (PACL)&Buf;
  bSus = ::InitializeAcl(pAcl,1024,ACL_REVISION);
  if(!bSus) goto Cleanup;
  bSus = ::AddAccessDeniedAce(pAcl,ACL_REVISION,0x000000FA,pSid);
  if(!bSus) goto Cleanup;
  bSus = ::AddAccessAllowedAce(pAcl,ACL_REVISION,0x00100701,pTokenUser->User.Sid);
  if(!bSus) goto Cleanup;
  if(::SetSecurityInfo(hProcess,SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,NULL,NULL,pAcl,NULL) == 0)
    bSus = TRUE;
Cleanup:
  if(hProcess != NULL)
    ::CloseHandle(hProcess);
  if(pSid != NULL)
    ::FreeSid(pSid);
  return bSus;
}

这段代码就可以锁住其他进程打开本进程,当然也就防止了注入,和读写内存.

可以更绝点Denied ALL ACCESS(0xFFFFFFFF)就连结束都不可能了

::AllocateAndInitializeSid 可以换成 :: InitializeSid .因为我们并不需要初始化子Sid.
另外.
  bSus = ::AddAccessDeniedAce(pAcl,ACL_REVISION,0x000000FA,pSid);
  if(!bSus) goto Cleanup;
  bSus = ::AddAccessAllowedAce(pAcl,ACL_REVISION,0x00100701,pTokenUser->User.Sid);
实际上只需要下面的一句,或者干脆把它去掉,因为如果不添加Ace默认就是没有权限.既然这样上面的那句话AllocateAndInitializeSid 也可以省掉,也似乎有些多余

posted @ 2007-08-22 00:03 紫雨轩 C++ 阅读(3231) | 评论 (2)编辑 收藏


2007年8月21日

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
  <assemblyIdentity version="1.0.0.0"
     processorArchitecture="X86"
     name="IsUserAdmin"
     type="win32"/> 
  <description>QQUpdateCenter</description> 
  <!-- Identify the application security requirements. -->
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"/>
        </requestedPrivileges>
       </security>
  </trustInfo>
</assembly>

这段代码放到manifest资源中。

posted @ 2007-08-21 23:58 紫雨轩 C++ 阅读(792) | 评论 (0)编辑 收藏


2006年10月28日


正常的情况下,我们写一个 C/C++ 函数,即使是一个空函数,编译器也为我们做了不少的工作,生成了一些“必要”的代码。请看下面的函数 (为了说明问题随便写的):


int Test()
{
    int iReturn;
    char szTemp[33];
   
    szTemp[0] = 'A';
    szTemp[1] = '';
    iReturn = MessageBox(NULL, szTemp, szTemp, MB_OK);
    MessageBeep(iReturn);
    return iReturn;
 }

下面是用 VC6 在 Release 方式下编译后的的反汇编代码:


00401000   sub         esp,24h     // 增加堆栈空间存放局部变量 (24H = 36D,4 字节对齐,注意这里没有为 iReturn 分配空间)
00401003   push        esi         // 保存要使用的重要寄存器
00401004   lea         eax,[esp+4] // 下面是传递 MessageBox() 要使用的参数
00401008   push        0
0040100A   lea         ecx,[esp+8] // 编译器愚蠢,根本不用 ECX,两个都是 szTemp,两次 PUSH EAX 不得了
0040100E   push        eax
0040100F   push        ecx
00401010   push        0
00401012   mov         byte ptr [esp+14h],41h
00401017   mov         byte ptr [esp+15h],0
0040101C   call        dword ptr ds:[40509Ch] // 调用 MessageBox()
00401022   mov         esi,eax     // 保存返回值到变量 iReturn 。靠!变量 iReturn 自动使用 ESI,编译器太聪明了:)
00401024   push        esi
00401025   call        dword ptr ds:[4050A0h] // 调用 MessageBeep()
0040102B   mov         eax,esi     // 把变量 iReturn 交给 EAX 作为返回值
0040102D   pop         esi         // 恢复要使用的重要寄存器
0040102E   add         esp,24h     // 减少堆栈空间
00401031   ret                     // 堆栈长度减 4 并返回


这段代码虽然很精干 (都能自动使用寄存器来保存变量了),但是有的时候我们并不需要编译器提供这些自作主张的代码 (比如写驱动程序的时候,不过我还没遇到过这种情况,呵呵~~),我们希望整个全部函数都是自己亲手写进去的 (BT 呀^o^)。好,请出今天的主角 —— “naked”(怎么是裸体呀?),欢迎!Visual C++ 的扩展关键字 naked 允许我们完全定制一个函数,废话不说了,看例子 (熬夜写的 Zzzzzz~~):


__declspec(naked) int Test()
{
    __asm
    {
        SUB         ESP,24H
        PUSH        ESI
        LEA         EAX,[ESP+4]
        PUSH        0                      
        PUSH        EAX                    
        PUSH        EAX                    
        PUSH        0                      
        MOV         BYTE PTR [ESP+14H],41H
        MOV         BYTE PTR [ESP+15H],0  
        CALL        DWORD PTR [MessageBoxA]
        MOV         ESI,EAX
        PUSH        ESI                    
        CALL        DWORD PTR [MessageBeep]
        MOV         EAX,ESI
        POP         ESI
        ADD         ESP,24H
        RET
     }
 }

上面的代码是使用的 VC 的内联汇编,和 VC 编译后生成的代码完全是一样的 (很有完全控制的成就感吧^_^)。上面我们并没有又节省什么 (节省的 PUSH ECX 并不是 naked 的功劳),但是有的时候确实需要的 (举不出例子来了,倒!)。最后随便说说注意事项:

1.使用 naked 关键字需要自己构建 EBP 参数指针 (如果用到了 EBP 作为参数指针的话);
2.必须自己使用 RET 或 RET n 指令返回 (除非你不返回)。

posted @ 2006-10-28 20:02 紫雨轩 C++ 阅读(3024) | 评论 (13)编辑 收藏


尽管 C 和 C++ 都已经有标准,但是几乎每个编译器 (广义,包含连接器等) 扩展一些 C/C++ 关键字。合理地应用这些关键字,有时候能使我们的工作非常方便。下面随便说说 Visual C++ 中 #pragma 指示符的使用。
一、用#pragma导出DLL函数

传统的到出 DLL 函数的方法是使用模块定义文件 (.def),Visual C++ 提供了更简洁方便的方法,那就是“__declspec()”关键字后面跟“dllexport”,告诉连接去要导出这个函数,例如:


__declspec(dllexport) int __stdcall MyExportFunction(int iTest);

把“__declspec(dllexport)”放在函数声明的最前面,连接生成的 DLL 就会导出函数“_MyExportFunction@4”。

上面的导出函数的名称也许不是我的希望的,我们希望导出的是原版的“MyExportFunction”。还好,VC 提供了一个预处理指示符“#pragma”来指定连接选项 (不仅仅是这一个功能,还有很多指示功能) ,如下:


#pragma comment(linker,"/EXPORT:MyExportFunction=_MyExportFunction@4")

这下就天如人愿了:)。如果你想指定导出的顺序,或者只将函数导出为序号,没有 Entryname,这个预处理指示符 (确切地说是连接器) 都能够实现,看看 MSDN 的语法说明:


/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]

@ordinal 指定顺序;NONAME 指定只将函数导出为序号;DATA 关键字指定导出项为数据项。

二、指示文件只包含一次

在头文件中,一般在整个工程中我们只要包含一次就够了,但是如果我在多个 .c/.cpp 文件中都要包含着个头文件,比如 Windows.h,那很多声明等等岂不是有两次了?解决这个问题的传统的方法是在头文件开始出用 #define 定义一个宏,比如 Windows.h 中:

#ifndef _WINDOWS_
#define _WINDOWS_



  然后在文件结为加上 #endif,这样就可以避免被包含多次。但是这样的后果是代码的可读性较差 (个人观点),VC 给我们提供了另外一个途径,那就是在文件的前面加上:






#pragma once





   是不是很方便?





三、使警告无效




   有时候我们不得不对变量进行强制转换,由此引来编译器的一番警告,特别是 C++ 中,类型检查相对于 C 更为严格。这虽然不影响什么,但是看起来多不爽——我是故意要这样的,你警告什么!:)这时候你看到警告类型,比如“warning C4311: “类型转换” : 从“HHOOK”到“BOOL”的指针截断”,在前面加上:






#pragma warning(disable: 4311)



  编译器就没话说了:)。





四、指定连接要使用的库





   比如我们连接的时候用到了 WSock32.lib,你当然可以不辞辛苦地把它加入到你的工程中。但是我觉得更方便的方法是使用 #pragma 指示符,指定要连接的库:





#pragma comment(lib, "WSock32.lib")





五、显示编译消息


    没多少用处,举个例子吧:





#ifdef _DEBUG
#pragma message("编译连接为调试模式...")
#endif // _DEBUG



posted @ 2006-10-28 20:01 紫雨轩 C++ 阅读(1117) | 评论 (2)编辑 收藏


2006年7月15日

算法如下: 

先随机取两个任意的小写字符接拼密码,然后取这个字符串的MD5,最后密码就为小写字符拼接整个字符串的MD5。示例如下: 

me(随机的)+netcat(密码)=>menetcat(随机字符拼接密码)=>39e3d88335cedfd488b5ba2e838d9623(menetcat的MD5码)=>me39e3d88335cedfd488b5ba2e838d9623(netcat的最终加密密码) 

修改好后在INI文件中的GLOBAL下面加一句ReloadSettings=True(修改好INI后这一句是一定要加的,否则Serv-U程序就不会重新加载新的配置文件了)就行了,一会你再打开这个INI会发现这句已经不见了,那是因为Serv-U的程序已经重新加载INI配置文件后并把这句自动删除了,也就说明你以后每做一次修改都要加上这句话,否则系统就不会重新加载配置文件的。 

如果谁要做一个Serv-U的Web管理程序,就可以分析一个这个INI的配置文件,分析一下修改配置后的一些变化规则,然后就可以通过Web程序自己去编辑了。

posted @ 2006-07-15 01:14 紫雨轩 C++ 阅读(694) | 评论 (0)编辑 收藏

编码方式
编码方式
下面的字节串用来表示一个字符。用什么串依照该字符在 UCS 编码中的序号来定: 
0x00000000 - 0x0000007F:
0xxxxxxx 
0x00000080 - 0x000007FF:
110xxxxx 10xxxxxx 
0x00000800 - 0x0000FFFF:
1110xxxx 10xxxxxx 10xxxxxx 
0x00010000 - 0x001FFFFF:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
0x00200000 - 0x03FFFFFF:
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
0x04000000 - 0x7FFFFFFF:
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
这里 xxx 的位置二进制位形式的字符编码填入。只用最短的那个足够表达一个字符编码数的多字节串。   

举例说明
Unicode 字符 0xa9 = 1010 1001 (版权所有的符号) 在 UTF-8 中被编码为: 

11000010 10101001 = 0xc2 0xa9 
字符0x2260 = 0010 0010 0110 0000 (“不等于”符号)被编码为: 


11100010 10001001 10100000 = 0xe2 0x89 0xa0 


''encode - 转成16进制符 

For i = 1 To Len(strIN) 
s = CStr(Hex(Asc(Mid(strIN, i, 1)))) ''是不是太~~~快了~~ 
If Len(s) = 1 Then s = "0" & s ''解出小于F的值就在前面加 0 
strOUT = strOUT & "%" & s 
Next i 

txtOUT.Text = strOUT ''这里就是输出了 
___________________________________________________________ 

''decode - 解码, 比较麻烦点 

For i = 1 To Len(strIN) Step 3 '' Step 3 因为格式是 %XX 三位一个字符 
s1 = UCase(Mid(strIN, i + 1, 1)) ''得到第2位的16进制数字 
s2 = UCase(Mid(strIN, i + 2, 1)) ''得到第1位的16进制数字 

select Case s1 '' 进制转换 
Case "A" To "F" 
n = (10 + 5 - (Asc("F") - Asc(s1))) * 16 
Case Else 
n = Val(s1) * 16 
End select 

select Case s2 
Case "A" To "F" 
n = n + (10 + 5 - (Asc("F") - Asc(s2))) 
Case Else 
n = n + Val(s2) 
End select 

s = ChrW$(n) '' 转为字符 
strOUT = strOUT & s 
Next i 
txtOUT.Text = strOUT 
  

怎么把字符串转成UTF-8格式????  
 
下面的代码不成功:  
WCHAR  wstr[10];  
char  cstr[20];  
memset(  wstr,  0,  sizeof(wstr)  );  
MultiByteToWideChar(CP_UTF8,  0,  "我是小于",  -1,  wstr,  sizeof(wstr));  
WideCharToMultiByte(CP_ACP,  0,  wstr,  -1,  cstr,  -1,  "",  0);  
 
求助。。。。  
---------------------------------------------------------------  
 
int    main(void)      
{      
           char    str    [    256    ]    =    "唐志国"    ;        //一段UTF-8编码      
           WCHAR*    strA;      
           int    i=    MultiByteToWideChar    (  CP_ACP      ,    0    ,(char*)    str    ,-1    ,NULL,0);      
           strA    =    new    WCHAR[i];      
           MultiByteToWideChar    (    CP_ACP    ,    0    ,(    char    *    )    str,    -1,    strA    ,    i);      
             
           i=    WideCharToMultiByte(CP_UTF8,0,strA,-1,NULL,0,NULL,NULL);      
           char    *strB=new    char[i];      
           WideCharToMultiByte    (CP_UTF8,0,strA,-1,strB,i,NULL,NULL);      
           //strB即为所求      
           delete    []strA;      
           delete    []strB;      
           return    0;      
}      
 
---------------------------------------------------------------  
 
这是转换函数:  
           int            CodePageConvert(UINT  SrcCodePage,  LPCTSTR  pBuff,  int  iBuffLen,  UINT  DestCodePage,  char*  &lpCodePage)  
           {  
                       int  iWideCharCnt  =  ::MultiByteToWideChar(SrcCodePage,  0,  pBuff,  iBuffLen,  NULL,  0);  
                       LPWSTR  lpszWideChar  =  new  wchar_t[iWideCharCnt  +  1];  
                       memset(lpszWideChar,  0,  (iWideCharCnt  +  1)  *  sizeof(WCHAR));  
                       iWideCharCnt  =  MultiByteToWideChar(SrcCodePage,  0,  pBuff,  iBuffLen,  lpszWideChar,  iWideCharCnt);  
 
                       if(DestCodePage  ==  54936    
                                               &&  !IsValidCodePage(54936))  
                                   DestCodePage  =  936;  
 
                       int  iDestCnt  =  WideCharToMultiByte(DestCodePage,  0,  lpszWideChar,  iWideCharCnt,  NULL,  0,  NULL,  NULL);  
                       lpCodePage  =  new  char[iDestCnt  +  1];  
                       memset(lpCodePage,  0,  iDestCnt  +  1);  
                       iDestCnt  =  WideCharToMultiByte(DestCodePage,  0,  lpszWideChar,  iWideCharCnt,  lpCodePage,  iDestCnt,  NULL,  NULL);  
 
                       delete  []lpszWideChar;              
                       return  iDestCnt;  
           }  
下面是调用方法:  
     utf-8  到  gbk  
           int  nLen  =  CodePageConvertUnix("UTF-8",_T("标准"),2,"GBK",lpOut);  
     gbk  到utf-8  
int  nLen  =  CodePageConvertUnix("UTF-8",_T("标准"),2,"GBK",lpOut);  
 

posted @ 2006-07-15 01:13 紫雨轩 C++ 阅读(1834) | 评论 (3)编辑 收藏

早上到公司msn死活登录不上去,报错信息就是80048820错误,狗狗了一下,

有一种情况就是是由于你的系统时间不对,先校准系统时间,如果仍然无法登录,可能是由于DLL文件注册信息丢失,下载这个批处理文件 
http://www.msn-problems.com/downloads/ ... ger-dll-files.bat 运行一下,问题也可以解决.
该bat内容如下.
请复制,粘贴到记事本,然后另存为msn.bat或作msn.cmd运行一次就可以了
@ECHO OFF
ECHO.如果你msn中修复不了就运行吧.
ECHO 
*****************************************************************
ECHO 
* This file is provided by msn-problems.com *
ECHO 
* Copyright (C) 2004-2005 *
ECHO 
*****************************************************************
ECHO.
ECHO 等一下.

REM Following files only needed 
in windows XP
IF NOT 
"%os%"=="Windows_NT" GOTO WIN9X
regsvr32 Dssenh.dll 
/s
regsvr32 Gpkcsp.dll 
/s
regsvr32 Slbcsp.dll 
/s
regsvr32 Sccbase.dll 
/s

:WIN9X
IF NOT 
"%os%"=="Windows_NT" CD %windir%\System\
REM Needed by both XP and 9X
regsvr32 Softpub.dll 
/s
regsvr32 Wintrust.dll 
/s
regsvr32 Initpki.dll 
/s
regsvr32 Rsaenh.dll 
/s
regsvr32 Mssip32.dll 
/s
regsvr32 Cryptdlg.dll 
/s
regsvr32 Msxml3.dll 
/s
ECHO.
ECHO 搞定,还不可以到这网站的论坛询问 www.msn
-problems.com
ECHO.
pause
我用系统的时间同步自动校对时间,下了那个批处理的文件执行,还是不好。
不小心把 那个时间同步的勾给去掉了,msn一下就登录上去了。
我把msn退出。把时间同步的勾打上。msn还是登录成功了。
有点莫名其妙,不过能登陆就算好了。:P

在网上还看到两个个性的解决方法,他们都是修改时间,和执行批处理后仍然无法登录的。
一个是改 机器 IP后就能登录了。

另一个是卡巴斯基地病毒库被破坏了,卸载了卡巴斯基换上了最新版本,再次登录MSN发现问题解决了。

今天又遇到这个问题:
怎么都登录不上去,
后来下载了安装了 msn 8 ,然后运行那个 bat ,就可以登录了。

posted @ 2006-07-15 01:09 紫雨轩 C++ 阅读(2336) | 评论 (0)编辑 收藏


仅列出标题  下一页

posts - 18, comments - 22, trackbacks - 0, articles - 7

Copyright © 紫雨轩 C++