MicroSoft编译器你不可不知的秘密

来源:互联网 发布:淘宝上卖的星巴克杯子 编辑:程序博客网 时间:2024/06/11 07:32

:  【原创】MicroSoft编译器你不可不知的秘密
: 黑猫①号
:  2012-12-15 23:33
转载请注明出处  :  http://blog.csdn.net/jha334201553/article/details/8300153

注:

1.  本文是写给调用dll原理不太清楚,以及不知道怎么调用未声明但是已经导出的windows API(例如Native API),大牛自动略过本文。

2.  一些基础的知识不会介绍,例如什么是函数调用约定(如:__stdcall、__cdecl、__fastcall等),为什么要加extern  “C” 、 #ifdef  __cplusplus。也不会介绍一些很容易百度(谷歌)到的知识。


用控制台直接编译程序

以下演示内容全部通过控制台编译,在开始之前先大概的说下环境(加这部分是因为本文内容就是有关导出函数环境的O(∩_∩)O~ )。

VC6其实可以免安装使用的,将bin、lib、include文件夹( 如果想要alt、mfc功能,那把他们的文件也一起 )直接复制到U盘中走到哪带到哪,再写个脚本设置下环境变量,就可以随心编译。(PS: 我用的控制台编译,之前我就说过了)。脚本如下形式:

@echo offset path =%path%;%cd%\bin;set include=%cd%\Include;%cd%\MFC\include;%cd%\ATL\Include;set lib=%cd%\lib;%cd%\MFC\Lib;cmd.exe


头文件、lib、dll是怎么回事

a.       头文件

int main(int argc, char* argv){        return 0;}

以上代码直接在控制台编译通过很正常。那以下呢?

int main(int argc, char* argv){        printf(“hello world!\n”);        return 0;}

编译器会拒绝编译,显示printf函数未定义,通常的做法是加入stdio.h头文件。可是,如果不加这个头文件呢,可否有方案解决这个文件?答案是:有。

在调用printf之前加入声明、编译时加入msvcrt.lib即可。

#pragma  comment(lib, "MSVCRT")#ifdef  __cplusplusextern "C" {#endif int printf( const char* format , ...  );#ifdef  __cplusplus};#endif int main(int argc, char* argv){        printf("hello world!\n");        return 0;}

命令行定位到文件所在位置,然后输入  cl  *.cpp   即可编译出exe程序来(虽然有些警告信息o(╯□╰)o  )

提示:  .h头文件中是各种函数的声明部分,使程序在编译的时候可以找到函数,这样编译阶段就不会报undefined错误了。而lib文件呢,是在链接的时候根据编译阶段生成的obj文件中指明的label到lib文件中找这个函数,如果lib为静态库(是指代码在lib中,而没有生成dll,见示例代码 ),则连接器会把lib中的代码复制到可执行文件中,如果lib是动态库,则从lib文件中找这个函数的导出名与跟这个lib库一起编译出来的动态库名称,写入导入表(动态库中其实只需要两个参数,一个dll名称,一个原函数名称)。


b.       Lib与dll文件

自己写一个user32.dll动态库调用里面的MessageBoxA调用会是什么情况呢?

User32.asm内容(以下为汇编版本,在下面的下载包中有个C语言版)

.386           .model     flat,stdcall           option     casemap :none    .code;DLL入口DllEntry    proc   _hInstance,_dwReason,_dwReserved                    mov   eax,1                    retDllEntry    Endp; 导出函数MessageBoxA  proc        _dwNumber1:DWORD,                         _dwNumber2:DWORD,                         _dwNumber3:DWORD,                         _dwNumber4:DWORD                    xor   eax,eax                    retMessageBoxA  endp End            DllEntry

User32.def文件内容(一些人喜欢用__declspec( dllexport ) 导出函数,我喜欢用def文件申明,这样我的dll可以导出自己想要的任何名字,还可以选择不导出名字)

EXPORTS               MessageBoxA

编译命令     ml /c  /coff  /Cp  *.asm 

                     link  /section:.bss,S  /dll  /def:user32.def  /subsystem:windows  *.obj

生成一个user32.lib,将此文件重命名为myuser32.lib (防止于编译器库中的user32.lib 冲突(原user32.lib 后面有用))。

再创建一个测试的cpp文件:

#pragma comment(lib,"myuser32.lib") #ifdef __cplusplusextern "C" {#endif        int __stdcall MessageBoxA(unsigned long,                          char*,                          char*,                          unsigned long ); #ifdef __cplusplus};#endif int main(){         MessageBoxA(0, "你好", "标题栏", 0);         return 0;}

编译命令: cl  *.cpp

运行下编译出来的程序,呀!糟糕,为什么运行的时候出现了一个MessageBox对话框呢?难道没有调用我们自己写的user32.dll么?但是链接的时候明明是链接我们的lib文件的呀!!!这时候你是不是觉得很奇怪?这个问题留给你了(跟PE格式、系统有关)。(可以参考win loader (注:不是boot loader,是可执行文件加载器))


调用库lib中没有的函数

记得网上一直流传着一个函数叫 SetLayeredWindowAttributes 可以改变窗体,可是调用这个函数的方法都是先LoadLibrary 再 GetProAddress 地址以后调用的,那么可以利用上面调用MessageBoxA的方法去调用这个函数了,再在上面的代码中加入如下代码:

SetLayeredWindowAttributes      proc        _dwNumber1:DWORD,                                            _dwNumber2:DWORD,                                            _dwNumber3:DWORD,                                            _dwNumber4:DWORD                                                                                                                               xor   eax,eax                                   retSetLayeredWindowAttributes      endp

导出函数也相应的加一个SetLayeredWindowAttributes

编译出user32.lib文件,重新命名为myuser32.lib


再写一个窗体程序,直接加入SetLayeredWindowAttributes声明,myuser32.lib库 后调用,编译通过。

#pragma comment(lib,"myuser32.lib")#pragma comment(lib,"user32.lib") #include <windows.h> #ifdef __cplusplusextern "C" {#endif long WINAPI SetLayeredWindowAttributes(   HWND  hWnd,                                          int   cr,                                          unsigned char bAlpha,                                          unsigned long dwFlags);#ifdef __cplusplus};#endif//…… 略

完整的示例代码见下载包(MicroSoft编译器你不可不知的秘密\自己构建lib文件调用\窗口透明

 

调用Native API

所谓的应用层Native API就是ntdll导出的函数。这些函数大部分是直接通过调用门进入系统内核运行的,在ring3中是比较底层的函数(或许自己利用int 0x2e、sysenter进入内核才算底层吧),对于杀毒软件的主动防御有很好的免杀作用(比如ReadProcessMemory,CreateRemoteThread替换成更底层的NtReadVirtualMemory、NtCreateThread可以解决ring3的主动防御、导入表查杀)。所以Native API在安全方面有着重要的作用。

下载包中带了几个ntdll.lib文件(MicroSoft编译器你不可不知的秘密\直接调用ntdll函数\ntdll),可以接加入工程链接,如果发现这个lib中没有的函数,那么利用上面说的方法自己创建一个ntdll.lib文件导入工程。

例子代码中有对NtAllocateVirtualMemory的调用,其他函数方法类似,关于函数的声明直接到wdk文档中找,或者微软在线帮助(把函数名改成Zw开头,如ZwAllocateVirtualMemory)

#pragma once#ifndef __NTDLL__HH_#define __NTDLL__HH_ #pragma comment(lib, "ntdll")#include<windows.h> typedef unsigned long* ULONG_PTR;#define NTSTATUS  LONG #ifdef  __cplusplusextern"C"{#endif NTSTATUS WINAPINtAllocateVirtualMemory(                         HANDLE  ProcessHandle,                         PVOID*  BaseAddress,                         ULONG_PTR ZeroBits,                         PSIZE_T  RegionSize,                         ULONG  AllocationType,                         ULONG Protect                       ); #ifdef  __cplusplus};#endif #endif // __NTDLL__HH_
可以写个ntdll.h的头文件,以后要用ntdll函数的时候就添加这个头文件,没有的函数就继续加进去,这样这个头文件就日渐的完善了。不过像CreateRemoteThread这种函数在ntdll中实现还是比较蛋疼的,要想让这个函数跟原来的运行效果一样还要下一点功夫才行,涉及的函数涉及的数据相当多,不过奖品也是很丰盛的,你用你自己实现方法,360就不会吭声了。


示例程序下载地址:http://ishare.iask.sina.com.cn/f/35074546.html

 

原创粉丝点击