Delphi之萝莉调教篇
本文纯属技术交流.如果各位看官想与小生一起探讨萝莉的问题的话...PM我吧
关于Delphi的萝莉调教技术,很久以前就有大牛做过了...其实技术早掌握了
只是觉得太无聊~估计大家也都会于是就没有写~既然群里有人提出~就留下一份记录
以前我很傻很天真.主要原因是也因为很懒.
正值新春之际全当写出来给各位献礼了.给大家拜个晚年
由于本文作者水平问题,有说的不对或者不明确的地方请大家海涵.菜鸟之作高手跳过...
Q:为啥不用Delphi?
A:体积太大
Q:为啥用Delphi?
A:很方便
体积问题一直都是Delphi Programer头痛的问题...
我也幻想过有一天用Delphi像VC一样的写出迷你小程序...
直到某一天我发现许多萝莉控都在调教萝莉...于是我突发奇想我也来调教一个萝莉吧
目标Delphi~御姐变萝莉...
关于这篇文章打算分为两个部分描述...
1.萝莉自身的调教(关于核心库的修改.导入表的迷你化)
2.外界的调教力量(Delphi编译...MASM link)
开始说说如何调教Delphi(萝莉)吧...
0.VCL的力量
1.KOL的力量
2.自修改核心库
3.导入表的优化
4.进一步优化
[0].VCL的力量...
关于VCL不多说了...其实在迷你化程序中
VCL基本上都不会使用.
这里我说的VCL不是说窗体VCL...例如SysUtils,Classes等单元也都是属于VCL部分
system,sysinit也是属于吧?我也不太清楚,这两个单元是Delphi默认加载的...
想取消不编译到工程中...不在本章讨论范围中
给各位的建议是...除了system,sysinit以外的Delphi自带VCL单元都不要使用...
system中其实已经有许多函数了...
由于这两个单元是默认的所以想不用也没办法...
1
2
3
4
5
6
7
8
|
program Project1; uses Windows; begin MessageBox(0, 'Hello World!' , 'By Anskya' , 0); end. |
写下以下代码,编译后...15k For Delphi7(Delphi 6 这份代码编译出是8k)
为什么这么大?和VC一样的原因,系统默认库,编译器底层干了许多不为人所知的事情
看看导入表...
看到没有?除了user32和kernel32,还有advapi32??还是操作注册表函数.
我们并没有操作注册表呀?
IDA逆向分析一下发现是读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
unknown_libname_13 proc near cbData= dword ptr -0Ch Data= byte ptr -8 hKey= dword ptr -4 push ebp mov ebp, esp add esp, 0FFFFFFF4h movzx eax, ds:word_40400C mov dword ptr [ebp+Data], eax lea eax, [ebp+hKey] push eax ; phkResult push 1 ; samDesired push 0 ; ulOptions push offset SubKey ; "SOFTWARE\\Borland\\Delphi\\RTL" push 80000002h ; hKey call RegOpenKeyExA test eax, eax jnz short loc_402A84 . . . |
好了这个就是大家平时所编译的Delphi最小化程序...
[1]KOL的力量
KOL是俄罗斯的一群Delphi fans有感于VCL的庞大而创造的一个framework工程
内部与VCL一样包含了大量的窗体等等操作封装的控件...不熟悉的朋友
可以去官方主页上看一下KOL+MCK,官方主页:http://kolmck.net/
我们这里主要使用到的是KOL的sysdcu库.这个是一个优化的核心库.Delphi7优化的很好
还是上面的代码我们进行优化设置...使用指定的核心库
菜单选择->Project->Options->Directories/Conditionals-Search path
在这个选项中选择核心库的位置(其实一般也是用于设置..控件的源代码或者dcu路径)
设置好编译后 5.5k
查看导入表发现,居然只有kernel32和user32这两个库...
体积居然缩减了这么多
其实仔细对比一下system.pas就可以发现其中的奥秘
许多不必要的函数操作单元函数和过程被取消掉了...
下场非常悲惨(比如几个函数都没办法用了...Write,Writeln函数等等Readln函数等)
不过对于写Windows程序来说不大.KOL库中有专门的console函数
是不是想说什么?配合VCL试试?我建议大家放弃这个想法,因为VCL本身需要这些不知名和知名
的一些函数和声明,不信你可以试试编译一下~Delphi就会大量提示你编译错误,许多
VCL本身需要的东西都被优化掉了...
好了继续我们的优化之旅
[2]自修改核心库
看完上面的两个试验后是不是有感,user32是我们需要的那个函数,但是..kernel32中还是有
大量的我们不需要的函数...
是的,kol虽然进行的优化,但是他必须保证一些基本的操作函数存在
例如string...string这个类型以后的其他文章我会详细介绍的...
string的暗藏操作也是许多的.和MFC的CString其实差不多~类似的性质而已
只是string做的很好让许多人误认为那个就是一个字符串类型...
关于取舍问题在这里我就不多废话了...说说优化吧...
对于我们来说string也是不需要的因为我们有了PChar(别说不知道是什么其实就是char *)
由于我们要最终打造一个只有一个迷你化的库,迷你到什么程度?
需要什么函数他就给我们保留什么函数其他的什么都不需要.好的为了这个目的我们继续
相信有些人看过潘爱民老师的Delphi源码分析这本书,说实话我没有看过,
只是在CSDN上看了一下目录,发现其中的许多东西都是大家应该知道的常识
自己穷也没钱买,Google可以搜一下电子版(电子版我也没有.太懒~有时间我宁可看漫画)
文中提到Nico大牛的MiniDExe这个自己优化的迷你EXE演示(见附件)
MiniDExe.zip
我们来看一下基本的代码.其实system,sysinit中的代码已经被删除的差不多了
只保留了最基本的...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
unit SysInit; interface var TlsIndex: Integer; TlsLast : Byte; PtrToNil: Pointer; var HInstance: Pointer; GetCommandLine: PAnsiChar; procedure _InitExe; implementation procedure _InitExe; assembler; asm mov eax, [ebp+$08] mov [HInstance], eax mov eax, [ebp+$10] mov [GetCommandLine], eax end; end. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
unit System; interface type TGUID = record D1: LongWord; D2: Word; D3: Word; D4: array[0..7] of Byte; end; TDLLProc = procedure(Reason: Integer); TDLLProcEx = procedure(Reason: Integer; Reserved: Integer); procedure _Halt0; procedure _HandleFinally; var ExitCode: Integer; implementation procedure _Halt0; assembler; asm mov eax, [ExitCode] leave end; procedure _HandleFinally; assembler; asm mov eax, True end; end. |
这些都是最基本的核心库了OD跟踪一下发现
1
2
3
4
5
6
7
8
9
10
11
|
00401124 > $ 55 push ebp 00401125 . 8BEC mov ebp, esp 00401127 . 83C4 F0 add esp, -10 0040112A . B8 FC104000 mov eax, 004010FC 0040112F . E8 14FFFFFF call 00401048 00401134 . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL 00401136 . 68 4C114000 push 0040114C ; |Title = "By Anskya" 0040113B . 68 58114000 push 00401158 ; |Text = "Hello World!" 00401140 . 6A 00 push 0 ; |hOwner = NULL 00401142 . E8 4DFFFFFF call <jmp.&user32.MessageBoxA> ; \MessageBoxA 00401147 . E8 B4FEFFFF call 00401000 |
仅仅剩下这些东西.
导入表下也剩下了user32.MessageBoxA
由于不能使用Windows.pas单元中的函数了所以不得不使用自己编写的
引用单元了.
现在体积:3.5k
[3]导入表的优化
不知道大家注意过没有...Delphi生成的PE很奇怪.为什么这么说呢
1.不知名的资源
2.导入表的胡乱创建
3.不分场合的重定位表
我们这里说说导入表的奇怪现象
见代码:Project4
[图4]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
program Project4; uses Windows, Unit1, Unit2; var hSelfModule: HMODULE; szBuffer: Array[0..MAX_PATH] Of Char; begin hSelfModule := GetModuleHandle(nil); ZeroMemory(@szBuffer, SizeOf(szBuffer)); szBuffer[GetModuleFileName(hSelfModule, szBuffer, SizeOf(szBuffer))] := #0; BoxFun1(); BoxFun2(); MessageBox(0, szBuffer, 'By Anskya' , 0); end. |
单元1,单元2都是一样的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
unit Unit1; interface uses Windows; procedure BoxFun1(); implementation function MessageBoxA(hWnd: HWND; lpText, lpCaption: PAnsiChar; uType: UINT): Integer; stdcall; external user32 name 'MessageBoxA' ; procedure BoxFun1(); begin MessageBoxA(0, 'Hello Wolrd!' , 'Fun1' , 0); end; end. |
但是导入表中确出现了许多特别的东西.
比如说代码中调用了GetModuleHandleA函数一次,但是导入表中确出现了三次...
user32.MessageBoxA竟然出现了四次...
由于代码的疏忽也会产生大量的~重复导入表函数
为此只能建议大家尽量的把声明和引用代码都尽量写在一个单元中...
大家可以使用PEID或者Stud_PE自行查看演示程序的导入表
[4]进一步优化
上面说了Delphi会产生一些非常无聊的垃圾
下面说说最大的两个部分.
1.资源
2.重定位表
不管我们是否使用了资源,Delphi最后生成的资源目录中始终会产生资源目录
关于这个部分的优化我只能建议大家使用ResHacker或者Restorator进行删除
其实这个资源也包含了大家的一些代码信息
RCData->PACKAGEINFO
资源中就包含了程序主dpr中包含的代码单元...
DEDE也是根据这个文件查看你的Delphi程序一些信息的
删除掉这些没用的资源可以减少0.5k体积
现在我们的程序只有3k体积了
清除重定位表.Delphi给我们添加的.一般来说没什么用清除掉也就清除掉吧
清除工具很多,大家自己找找Stud_PE自己手工删除也可以
手工删除资源和重定位表后...程序体积只有2.5k了
和VC指定入口点编译也没啥区别...
Mew11 1.2SE压缩后 861字节...不知道大家满意否?不满意?
剩下占用大量体积的东西我们看看也就知道了...大量无用的数据段
在VC中可以通过设置编译参数来进行段合并.Delphi没这麽强的参数
而且编译的时候会产生大量的垃圾...那怎么办呢?
那就只有祭出绝招...武力调教萝莉了...
看下篇
https://bbs.pediy.com/thread-59585.htm
上篇只是简单的说了一下Delphi迷你化程序的初级优化
这篇主要是说如何进行外部改变Delphi架构...
现在Delphi编译器本身已经优化的暂时到顶了.我们来看看
下面影响我们继续迷你化的是什么问题...
1.数据段和代码段的融合,还有没用的bss等等段
完全可以融合到一起
2.文件对齐,还有DOS Stub
在VC下我们可以很容易解决这个问题.谁让Borland的编译器技术很好,连接器技术确
。。。算了这个不是在我们考虑范围以内
我们来解决以上两个问题。
如果要解决这两个问题就要追忆。。。Delphi的发展史了。。。
(BTW:看来这丫~是彻底秀逗了...)
Delphi的编译器是Anders一手调教出来的...(我也不知道说什么好)
据说在Delphi老版本的时候是可以生成COFF格式的,关于这个问题
有一个彻底的项目...Ms-Rem大牛以前彻底研究过这个东西
还扔出一个演示品...由于当时的疏忽没有发现他使用的是Delphi3进行编译的
[见附件1]
由于Delphi3的特殊性造就了这个很奇怪的东西...兼容COFF的OMF???不明白
masm的link(polink)可以对其进行实施link操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
unit HelloWorld; interface Procedure Start; implementation function MessageBoxA(hWnd: cardinal; lpText, lpCaption: PChar; uType: Cardinal): Integer; stdcall; external 'user32.dll' name '_MessageBoxA@16' ; Procedure Start; begin MessageBoxA(0, 'Hello world!' , nil, 0); end; end. |
由于Delphi使用的是OMF结构...Delphi本身默认是产生dcu文件的
obj文件需要重新设置一下...看代码即可知道...
MASM中的默认库函数到处头部都是采用_MessageBoxA@16这样的结构
@16这个是传递参数的大小---SizeOf(DWORD) * 4
Win32下指针其实也是DWORD~不是吗?呵呵
根据这个obj进行一下连接操作
1
2
3
|
dcc32.exe -jP -$A-,B-,C-,D-,G-,H-,I-,J-,L-,M-,O+,P-,Q-,R-,T-,U-,V-,W+,X+,Y- HelloWorld.pas link.exe /ALIGN :32 /FORCE :UNRESOLVED /SUBSYSTEM :WINDOWS /ENTRY :Start$qqrv HelloWorld.obj user32.lib /out :Hello.exe |
/ALIGN:32 (这里最好修改为0x200)
/FORCE:UNRESOLVED(强制输出)
请使用delphi3进行编译...这里提供一个开发包...
[见附件2]
包括代码演示...请大家自行研究...
关于入口点/ENTRY:Start$qqrv
为什么采用pas而不是dpr,主要原因是因为..dpr的入口点是锁死的
就是@InitEXE...由于是不导出的所以没办法使用...
所以采用pas.那不是每次写代码都非常郁闷,还有Delphi3的优化
为了解决这两个问题,所以重新改变一下
masm 7.0的link具有重新创建obj的功能
这里使用这个方法...
Delphi 7依然可以创建。做一个通用工程来玩玩
总是用批处理不好讲究点实际开发。。来新建一个工程吧
[见附件2]
1
2
3
4
5
6
7
8
|
program Project1; uses Unit_Main; begin _entry(); end. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
unit Unit_Main; interface function MessageBoxA(hWnd: cardinal; lpText, lpCaption: PChar; uType: Cardinal): Integer; stdcall; external 'user32.dll' name '_MessageBoxA@16' ; procedure _entry(); implementation procedure _entry(); begin MessageBoxA(0, 'Hello World!' , 'By Anskya' , 0); end; end. |
剩下的完全API就可以了(小心使用ZeroMemory,FillMemory,CopyMemory这三个函数,
Delphi为了编码效率把这两个函数给内嵌了...切忌切忌实在需要咱自己写,
要不然就自己GetProcAddress...).自己写函数声明吧...
_entry();这个名称是因为连接器把你输入入口函数名称加上_
不知道为啥这个习惯...
工程设置需要重新设置一下(Delphi默认是不会生成obj的)
Project Options->link
选择生成C++ object Files,Export All symbols
就可以生成我们需要的obj了...
为了减去不必要的转换麻烦,这里使用mickeylan 大侠为我们准备的rmcoff
为了减少大家的麻烦.发现这个简单的东西就没必要自己去写批处理了.吼吼
批处理代码如下(基本上是死代码..写好一次以后的随机修改就好了)
1
2
3
4
|
..\rmcoff\rmcoff.exe Unit_Main.obj Unit_Main.obj ..\small\link.exe /ALIGN :0x200 /SUBSYSTEM :windows /ENTRY :entry Unit_Main.obj ..\lib\user32.lib /MERGE :.data=.text /MERGE :.rdata=.text /MERGE :_EXIT_=.text /MERGE :_INIT_=.text /SECTION :.text,RWEX /ignore :4033,4078,4108 /out :Hello.exe pause |
link编译说明:
这里的user32.lib链接库不是masm32\lib下的
这个链接库是重新创建的:
LINK -LIB -MACHINE:IX86 -DEF:KERNEL32.DEF
具体可以参考以前我写的文章<<浅谈连接库函数的声明与创建>>
http://bbs.pediy.com/showthread.php?t=25555
这里编译是完全没问题了(看...附件中的样式代码)
Delphi 7 编译 MASM Link
如果你觉得体积还是不够小请调整:/ALIGN:0x200,可以修改为4
那编译出来的效果就是传说中的680字节了~不过为了兼容Win9x,最好不要修改
好了至此~小萝莉已经被我们调教的差不多了...下面你还想干什么???
//====================
哦我知道了~你们是说这样写代码太痛苦了是吧...的确每个函数都要声明...
后才可以使用...再次教大家一个秘籍吧...
Borland Format For COFF...吼吼~
意思就是说创建一个Borland格式的到处头部的COFF格式LIB
听起来有点怪异哦~就是取消掉_,@16等特征的纯正COFF LIB...
使用大牛Vortex的工具...来创建我们需要的LIB格式
def2lib user32.def -nod
user32.def
1
2
3
|
LIBRARY user32 EXPORTS "MessageBoxA" |
你可以自己写一个遍历导出表的程序来自动生成这个文件...
修改代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
unit Unit_Main; interface uses Windows; procedure _entry(); implementation procedure _entry(); begin MessageBox(0, 'Hello World!' , 'By Anskya' , 0); ExitProcess(0); end; end. |
批处理代码
1
2
3
4
|
..\rmcoff\rmcoff.exe Unit_Main.obj Unit_Main.obj ..\small\link.exe /ALIGN :0x200 /SUBSYSTEM :windows /ENTRY :entry Unit_Main.obj ..\def2lib\kernel32.lib ..\def2lib\user32.lib /MERGE :.data=.text /MERGE :.rdata=.text /MERGE :_EXIT_=.text /MERGE :_INIT_=.text /SECTION :.text,RWEX /ignore :4033,4078,4108 /out :Project2.exe pause |
这样就可以直接引用Windows.pas了~~以后再也不需要自己声明头部了...感谢Vortex
赐予我们这麽好的一个工具...感谢...
如果是多个单元的编译的话
请在批处理中增加你要处理的obj...
此次调教活动得以顺利展开感谢以下大牛们:
Anders,EliCZ,Vortex,Ms-Rem,mickeylan
再次感谢大家顺利看完这篇文章所有工具和代码实例都在附件中请大家自行查看
https://bbs.pediy.com/thread-59585.htm
Delphi之萝莉调教篇的更多相关文章
- delphi数组之菜鸟篇
数组是可以通过索引来引用的同类型数据的列表.按照存储空间的获取方式,Delphi支持的数组类型有两种,即静态数组和动态数组.所谓静态数组就是在声明时就已经确定大小的数组类型,而动态数组是指其大小在声明 ...
- TGL站长关于常见问题的回复
问题地址: http://www.thegrouplet.com/thread-112923-1-1.html 问题: 网站配有太多的模板是否影响网站加载速度 月光答复: wp不需要删除其他的模板,不 ...
- [No0000188][VCB-Studio 科普教程 2.5] 基于 PotPlayer 和 madVR 的播放器教程(已更新 XySubFilter)
Potplayer 是高清影视常用的播放器,界面简洁,功能齐全,比 MPC-HC 和 MPC-BE 更人性化:但其默认方案十分糟糕,预设过多错误,无法正确播放 10-bit 视频,一直饱受诟病.VCB ...
- Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)
本文目标: 了解Delphi的字符串类型 字符编码的检测与转换 简体繁体转换 0. 导言 看完“.Net与字符编码(理论篇)”,我们明白了字符是自然语言中的最小单位,在存储和传输的过程中可以使用三种编 ...
- Delphi 泛型(三十篇)
Delphi 泛型(三十篇)http://www.cnblogs.com/jxgxy/category/216671.html
- Delphi驱动开发研究第一篇--实现原理
Delphi能不能开发Windows的驱动程序(这里的驱动程序当然不是指VxD了^_^)一直是广大Delphi fans关注的问题.姑且先不说能或者不能,我们先来看看用Delphi开发驱动程序需要解决 ...
- delphi基础篇之数据类型概论
delphi基础篇之数据类型概论 Object Pascal 语言提供了非常丰富的数据类型,即简单类型(Simple).字符串类型(String).结构类型(Struct).指针类型(Pointer) ...
- delphi基础篇之项目文件
delphi基础篇之项目文件 program Teacher2018; uses Forms, Unit1 in 'Unit1.pas' {Form1}, Unit2 in 'Unit2.pa ...
- 远程控制篇:在DELPHI程序中拨号上网
用MODEM拨号上网,仍是大多数个人网民选择上网的方式.如果能在我们的应用程序中启动拨号连接(如IE浏览器程序中的自动拨号功能),无疑将会方便我们的软件用户(不用再切换应用程序,运行拨号网络),提高我 ...
随机推荐
- app审核相关
app加急审核通道:https://developer.apple.com/contact/app-store/?topic=expedite
- cf493E Vasya and Polynomial
Vasya is studying in the last class of school and soon he will take exams. He decided to study polyn ...
- SQL Server中的@@ROWCOUNT
SQL Server中@@ROWCOUNT返回受上一语句影响的行数,返回值类型为 int 整型. 如果行数大于 20 亿,则需要使用 ROWCOUNT_BIG. @@ROWCOUNT和@@ERROR变 ...
- uva 10561 sg定理
Problem C Treblecross Input: Standard Input Output: Standard Output Time Limit: 4 Seconds Treblecros ...
- Mysql字符集与校对规则
字符集是一套字符和编码的集合,校对规则是用于比较字符集的一套规则. 所以字符集有两部分组成字符集合和对应的编码集合.比如说,现在有这几个字符:A B a b, 假设它们对应的编码分别是00, 01, ...
- Oracle命令行创建数据库
创建数据库文件 CREATE TABLESPACE MyDataBase LOGGING DATAFILE 'D:\Oracle\database\MyDataBase.dbf' SIZE 100M ...
- SGU 107 数学题
题意:求平方后末尾9个数是987654321的数个数. 之前做此题,竟然愚蠢到用计算器 在哪里算,还加笔算,SB啊!不知道先打印一下吗! #include<iostream> #inclu ...
- 树莓派LED指示灯说明
原文:http://shumeipai.nxez.com/2014/09/30/raspberry-pi-led-status-detail.html?variant=zh-cn LED亮灯状态 LE ...
- Leetcode 数组问题:删除排序数组内的重复项
问题描述: 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...
- 如何细粒度地控制你的MyBatis二级缓存(mybatis-enhanced-cache插件实现)
前几天网友chanfish 给我抛出了一个问题,笼统地讲就是如何能细粒度地控制MyBatis的二级缓存问题,酝酿了几天,觉得可以写个插件来实现这个这一功能.本文就是从问题入手,一步步分析现存的MyBa ...