• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-90  評論-947  文章-0  trackbacks-0

            突然想到個問題,EXE可否像DLL一樣導(dǎo)出函數(shù)呢?于是就起來做試驗——

             

            靜態(tài)鏈接調(diào)用

            嗯,先建立一個EXE,內(nèi)容很簡單:

             

            #include "stdafx.h"

            #define EXE_LIBRARY

            #include "ExeLibrary.h"

             

            EXE_LIBRARY_API int Sum(int a, int b)

            {

                return a + b;

            }

             

            int APIENTRY _tWinMain(_In_ HINSTANCE     hInstance,

                                   _In_opt_ HINSTANCE hPrevInstance,

                                   _In_ LPTSTR        lpCmdLine,

                                   _In_ int           nCmdShow)

            {

                return 0;

            }

             

            Sum函數(shù)是等下用來測試的。其中ExeLibrary.h 中模仿系統(tǒng)生成的DLL頭文件進(jìn)行了宏定義:

             

            #ifdef EXE_LIBRARY

            #define EXE_LIBRARY_API __declspec(dllexport)

            #else

            #define EXE_LIBRARY_API __declspec(dllimport)

            #endif

             

            EXE_LIBRARY_API int Sum(int a, int b);

             

             

            然后建立另一個EXE

             

            #include "../ExeLibrary/ExeLibrary.h"

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

             

            int main()

            {

                int s = Sum(1, 2);

             

                return s;

            }

             

            然后運(yùn)行:

            clip_image001

             

            能跑,運(yùn)行結(jié)果也正確,說明函數(shù)被正確執(zhí)行。

             

            查了下進(jìn)程列表,木有ExeLibrary.exe

            再查調(diào)試環(huán)境的模塊列表:

            clip_image002

            ExeLibrary赫然在目。

             

            整個運(yùn)行過程中,WinMain 函數(shù)木有進(jìn)入。嘗試

             

            動態(tài)加載調(diào)用

            先為剛才的ExeLibrary添加一個def文件:

             

            LIBRARY

             

            EXPORTS

                Sum

             

            然后新建另一個EXE

             

            #include <Windows.h>

            #include <tchar.h>

             

            int main()

            {

                HMODULE hModule = LoadLibrary(_T("ExeLibrary.exe"));

             

                if (hModule == nullptr)

                {

                    return 0;

                }

             

                typedef int (*FnSum)(int a, int b);

                FnSum Sum = (FnSum)GetProcAddress(hModule, "Sum");

             

                int s = 0;

             

                if (Sum != nullptr)

                {

                    s = Sum(1, 2);

                }

             

                FreeLibrary(hModule);

             

                return s;

            }

             

            運(yùn)行結(jié)果:

             

            clip_image003

             

            同剛才一樣,一切正常。

             

            結(jié)論

            可以像DLL一樣,在EXE里導(dǎo)出函數(shù)。調(diào)用時可以靜態(tài)鏈接也可以動態(tài)加載。EXE只作為一個進(jìn)程內(nèi)的模塊被加載,不會新起一個進(jìn)程。加載過程中EXE中的WinMain函數(shù)不會被調(diào)用。

             

            例程下載:http://pan.baidu.com/s/1mgqTYBI

            posted on 2012-12-01 11:41 溪流 閱讀(8082) 評論(15)  編輯 收藏 引用 所屬分類: Windows

            評論:
            # re: EXE導(dǎo)出函數(shù) 2012-12-01 13:15 | OwnWaterloo
            exe和dll還是有很多區(qū)別的。
            首先entrypoint肯定就被忽略了。
            其次重定位和依賴加載好像也會有問題。

            比如試試這個?
            int error_code;
            __declspec(dllexport) int get_error(void) { return error_code; }
            __declspec(dllimport) void set_error(int x) { error_code = x; }

            get和set產(chǎn)生的指令里會有error_code的地址。
            如果是dll,加載時指令中的地址會被正確地重定位。
            而exe不行,即使保留重定位信息也不行。

            exe可以被加載應(yīng)該是為了里面的資源而不是代碼。
              回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-02 12:09 | 溪流
            @OwnWaterloo
            不懂,為啥例子中一個是export一個是import?  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-02 16:36 | OwnWaterloo
            @溪流
            手誤。。。我的錯。。。  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-02 16:48 | OwnWaterloo
            @溪流
            兩個都是dllexport。兩個函數(shù)產(chǎn)生的代碼都會用上error_code的地址。

            鏈接產(chǎn)生這個dll/exe的時候,是很難確切地知道加載后該dll/exe的地址。同樣也很難確切知道error_code的地址,因為它和dll/exe被加載后的基地址之間的偏移是鏈接后就固定了的。

            于是鏈接器只能先假設(shè)dll/exe會被加載到某個位置(首選基地址),然后根據(jù)它產(chǎn)生代碼。

            比如一種極端情況,將dll/exe復(fù)制一份本地文件(首選基地址相同),然后loadlibrary它倆。
            那么,至少有一個dll/exe是無法被加載到首選基地址的,也就是set/get的指令中使用的地址是不正確的。

            如果是dll,沒有被加載到首選基地址的話,就會發(fā)生重定項。set/get的指令會相應(yīng)的修改。
            而exe,我記得loader就不會做這個工作,于是就。。。
              回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-02 16:50 | OwnWaterloo
            @溪流
            另外,load是不會根據(jù)dll/exe后綴名來判斷是否是dll還是exe。
            它是根據(jù)pe格式中的一個域來判斷的。具體位置我忘了。。。不過dumpbin好像能顯示出來。

            也就是說。。。很多那些后綴是exe(甚至是ocx什么的),而且加載后也能成功調(diào)用里面函數(shù)的文件,其實按pe格式來說都是dll文件,只是后綴名沒有用dll而已。
              回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-02 16:58 | 溪流
            @OwnWaterloo
            懂了~就是說EXE當(dāng)DLL用,不會被重定向咯?改天試下哈  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-02 23:47 | 朱峰everettjf
            當(dāng)時我也做了個類似的研究,
            http://www.shnenglu.com/everett/archive/2012/05/25/176073.aspx
            交流交流  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-04 10:58 | zuhd
            @OwnWaterloo
            我做了個實驗,exe的導(dǎo)出函數(shù)中調(diào)用全局變量也是沒問題的,沒出現(xiàn)樓上所說的崩潰現(xiàn)象,如果按照pe格式來理解的話,即使是exe應(yīng)該也會被重定向吧?  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-04 11:00 | zuhd
            樓主的例子我沒下載成功,用的是朱峰everettjf提供看雪里的例子,我新增加了兩個函數(shù),調(diào)用全局變量。如果需要提供現(xiàn)場的話,留個郵箱。  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-04 15:12 | OwnWaterloo
            @zuhd
            沒有崩潰=沒有問題=程序正確?

            *(int*)隨便寫個什么地址 = 12; // 只要運(yùn)氣好,同樣不會立即崩潰。  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-04 16:13 | OwnWaterloo
            本來應(yīng)該用高級評論將重點高亮的,但不知道cppblog出了什么問題用不了。只能眼睛尖點了。。。

            1. test files

            module.c 導(dǎo)出1個變量與3個函數(shù)

            __declspec(dllexport) int error_code;
            __declspec(dllexport) int get(void) { return error_code; }
            __declspec(dllexport) void set(int x) { error_code = x; }
            int main(void) { return 0; }

            main.c 輸出變量地址、函數(shù)地址以及函數(shù)包含的指令

            #include <stdio.h>

            __declspec(dllimport) int error_code;
            __declspec(dllimport) int get(void);
            __declspec(dllimport) void set(int x);

            int main(void)
            {
            int i;
            unsigned char const* p;

            printf("%p\n", (void*)&error_code);

            p = (unsigned char*)get;
            printf("%p:", p);
            for (i=0; i<12; ++i) printf(" %02X", p[i]);
            printf("\n");

            p = (unsigned char*)set;
            printf("%p:", p);
            for (i=0; i<12; ++i) printf(" %02X", p[i]);
            printf("\n");

            return 0;
            }


            2. dll

            編譯
            cl /LD /O1 module.c /link /noentry

            查看首選基地址
            dumpbin /all module.dll | find /i "image base"
            10000000 image base (10000000 to 10004FFF)

            查看反匯編與重定項
            dumpbin /disasm /relocations module.dll

            File Type: DLL

            10001000: A1 00 30 00 10 mov eax,dword ptr ds:[10003000h]
            10001005: C3 ret
            10001006: 8B 44 24 04 mov eax,dword ptr [esp+4]
            1000100A: A3 00 30 00 10 mov dword ptr ds:[10003000h],eax
            1000100F: C3 ret

            BASE RELOCATIONS #4
            1000 RVA, C SizeOfBlock
            1 HIGHLOW 10003000
            B HIGHLOW 10003000

            10001000 處(get的第1條)指令的操作數(shù)(地址在10001001)是 10003000
            1000100A 處(set的第2條)指令的操作數(shù)(地址在1000100B)也是 10003000
            注意"File Type: DLL",這是根據(jù)PE的域來的。

            編譯并運(yùn)行得到的輸出是
            cl main.c module.lib && main.exe
            10003000
            10001000: A1 00 30 00 10 C3 8B 44 24 04 A3 00
            10001006: 8B 44 24 04 A3 00 30 00 10 C3 00 00

            error_code的地址和指令中使用的地址是相同的。


            3. dll relocation

            上面 module.dll 恰好加載在首選基地址,所以沒有發(fā)生重定項。
            要演示重定項發(fā)生的情況, 可以將 module.dll 復(fù)制一份, 然后用 LoadLibrary 加載。
            或者直接首選基地址為一個會沖突的, exe的默認(rèn)基地址0x400000。

            cl /LD /O1 module.c /link /noentry /base:0x400000

            dumpbin /all module.dll | find /i "image base"
            400000 image base (00400000 to 00404FFF)

            dumpbin /disasm /relocations module.dll
            00401000: A1 00 30 40 00 mov eax,dword ptr ds:[00403000h]
            00401005: C3 ret
            00401006: 8B 44 24 04 mov eax,dword ptr [esp+4]
            0040100A: A3 00 30 40 00 mov dword ptr ds:[00403000h],eax
            0040100F: C3 ret

            BASE RELOCATIONS #4
            1000 RVA, C SizeOfBlock
            1 HIGHLOW 00403000
            B HIGHLOW 00403000

            cl main.c module.lib && main.exe
            00393000
            00391000: A1 00 30 39 00 C3 8B 44 24 04 A3 00
            00391006: 8B 44 24 04 A3 00 30 39 00 C3 00 00

            對比 dumpbin 得到的反匯編與 main.exe 的輸出,可以發(fā)現(xiàn)指令中的操作數(shù)有相應(yīng)的修改,以正確的使用00393000上的變量error_code。


            4. dll fixed

            如果鏈接時選擇基地址固定
            cl /LD /O1 module.c /link /noentry /base:0x400000 /fixed

            產(chǎn)生的dll里就沒有重定項信息
            dumpbin /relocations module.dll

            并且選擇的是一個肯定會沖突的基地址,所以加載main.exe就會失敗。

            main.exe


            5. exe export

            默認(rèn)exe是不會包含重定項信息的
            cl /O1 module.c && dumpbin /relocations module.exe

            File Type: EXECUTABLE IMAGE

            注意"File Type: EXECUTABLE IMAGE",這是根據(jù)PE的域來的。

            并且首選基地址也是沖突的。
            dumpbin /all module.exe | find /i "image base"
            400000 image base (00400000 to 0040BFFF)

            但是讓 main.c 鏈接到 module.exe 可以運(yùn)行成功(之前dll fixed的情況是加載 main.exe 失敗)
            cl main.c module.lib & main.exe
            0039B700
            00391000: A1 00 B7 40 00 C3 8B 44 24 04 A3 00
            00391006: 8B 44 24 04 A3 00 B7 40 00 C3 33 C0

            注意指令里的操作碼,并沒有修改為error_code的地址:0039B700。
            如果真的調(diào)用了get和set,也只是讀寫了其他的地址,而不是error_code。
            bug已經(jīng)產(chǎn)生了。 沒崩只是運(yùn)氣, 那個地址恰好有讀寫權(quán)限。
            而且實驗代碼一般都比較短,跑完馬上就退出了,這種意外的寫入產(chǎn)生的影響也不一定能發(fā)現(xiàn)。

            6. exe export with relocation information

            可以用 /fixed:no 附帶上重定項信息
            cl /O1 module.c /link /fixed:no

            dumpbin /relocations module.exe 會產(chǎn)生很多輸出,因為它還引用了libc。

            而讓 main.c 鏈接到 module.exe 并運(yùn)行的同樣不會發(fā)生重定項
            cl main.c module.lib & main.exe
            0039B700
            00391000: A1 00 B7 40 00 C3 8B 44 24 04 A3 00
            00391006: 8B 44 24 04 A3 00 B7 40 00 C3 33 C0
              回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-05 00:11 | 溪流
            @OwnWaterloo
            呀,信息量好大,學(xué)習(xí)了~!本想到周末研究一番的~  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-05 09:44 | zuhd
            @OwnWaterloo
            可以這樣理解嗎?
            1,用lib的方式加載exe的導(dǎo)出函數(shù),
            #pragma comment(linker,"/FIXED:NO") ,
            這樣exe就和dll一樣,均不會有問題。
            2,用loadlibrary的方式加載exe的到處函數(shù),
            即使是重定向,在操作全局變量,也不會定向到全局變量的正確地址。
            如:
            而讓 main.c 鏈接到 module.exe 并運(yùn)行的同樣不會發(fā)生重定項
            cl main.c module.lib & main.exe
            0039B700
            00391000: A1 00 B7 40 00 C3 8B 44 24 04 A3 00
            00391006: 8B 44 24 04 A3 00 B7 40 00 C3 33 C0  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-05 14:28 | OwnWaterloo
            @zuhd
            我記得是只與文件類型(PE中的那個域)有關(guān),與加載方式(隱式加載/顯式加載)無關(guān)。

            只要文件類型是exe,加載器就不會去處理重定項。
            如果exe沒有加載到首選基地址,里面的指令操作的就不是預(yù)想中的地址。

            前面說loadlibrary的意思是:這是一種讓dll/exe無法加載到首選基地址的方法。
            如果將dll/exe文件復(fù)制一份(文件各種信息都是相同的),然后用loadlibrary加載這兩者,那兩者之一肯定之多有一個是被加載到首選基地址。于是就可以觀察另一個的情況了。

            但前面為了偷懶。。。 就沒有用這個方法,而是用/base:0x400000 —— 這個是exe文件默認(rèn)的基地址 —— 讓dll無法加載到首選基地址。
              回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-06 09:24 | zuhd
            多謝OwnWaterloo
            如果可以的話 希望能在cppblog單開一主題,探其究竟。  回復(fù)  更多評論
              
            # re: EXE導(dǎo)出函數(shù) 2012-12-06 22:41 | OwnWaterloo
            @zuhd
            cppblog的排版功能太弱了。。。 寫東西很費(fèi)勁。。。  回復(fù)  更多評論
              
            一本久久a久久精品亚洲| 91久久精品国产91性色也| 国产精品久久久久久福利69堂| 久久综合成人网| 色综合久久天天综合| 亚洲精品乱码久久久久久按摩 | 亚洲国产高清精品线久久| 成人国内精品久久久久影院| 亚洲午夜精品久久久久久人妖| 久久久久久久久久久免费精品| 97久久精品人人做人人爽| 久久久久久国产精品无码下载 | 伊人久久综合精品无码AV专区| 久久综合给合久久狠狠狠97色69| 狠狠色丁香久久婷婷综合蜜芽五月| 久久夜色精品国产亚洲av| 波多野结衣久久一区二区| 国产精品久久久久久久久| 一本一道久久a久久精品综合 | 国产精品热久久毛片| 九九久久精品无码专区| 精品人妻伦一二三区久久| 亚洲中文字幕无码久久2017| 草草久久久无码国产专区| 久久久国产视频| 无码国产69精品久久久久网站| 国内精品久久久久久久coent| 亚洲国产欧美国产综合久久| 精品水蜜桃久久久久久久| 久久久久免费看成人影片| 韩国无遮挡三级久久| 国内精品久久久久久久久电影网 | 国产人久久人人人人爽| 国产精品久久一区二区三区| 欧美亚洲国产精品久久久久| 一级做a爰片久久毛片人呢| 亚洲AV无码1区2区久久 | 一本久道久久综合狠狠躁AV| AA级片免费看视频久久| 国产一区二区三区久久精品| 久久久噜噜噜久久中文福利|