关于编译EXE的问题。分数不多,不过有答案立刻结贴。 ( 积分: 50 )

  • 主题发起人 主题发起人 haiip2000
  • 开始时间 开始时间
H

haiip2000

Unregistered / Unconfirmed
GUEST, unregistred user!
[:D]
我用DELPHI7做的,用到一些delphi自带的dll文件还有一些第三方的控件的DLL。
想知道如何才能通过delphi,统统编译到EXE中。
使编译出来的EXE文件在没有装delphi的机器上也可以独立运行。
 
在线等哈
 
555555555
这个问题应该不难吧......都来help一下啦
 
delphi自带的dll??meiyouba
link里边有个选项,rlt是否编译进去!!!
 
如果是商用软件,可以找我。把DLL都打包进去。
 
TO:menzhe
怎么没有? 就像dbexpora.dll、dbexpdb2.dll、dbexpmysql.dll之类的
TO:白河愁
打包我会,我是要编译。
 
看可不可以做成RC文件,然后释放出来,但是问题是做成了RC文件但是软件也运行不了也无法释放呀,不知道如何搞,期待学习中。。。ing.
 
我说的打包不是你会的那种。是把DLL内嵌到 EXE 文件里,使用时不需要释放到文件。
调用代码维持不变,无论是 external 还是 loadlibary。
简单的说就是你光给我EXE和 DLL,我弄成一个文件。
 
有这样的工具?把DLL也编译进去?哈哈 太有兴趣了....
 
不知道FileMapping能不能帮上忙
 
GOOGLE找一个DLLUnit.pas的单元
然后把dll编译进资源文件,工程里加上{$R 资源文件名},把资源加入工程
然后用dllunit里的代码,从资源加载DLL
 
把EXE,DLL都放到一个文件中.然后程序一开始进行释放工作.
两种方式释放:
1.释放到一个文件中,再调用.
2.直接释放到内存中,然后映射到进程空间去运行.
 
这样的exe不是大了吗?第三方都是单独注册好啊。。呵呵跑题了。
 
难道老白要自己把dll写到exe文件末尾?
再给程序做个壳,运行时先运行壳,把程序和dll加载到内存后执行?
 
也许会有其他的方式.EXE运行后,对DLL的调用是通过输入表.输入表内的地址项在PE头加载后替换成DLL内存中输入函数的相应地址,那么如果根据输入表中的地址,在整个EXE中查找对应的跳转指令地址,替换成捆绑在一起的DLL中输出函数的相对地址,那么也许可以.哈,猜想的,木试过.
 
呵,现成的工具,两种方式,内存和磁盘释放
http://www.bitsum.com/pebundle.asp
 
再贴一个资料,基本上,这个问题已经解决了.

Win32 EXE 在调用一个外部 DLL 中的函数时,首先要调用 LoadLibary 函数来载入此 DLL 到程序的进程地址空间。 如果 LoadLibary 载入此 DLL 成功,将返回一个该 DLL 的句柄。 这个句柄实际上就是该 DLL 在内存中的起始地址。 在载入 DLL 成功后,还必须调用 GetProcAddress 函数来获取要调用的函数的地址。然后再根据该地址来调用这个函数。
根据上述原理,我们可以把一个 DLL 作为资源文件放到 EXE 文件中,在程序运行时,分配一块内存,然后将此资源复制到该分配的内存中,并根据该内存地址计算得到相关的导出函数地址,然后,当我们需要调用某一函数时,可以用该函数在内存中的地址来调用它。
程序实现。
首先,把要合并的 DLL 作为资源加入到项目的资源文件中,然后在程序运行时,从资源中载入该资源,以得到该 DLL 在内存中的位置:

LPVOID sRawDll; // 资源文件在内存中的地址 HRSRC hRes; HMODULE hLibrary; HGLOBAL hResourceLoaded; char lib_name[MAX_PATH]; GetModuleFileName(hInstance, lib_name, MAX_PATH ); // 得到运行程序的名字 hLibrary = LoadLibrary(lib_name); // 就象载入一个 DLL 一样载入运行程序到内存中 if (NULL != hLibrary) { // 得到指定的资源文件在内存中的位置 hRes = FindResource(hLibrary, MAKEINTRESOURCE(IDR_DATA1), RT_RCDATA); if (NULL != hRes) { // 将资源文件载入内存 hResourceLoaded = LoadResource(hLibrary, hRes); if (NULL != hResourceLoaded) { // 得到资源文件大小 SizeofResource(hLibrary, hRes); // 锁定资源以得到它在内存中的地址 sRawDll = (LPVOID)LockResource(hResourceLoaded); } } else return 1; FreeLibrary(hLibrary);}else return 1;
然后,从资源中载入 DLL 到内存函数 LoadPbDllFromMemory 将载入 DLL 到内存中, 该函数有两个参数,第一个参数是指向 DLL 资源在内存中的地址的指针,也就是前面代码中的 LockResource 函数的返回值。第二个参数是一个空的指针,如果函数 LoadPbDllFromMemory 运行成功,该指针将指向重新组合后的内存中的 DLL 的起始地址。该函数还有一个功能就是如果运行成功,它将手动地用 DLL_PROCESS_ATTACH 参数调用 DLL 的入口函数 DllMain 来初始化该 DLL。除此之外,它还会手动地载入合并的 DLL 的入口表中导入的 DLL 并调整它们在内存中的相对地址。以下是该函数代码:
DWORD LoadPbDllFromMemory(LPVOID lpRawDll, LPVOID lpImageDll) { SYSTEM_INFO sSysInfo; PIMAGE_DOS_HEADER dosHeader; PIMAGE_NT_HEADERS pNTHeader; PIMAGE_SECTION_HEADER section; PIMAGE_IMPORT_DESCRIPTOR pImportDesc; PIMAGE_IMPORT_BY_NAME pOrdinalName; PIMAGE_BASE_RELOCATION baseReloc; PDWORD lpLink; unsigned char Protection[4096]; HINSTANCE hDll; WORD i; DWORD ImagePages,fOldProtect,j,MaxLen,HdrLen,Addr1,Addr2,Pg,Pg1,Pg2; char * sDllName; if(NULL == lpRawDll) return 1 ; dosHeader = (PIMAGE_DOS_HEADER)lpRawDll; // Is this the MZ header? if ((TRUE == IsBadReadPtr(dosHeader,sizeof (IMAGE_DOS_HEADER))) || (IMAGE_DOS_SIGNATURE != dosHeader->e_magic)) return 2; // Get the PE header. pNTHeader = MakePtr(PIMAGE_NT_HEADERS,dosHeader,dosHeader->e_lfanew); // Is this a real PE image? if((TRUE == IsBadReadPtr(pNTHeader,sizeof ( IMAGE_NT_HEADERS))) || ( IMAGE_NT_SIGNATURE != pNTHeader->Signature)) return 3 ; if(( pNTHeader->FileHeader.SizeOfOptionalHeader != sizeof(pNTHeader->OptionalHeader)) || (pNTHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)) return 4; if (pNTHeader->FileHeader.NumberOfSections < 1) return 5; section = IMAGE_FIRST_SECTION( pNTHeader ); int HeaderSize = sizeof(IMAGE_SECTION_HEADER); // 节头长度 HdrLen = (DWORD)section - (DWORD)dosHeader + HeaderSize * pNTHeader->FileHeader.NumberOfSections; // 找出最大的节的长度,此节一般是代码所在的节(.text 节) MaxLen = HdrLen; int ii=0; for (i = 0;i<(DWORD)pNTHeader->FileHeader.NumberOfSections;i++)// find MaxLen { if(MaxLen < section.VirtualAddress + section.SizeOfRawData) { MaxLen = section.VirtualAddress + section.SizeOfRawData; } if(strcmp((const char *)section.Name,".rsrc") == 0) ii=i; } GetSystemInfo(&sSysInfo); ImagePages = MaxLen / sSysInfo.dwPageSize; if (MaxLen % sSysInfo.dwPageSize) ImagePages++; // 分配所需的内存 DWORD NeededMemory = ImagePages * sSysInfo.dwPageSize; lpImageDll = VirtualAlloc(NULL, NeededMemory, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (lpImageDll == NULL) return 6; // 分配内存失败 MoveMemory( lpImageDll, lpRawDll, HdrLen ); // 复制节头 DWORD OrgAddr = 0; DWORD NewAddr = 0; DWORD Size = 0; // 复制 .text 节数据 for (i = 0;i<pNTHeader->FileHeader.NumberOfSections;i++) { OrgAddr = (DWORD)lpImageDll + (DWORD)section.VirtualAddress; NewAddr = (DWORD)lpRawDll + (DWORD)section.PointerToRawData; Size = (DWORD)section.SizeOfRawData; MoveMemory((void *)OrgAddr, (void *)NewAddr, Size ); } // 把指针指向新的 DLL 映像 dosHeader = (PIMAGE_DOS_HEADER) lpImageDll; // Switch to new image pNTHeader = (PIMAGE_NT_HEADERS) ((DWORD)dosHeader + dosHeader->e_lfanew); section = (PIMAGE_SECTION_HEADER) ((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS)); pImageBase = (PBYTE)dosHeader; if((ii!=0) && (IsNT()==TRUE)) { section[ii].VirtualAddress = section[ii].VirtualAddress + (DWORD)lpRawDll; section[ii].PointerToRawData = section[ii].PointerToRawData + (DWORD)lpRawDll; } DWORD importsStartRVA; // Look up where the imports section is (normally in the .idata section) // but not necessarily so. Therefore, grab the RVA from the data dir. importsStartRVA = GetImgDirEntryRVA(pNTHeader,IMAGE_DIRECTORY_ENTRY_IMPORT); if ( !importsStartRVA ) { VirtualFree(dosHeader,0, MEM_RELEASE); return 7; } pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) pNTHeader-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; if(pImportDesc!= 0) pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ((DWORD)pImportDesc + (DWORD)dosHeader); else { VirtualFree(dosHeader,0, MEM_RELEASE); return 8; } while (1) // 处理各入口表中的 DLL { // 检查是否遇到了空的 IMAGE_IMPORT_DESCRIPTOR if ((pImportDesc->TimeDateStamp==0 ) && (pImportDesc->Name==0)) break; // 从磁盘载入必须的 Dll, // 注意,载入的 DLL 是合并的 DLL 的入口表中的 DLL, // 不是合并到 EXE 中的 DLL sDllName = (char *) (pImportDesc->Name + (DWORD)pImageBase); hDll = GetModuleHandle(sDllName); if (hDll == 0 ) hDll = LoadLibrary(sDllName); if (hDll == 0 ) { MessageBox(NULL, "Can''t find required Dll", "Error in LoadPbDllFromMemory()",0); VirtualFree(dosHeader,0, MEM_RELEASE); return 9; } DWORD *lpFuncNameRef = (DWORD *) (pImportDesc->OriginalFirstThunk + (DWORD)dosHeader); DWORD *lpFuncAddr = (DWORD *) (pImportDesc->FirstThunk + (DWORD)dosHeader); while( *lpFuncNameRef != 0) { pOrdinalName = (PIMAGE_IMPORT_BY_NAME) (*lpFuncNameRef + (DWORD)dosHeader); DWORD pIMAGE_ORDINAL_FLAG = 0x80000000; if (*lpFuncNameRef & pIMAGE_ORDINAL_FLAG) *lpFuncAddr = (DWORD) GetProcAddress(hDll, (const char *)(*lpFuncNameRef & 0xFFFF)); else *lpFuncAddr = (DWORD) GetProcAddress(hDll, (const char *)pOrdinalName->Name); if (lpFuncAddr == 0) { VirtualFree(dosHeader,0, MEM_RELEASE); return 10;// Can''t GetProcAddress } lpFuncAddr++; lpFuncNameRef++; } pImportDesC++; } DWORD TpOffset; baseReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pNTHeader-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); if (baseReloc !=0) { baseReloc = (PIMAGE_BASE_RELOCATION) ((DWORD)baseReloc + (DWORD)dosHeader); while(baseReloc->VirtualAddress != 0) { PWORD lpTypeOffset = (PWORD) ((DWORD)baseReloc + sizeof(IMAGE_BASE_RELOCATION)); while (lpTypeOffset < (PWORD)((DWORD)baseReloc + (DWORD)baseReloc->SizeOfBlock)) { TpOffset = *lpTypeOffset & 0xF000; if(TpOffset == 0x3000) { lpLink = (PDWORD) ((DWORD)dosHeader + baseReloc->VirtualAddress + (*lpTypeOffset & 0xFFF)); *lpLink = (DWORD)dosHeader + (*lpLink) - pNTHeader->OptionalHeader.ImageBase; } else { if (TpOffset != 0) { VirtualFree(dosHeader,0, MEM_RELEASE); return 10; } } lpTypeOffset++; } baseReloc = (PIMAGE_BASE_RELOCATION)((DWORD)baseReloc + (DWORD)baseReloc->SizeOfBlock); } } // 取得原始的内存状态 memset(Protection,0,4096); for (i = 0;i<=pNTHeader->FileHeader.NumberOfSections;i++) { if (i == pNTHeader->FileHeader.NumberOfSections) { Addr1 = 0; Addr2 = HdrLen; j = 0x60000000; } else { Addr1 = section.VirtualAddress; Addr2 = section.SizeOfRawData; j = section.Characteristics; } Addr2 += Addr1 - 1; Pg1 = Addr1 / sSysInfo.dwPageSize; Pg2 = Addr2 / sSysInfo.dwPageSize; for(Pg = Pg1 ;Pg<=Pg2;Pg++) { if (j & 0x20000000) Protection[Pg] |= 1; // Execute if (j & 0x40000000) Protection[Pg] |= 2; // Read if (j & 0x80000000) Protection[Pg] |= 4; // Write } } // 恢复原始的内存状态 Addr1 = (DWORD)dosHeader; for (Pg = 0 ;Pg<= ImagePages;Pg++) { switch(Protection[Pg]) { case 2: fOldProtect = PAGE_READONLY; break; case 3: fOldProtect = PAGE_EXECUTE_READ; break; case 6: fOldProtect = PAGE_READWRITE; break; default: // Ignore strange combinations fOldProtect = PAGE_EXECUTE_READWRITE; break; } if (fOldProtect !=PAGE_EXECUTE_READWRITE) { if (VirtualProtect((void *)Addr1, sSysInfo.dwPageSize, fOldProtect, &fOldProtect) == 0) { VirtualFree(dosHeader,0, MEM_RELEASE); return 11; } } Addr1 += sSysInfo.dwPageSize; } EntryPoint = (LPENTRYPOINT) ((DWORD)pNTHeader->OptionalHeader.AddressOfEntryPoint + (DWORD)dosHeader); LPVOID lpReserved = 0; EntryPoint((HINSTANCE)dosHeader, DLL_PROCESS_ATTACH, lpReserved); lpImageDll2=lpImageDll; return 0; }
一但 DLL 被正确地载入到内存中,我们就可以通过自定义函数 GetProcAddressDirectly 来获取某函数在内存中的地址,并根据该地址来调用该函数,该函数也有两个参数,第一个参数是指向载入到内存中的 DLL 的起始地址的指针,第二个是要调用的函数的函数名。以下是 GetProcAddressDirectly 函数代码:
DWORD GetProcAddressDirectly(PIMAGE_DOS_HEADER dosHeader, char * FuncName) { PIMAGE_NT_HEADERS pNTHeader; PIMAGE_EXPORT_DIRECTORY pExportDir; PWORD lpNameOrdinals; LPDWORD lpFunctions; DWORD * lpName; char * lpExpFuncName; DWORD i; DWORD j; char * lpFuncName; if(dosHeader->e_magic != IMAGE_DOS_SIGNATURE) return 0; pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)dosHeader + dosHeader->e_lfanew); if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) return 0; if ((pNTHeader->FileHeader.SizeOfOptionalHeader != sizeof(pNTHeader->OptionalHeader)) || (pNTHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)) return 0; DWORD exportsStartRVA, exportsEndRVA; pImageBase = (PBYTE)dosHeader; // Make pointers to 32 and 64 bit versions of the header. pNTHeader = MakePtr( PIMAGE_NT_HEADERS, dosHeader,dosHeader->e_lfanew ); exportsStartRVA = GetImgDirEntryRVA(pNTHeader,IMAGE_DIRECTORY_ENTRY_EXPORT); exportsEndRVA = exportsStartRVA + GetImgDirEntrySize(pNTHeader, IMAGE_DIRECTORY_ENTRY_EXPORT); // Get the IMAGE_SECTION_HEADER that contains the exports. This is // usually the .edata section, but doesn''t have to be. PIMAGE_SECTION_HEADER header; header = GetEnclosingSectionHeader( exportsStartRVA, pNTHeader ); if ( !header ) return 0; INT delta; delta = (INT)(header->VirtualAddress - header->PointerToRawData); pExportDir = (PIMAGE_EXPORT_DIRECTORY)GetPtrFromRVA(exportsStartRVA, pNTHeader, pImageBase); pExportDir =(PIMAGE_EXPORT_DIRECTORY) (pNTHeader-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if (pExportDir == 0) { MessageBox(NULL,"Error in GetProcAddressDirectly()",0,0); return 0; } pExportDir =(PIMAGE_EXPORT_DIRECTORY) ((DWORD)pExportDir + (DWORD)dosHeader); lpNameOrdinals =(PWORD)((DWORD)pExportDir->AddressOfNameOrdinals + (DWORD)dosHeader); lpName =(LPDWORD) (pExportDir->AddressOfNames + (DWORD)dosHeader); lpFunctions =(LPDWORD) (pExportDir->AddressOfFunctions + (DWORD)dosHeader); lpFuncName = FuncName; if(HIWORD(lpFuncName)!=0 ) { for( i = 0;i<=pExportDir->NumberOfFunctions - 1;i++) { DWORD entryPointRVA = *lpFunctions; // Skip over gaps in exported function if ( entryPointRVA == 0 ) continue; for( j = 0;j<=pExportDir->NumberOfNames-1;j++) { if( lpNameOrdinals[j] == i) { lpExpFuncName = (char *) (lpName[j] + (DWORD)dosHeader); if(strcmp((char *)lpExpFuncName,(char *)FuncName)==0) return (DWORD) (lpFunctions + (DWORD)dosHeader); } } } } else { for (i = 0 ;i<=pExportDir->NumberOfFunctions - 1;i++) { if (lpFuncName == (char *)(pExportDir->Base + i)) { if (lpFunctions) return (unsigned long) (lpFunctions + dosHeader); } } } return 0; }
在调用完函数后,不要忘记用 UnloadPbDllFromMemory 来从内存中移去 DLL 以释放分配的内存,该函数还会用 DLL_PROCESS_DETACH 参数调用 DLL 的入口函数 DllMain 来从调用进程的地址空间卸载该 DLL。 以下是 UnloadPbDllFromMemory 函数代码:
DWORD UnloadPbDllFromMemory(PIMAGE_DOS_HEADER dosHeader) { PIMAGE_NT_HEADERS pNTHeader; pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)dosHeader + (DWORD)dosHeader->e_lfanew); EntryPoint = (LPENTRYPOINT)(pNTHeader->OptionalHeader.AddressOfEntryPoint + (DWORD)dosHeader); EntryPoint((HINSTANCE)dosHeader, DLL_PROCESS_DETACH, 0); return VirtualFree(dosHeader, 0, MEM_RELEASE); }
 
这段到处都有的代码是跑不起来的。
 
这个代码不重要,具体思路知道就行了.这些不是什么实现难度问题,是资料是否充足,思路是否清晰的问题.
 
这个代码的思路没错,不过你写写看看?
资料思路都不重要,关键是要有足够的coding能力。
 
后退
顶部