Phalcon学习笔记(2)DI依赖注入器和加载器

Phalcon启动过程中,需要使用到各种各样的类,这些类都是通过DI依赖注入器配置,然后通过加载器载入的。


加载器的配置非常简单,它主要配置3个参数:

1)命名空间。遍历某个目录,并将其下的类都注册为某命名空间的类,以便在需要的时候将其加载。


$loader = new \Phalcon\Loader();

        $loader->registerNamespaces(array(
            'Entrance\Controllers' => __DIR__ . '/controllers/',
            'Entrance\Models' => __DIR__ . '/models/',
        ));

2)目录。将某个目录下的所有后缀名为php的文件全部识别出来,以便在需要的时候将其加载。


$loader = new \Phalcon\Loader();

        $loader->registerNamespaces(array(
            'Multiple\Entrance\Controllers' => __DIR__ . '/controllers/',
            'Multiple\Entrance\Models' => __DIR__ . '/models/',
        ))->registerDirs(array(
            __DIR__ . '/controllers/',
            __DIR__ . '/models/',
        ));

3)类。将某个文件内的类全部识别出来,以便需要的时候加载。

$loader = new \Phalcon\Loader();

        $loader->registerNamespaces(array(
            'Multiple\Entrance\Controllers' => __DIR__ . '/controllers/',
            'Multiple\Entrance\Models' => __DIR__ . '/models/',
        ))->registerDirs(array(
            __DIR__ . '/controllers/',
            __DIR__ . '/models/',
        ))->registerClasses(array(
        __DIR__.'/extlibrary1/content.php',
        __DIR__.'/extlibrary2/lib2.php'
))->register();

加载器类还支持连续操作,最后加上register()函数完成注册,剩下的就不需要管了,只要加载器在Application入口类之前声明并配置,Application类就会自动使用,这样我们需要某个类的时候就不用非得在文件头部include了,如果类比较少,还可以手动处理,如果特别多每次手动include就太烦了。一般我们要把Controller和Model类所在的目录加入,如果有其他外部库,也要一并加入。但是一般不加入View的目录,因为View目录下一般不是类。


DI依赖注入器才是真正需要复杂配置的,而且是Application类能够运行的必备参数(加载器不是必备的,理论上,你可以手动加载每一个类)。


根据上一篇所述的启动过程,DI至少需要配置

1)路由器router。http服务器的URL rewrite负责将浏览器客户端发送来的URL解析成index.php入口文件和相关参数,路由器负责将相关参数详细解释,以便主程序能够根据相关参数提供相关运算。

$di = new \Phalcon\DI\FactoryDefault();
$di->set('router', function () {

            $router = new \Phalcon\Mvc\Router();

            $router->add("/:module/:controller/:action/:params", array(
                'module' => 1,
                'controller' => 2,
                'action' => 3,
                'params' => 4,
            ));
            $router->setDefaultModule("entrance");
            $router->setDefaultController("index");
            $router->setDefaultAction("index");
            $router->notFound(
                array(
                    "module" => "entrance",
                    "controller" => "index",
                    "action" => "index",
                )
            );
            return $router;
 }

DI各项的配置都可以使用匿名函数,反正只配置一次,以后一般只有极特殊的情况才需要重新配置。另外配置的时候还需要考虑各种可能的因素,比如对URL的识别是否会有歧义等。这里所列的是针对多模块的程序所配置的,如果没有多模块,那么可以考虑去掉module部分


2)分发器dispatcher。路由器得到相关参数后,要由分发器分发给各个控制器来执行。默认情况下,不需要过多的配置,会按照路由器识别的Controler、Action和Params来执行,如果你需要增加一些其他的内容,比如识别用户权限之类的,就需要增加事件机制进行处理,事件处理的说明会另外撰文。

$di->set('dispatcher', function () {
            $dispatcher = new \Phalcon\Mvc\Dispatcher();

            //Attach a event listener to the dispatcher
            $eventManager = new \Phalcon\Events\Manager();
            $eventManager->attach('dispatch:beforeDispatch', new \SecurityPlugin(__CLASS__));

            $dispatcher->setEventsManager($eventManager);
            $dispatcher->setDefaultNamespace('Multiple\Entrance\Controllers\\');
            return $dispatcher;
        });

3)视图view。由于视图没有在加载器中配置,所以在依赖注入器中必须要说明。下面的代码中所示的设置中BasePath和ViewsDir是必须设置的,否则程序会找不到视图文件,也就没有办法输出页面。至于解析引擎,Phalcon自行开发了一套模板引擎Volt,类似于twig,基本语法都是跟twig相同,只有Phalcon相关的函数会特殊处理。另外还可以配置其他的引擎,甚至你自己的模板引擎。

$di->set('view', function () {
            $view = new \Phalcon\Mvc\View();
            $view->setBasePath('./entrance/');
            $view->setViewsDir('./views/');
            $view->registerEngines(array(
                '.volt' => 'Phalcon\Mvc\View\Engine\Volt',
                ".phtml" => 'Phalcon\Mvc\View\Engine\Php',
            ));
            return $view;
        });

除了这3项必须的配置以外,还有一些其他的配置也很常用

4)配置config。配置是Phalcon提供的一个集中管理站点配置的类,使用起来比较方便,更多的内容以后再写。

require_once ROOT_PATH . '/library/Config.php';
        $di->setShared('config', $config);
5)URL。如果你的站点不是放在服务器的根目录,就需要增加一些配置,这样在Phalcon生成各种URL的时候都会把你的目录加在URL上,用户在点击的时候才不会出错。

$di->set('url', function () {
            $url = new Phalcon\Mvc\Url();
            $url->setBaseUri('/myfolder/');
            return $url;
        });
6)会话session。涉及到用户交互的化,会话是不可缺少的功能,当然Phalcon也支持cookie

$di->setShared('session', function () {
            $session = new Phalcon\Session\Adapter\Files();
            $session->start();
            return $session;
        });
7)数据库db。现代的站点基本上都会要使用到数据库做管理,下面的代码还捎带着展示了一下配置config的用法,而且还是动态调用函数的方式


        $di->set('db', function () {
            return new $dbadapter(array(
                $config=$this->di->get('config');
<pre name="code" class="php">                $dbadapter = '\Phalcon\Db\Adapter\Pdo\\' . $config->database->adapter;
  "host" => $config->database->host, "username" => $config->database->username, "password" => $config->database->password, "dbname" => $config->database->dbname, "options" => $config->database->options, )); });
 对于多个module的站点,DI还可以针对不同的module来设置,默认情况下是针对当前module来设置的,也可以通过setShared('var', 'value')方法实现共享设置,一般用于config。多模块的设置以后再撰文。 


在启动类Application中,可以使用

$this->setDI($di);

将依赖注入器关联到启动程序中。也可以在Application的构造函数中把DI作为参数传入。一般来讲,如果Application类没有什么特别配置,采用Phalcon默认的情况下,采用构造函数的方法比较简单;如果你需要扩展一下Application类的功能,比如对异常错误信息进行一些处理,或者加载多模块,那么使用setDI()方式会比较方便,甚至可以在自定义的构造函数中使用匿名函数创建DI,然后在DI的设置中再使用匿名函数创建其他配置类,多层嵌套也是没问题的。






  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
远程注入DLL方法有很多种,也是很多木马病毒所使用的隐藏进程的方法,因为通过程序加载的DLL在进程管理是没有显示的.这里介绍一种用 CreateRemoteThread 远程建立线程的方式注入DLL. 首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的. function EnableDebugPriv: Boolean; var hToken: THandle; tp: TTokenPrivileges; rl: Cardinal; begin Result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 Result := AdjustTokenPrivileges(hToken, false, tp, SizeOf(tp), nil, rl); end; end; 关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍: OpenProcessToken():获得进程访问令牌的句柄. function OpenProcessToken( ProcessHandle: THandle; //要修改访问权限的进程句柄 DesiredAccess: DWORD; //指定你要进行的操作类型 var TokenHandle: THandle//返回的访问令牌指针 ): BOOL; AdjustTokenPrivileges() :调整进程的权限. function AdjustTokenPrivileges( TokenHandle: THandle; // 访问令牌的句柄 DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges; { 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组, 数据组的每个项指明了权限的类型和要进行的操作; } BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD //实际PreviousState结构返回的大小 ) : BOOL; 远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入的 DLL 的路径. 需要用到的 API 函数有: OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDN function OpenProcess( dwDesiredAccess: DWORD; // 希望获得的访问权限 bInheritHandle: BOOL; // 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD // 要访问的进程ID ): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名 function VirtualAllocEx( hProcess: THandle; // 申请内存所在的进程句柄 lpAddress: Pointer; // 保留页面的内存地址;一般用nil自动分配 dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD ): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名 function WriteProcessMemory( hProcess: THandle; //要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; //要写入的目标进程的内存指针, 需以 VirtualAllocEx() 来申请 lpBuffer: Pointer; //要写入的数据 nSize: DWORD; //写入数据的大小 var lpNumberOfBytesWritten: DWORD //实际写入的大小 ): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLL. CreateRemoteThread() //在一个远程进程中建立线程 function CreateRemoteThread( hProcess: THandle; //远程进程的句柄 lpThreadAttributes: Pointer; //线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD; //线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine; // 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; //传入参数的指针 dwCreationFlags: DWORD; //创建线程的其它标志 var lpThreadId: DWORD //线程身份标志,如果为0, 则不返回 ): THandle; 整个远程注入DLL的具体实现代码如下: function InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: Pointer; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入的dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread 0) then Result := true; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end; 接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary,代码如下: function UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; // 进程注入和取消注入其实都差不多,只是运行的函数不同而已 var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: PChar; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入的dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 如果执行成功返回 True; if hRemoteProcess 0 then Result := true; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath) + 1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值