姓名:Anne Hathaway
生日:1982年11月12日
出生地:美国纽约
身高:173cm
发色:深棕色
眼睛:棕色
安妮·海瑟薇出生在纽约布鲁克林,在新泽西州长大,父亲是位律师,母亲则是歌手兼演舞台剧演员。她在大学里主修英文,副修妇女研究,她说过,如果当不成演员,她会想当一位文学家或是心理学家。
在《公主日记》的试镜时,安妮意外地从椅子上摔了下来,结果反而被导演一眼相中,得到了扮演主角米娅的机会,并一举成名。安妮拥有灿烂明媚的笑容和高雅端正的气质,还带有一点点的调皮可爱,被誉为”茱莉亚·罗伯茨和奥黛莉·赫本”的综合体。

Tags: 性感
Philippines — ph.pool.ntp.org (1)
Malaysia — my.pool.ntp.org (4)
Turkey — tr.pool.ntp.org (1)
Singapore — sg.pool.ntp.org (2)
India — in.pool.ntp.org (1)
Hong Kong — hk.pool.ntp.org (1)
United Arab Emirates — ae.pool.ntp.org (0)
Japan — jp.pool.ntp.org (6)
Bangladesh — bd.pool.ntp.org (0)
Israel — il.pool.ntp.org (3)
Korea — kr.pool.ntp.org (4)
Thailand — th.pool.ntp.org (1)
Iran — ir.pool.ntp.org (0)
Taiwan — tw.pool.ntp.org (15)
China — cn.pool.ntp.org (6)
Indonesia — id.pool.ntp.org (3)
LXGIWYL = 暴徒武器
KJKSZPJ = 专业武器
UZUMYMW = 疯狂武器
HESOYAM = 生命、护甲满,加25万美圆
OSRBLHH = 增加两星通缉度
ASNAEB = 清除通缉程度
AFZLLQLL = 万里无云
ICIKPYH = 阳光明媚
ALNSFMZO = 阴云密布
AUIFRVQS = 阴雨绵绵
CFVFGMJ = 大雾弥漫
YSOHNUL = 时钟加快
PPGWJHT = 操控加快
LIYOAAY = 操控减慢
AJLOJYQY = 行人互相攻击,得到高尔夫球杆
BAGOWPG = 得到一大笔奖励
FOOOXFT = 行人全副武装
AIWPRTON = 刷新一辆坦克
CQZIJMB = 刷新一辆Bloodring Banger
JQNTDMH = 刷新一辆Rancher
PDNEJOH = 刷新一辆Racecar
VPJTQWV = 刷新一辆Racecar
AQTBCODX = 刷新一辆Romero
KRIJEBR = 刷新一辆Stretch
UBHYZHQ = 刷新一辆Trashmaster
RZHSUEW = 刷新一辆Caddy
CPKTNWT = Cars所有车辆爆炸
XICWMD = 隐行车辆
PGGOMOY = 完美操控
SZCMAWO = 自杀
ZEIIVG = 交通信号灯变绿
YLTEICZ = 司机有攻击性
LLQPFBN = 所有车辆变粉色
IOWDLAC = 所有车辆变黑色
AFSNMSMW = 船只飞行
BTCDBCB = 主角变胖
JYSDSOD = 主角肌肉值最大
KVGYZQK = 主角变得皮包骨
ASBHGRB = 行人变成猫王
BGLUAWML = 行人用武器攻击你,得到火箭发射器
CIKGCGX = 海滩聚会
MROEMZH = 到处都是黑帮
BIFBUZZ = 黑帮控制街道
AFPHULTL = 忍者模式
BEKKNQV = 吸引女
BGKGTJH = 通工具慢速
GUSNHDE = 交通工具快速
RIPAZHA = 汽车飞行
JHJOECW = 超级兔子跳
JUMPJET = 刷新一辆Hydra
KGGGDKP = 刷新一辆Vortex Hovercraft
JCNRUAD = 汽车一击必炸
COXEFGU = 所有车辆得到一氧化二氮加速剂
BSXSGGC = 车辆被撞击时会漂浮
XJVSNAJ = 永远是午夜
OFVIAC = 永远是晚上9点
MGHXYRM = 雷暴天气
CWJXUOC = 沙尘暴天气
LFGMHAL = 超级跳跃
BAGUVIX = 无限生命
CVWKXAM = 无限氧气
AIYPWZQP = 得到降落伞
BAGUVIX = 无限生命
AEZAKMI = 永远不会被通缉
WANRLTW = 无限弹药,不用换弹夹
IAVENJQ = 超级攻击
JCNRUAD = 汽车一击必炸
转至: http://www.codeguru.com/cpp/w-p/system/misc/article.php/c11393
Downloads
Windows NT 3.51 (I mean, Win3.1, Win95, Win98 were not perfect OSs). The MS-DOS data causes that your executable file to have the performance inside MS-DOS and the MS-DOS Stub program lets it display: "This program can not be run in MS-DOS mode" or "This program can be run only in Windows mode", or some things like these comments when you try to run a Windows EXE file inside MS-DOS 6.0, where there is no footstep of Windows. Thus, this data is reserved for the code to indicate these comments in the MS-DOS operating system. The most interesting part of the MS-DOS data is "MZ"! Can you believe, it refers to the name of "Mark Zbikowski", one of the first Microsoft programmers?
You might demand to comprehend the ways a virus program injects its procedure into the interior of a portable executable file and corrupts it, or you are interested in implementing a packer or a protector to encrypt the data of your portable executable (PE) file. This article is committed to represent a brief discussion to realize the performance that is accomplished by EXE tools or some kinds of mal-ware.
You can employ this article’s source code to create your custom EXE builder. It could be used to make an EXE protector in the right way, or with the wrong intention, to spread a virus. However, my purpose of writing this article has been the first application, so I will not be responsible for the immoral usage of these methods.
There are no specific mandatory prerequisites to follow the topics in this article. If you are familiar with a debugger and also the portable file format, I suggest you to drop to Sections 2 and 3; the whole of these sections has been made for people who don’t have any knowledge regarding the EXE file format or debuggers.
The Portable Executable file format was defined to provide the best way for the Windows Operating System to execute code and also to store the essential data that is needed to run a program—for example constant data, variable data, import library links, and resource data. It consists of MS-DOS file information, Windows NT file information, Section Headers, and Section images, as shown in Table 1.
These data let you remember the first days of developing the Windows Operating System. You were at the beginning of a way to achieve a complete Operating System such as
To me, only the offset of the PE signature in the MS-DOS data is important, so I can use it to find the position of the Windows NT data. I just recommend that you take a look at Table 1, and then observe the structure of IMAGE_DOS_HEADER in the <winnt.h> header in the <Microsoft Visual Studio .net path>\VC7\PlatformSDK\include\ folder or the <Microsoft Visual Studio 6.0 path>\VC98\include\ folder. I do not know why the Microsoft team has forgotten to provide some comment about this structure in the MSDN library!
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header "MZ" WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in // paragraphs WORD e_minalloc; // Minimum extra paragraphs // needed WORD e_maxalloc; // Maximum extra paragraphs // needed WORD e_ss; // Initial (relative) SS // value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS // value WORD e_lfarlc; // File address of relocation // table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier // (for e_oeminfo) WORD e_oeminfo; // OEM information; // e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of the new // exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
e_lfanew is the offset that refers to the position of the Windows NT data. I have provided a program to obtain the header information from an EXE file and to display it to you. To use the program, just try:
This sample is useful for the whole of this article.
Table 1: Portable Executable file format structure
| MS-DOS information |
IMAGE_DOS_ HEADER |
DOS EXE Signature |
00000000 ASCII "MZ"00000002 DW 009000000004 DW 000300000006 DW 000000000008 DW 00040000000A DW 00000000000C DW FFFF0000000E DW 000000000010 DW 00B800000012 DW 000000000014 DW 000000000016 DW 000000000018 DW 00400000001A DW 00000000001C DB 00b&b&0000003B DB 000000003C DD 000000F0
|
| DOS_PartPag | |||
| DOS_PageCnt | |||
| DOS_ReloCnt | |||
| DOS_HdrSize | |||
| DOS_MinMem | |||
| DOS_MaxMem | |||
| DOS_ReloSS | |||
| DOS_ExeSP | |||
| DOS_ChkSum | |||
| DOS_ExeIPP | |||
| DOS_ReloCS | |||
| DOS_TablOff | |||
| DOS_Overlay | |||
| b& Reserved words b& |
|||
| Offset to PE signature | |||
| MS-DOS Stub Program |
00000040 ..B:..B4.C!B8\LC!This program canno00000060 t be run in DOS mode....$.......
|
||
| Windows NT information IMAGE_ |
Signature | PE signature (PE) |
000000F0 ASCII "PE"
|
| IMAGE_ FILE_HEADER |
Machine |
000000F4 DW 014C000000F6 DW 0003000000F8 DD 3B7D8410000000FC DD 0000000000000100 DD 0000000000000104 DW 00E000000106 DW 010F
|
|
| NumberOfSections | |||
| TimeDateStamp | |||
| PointerToSymbolTable | |||
| NumberOfSymbols | |||
| SizeOfOptionalHeader | |||
| Characteristics | |||
| IMAGE_ OPTIONAL_ HEADER32 |
MagicNumber |
00000108 DW 010B0000010A DB 070000010B DB 000000010C DD 0001280000000110 DD 00009C0000000114 DD 0000000000000118 DD 000124750000011C DD 0000100000000120 DD 0001400000000124 DD 0100000000000128 DD 000010000000012C DD 0000020000000130 DW 000500000132 DW 000100000134 DW 000500000136 DW 000100000138 DW 00040000013A DW 00000000013C DD 0000000000000140 DD 0001F00000000144 DD 0000040000000148 DD 0001D7FC0000014C DW 00020000014E DW 800000000150 DD 0004000000000154 DD 0000100000000158 DD 001000000000015C DD 0000100000000160 DD 0000000000000164 DD 00000010
|
|
| MajorLinkerVersion | |||
| MinorLinkerVersion | |||
| SizeOfCode | |||
| SizeOfInitializedData | |||
| SizeOfUninitializedData | |||
| AddressOfEntryPoint | |||
| BaseOfCode | |||
| BaseOfData | |||
| ImageBase | |||
| SectionAlignment | |||
| FileAlignment | |||
| MajorOSVersion | |||
| MinorOSVersion | |||
| MajorImageVersion | |||
| MinorImageVersion | |||
| MajorSubsystemVersion | |||
| MinorSubsystemVersion | |||
| Reserved | |||
| SizeOfImage | |||
| SizeOfHeaders | |||
| CheckSum | |||
| Subsystem | |||
| DLLCharacteristics | |||
| SizeOfStackReserve | |||
| SizeOfStackCommit | |||
| SizeOfHeapReserve | |||
| SizeOfHeapCommit | |||
| LoaderFlags | |||
| NumberOfRvaAndSizes | |||
| IMAGE_ DATA_DIRECTORY[16] |
Export Table | ||
| Import Table | |||
| Resource Table | |||
| Exception Table | |||
| Certificate File | |||
| Relocation Table | |||
| Debug Data | |||
| Architecture Data | |||
| Global Ptr | |||
| TLS Table | |||
| Load Config Table | |||
| Bound Import Table | |||
| Import Address Table | |||
| Delay Import Descriptor | |||
| COM+ Runtime Header | |||
| Reserved | |||
| Sections information |
IMAGE_ SECTION_ HEADER[0] |
Name[8] |
000001E8 ASCII".text"000001F0 DD 000126B0000001F4 DD 00001000000001F8 DD 00012800000001FC DD 0000040000000200 DD 0000000000000204 DD 0000000000000208 DW 00000000020A DW 00000000020C DD 60000020 CODE|EXECUTE|READ
|
| VirtualSize | |||
| VirtualAddress | |||
| SizeOfRawData | |||
| PointerToRawData | |||
| PointerToRelocations | |||
| PointerToLineNumbers | |||
| NumberOfRelocations | |||
| NumberOfLineNumbers | |||
| Characteristics | |||
| b& b& b& IMAGE_ SECTION_ HEADER[n] |
00000210 ASCII".data"; SECTION00000218 DD 0000101C ; VirtualSize = 0x101C0000021C DD 00014000 ; VirtualAddress = 0x1400000000220 DD 00000A00 ; SizeOfRawData = 0xA0000000224 DD 00012C00 ; PointerToRawData = 0x12C0000000228 DD 00000000 ; PointerToRelocations = 0x00000022C DD 00000000 ; PointerToLineNumbers = 0x000000230 DW 0000 ; NumberOfRelocations = 0x000000232 DW 0000 ; NumberOfLineNumbers = 0x000000234 DD C0000040 ; Characteristics = INITIALIZED_DATA|READ|WRITE00000238 ASCII".rsrc"; SECTION00000240 DD 00008960 ; VirtualSize = 0x896000000244 DD 00016000 ; VirtualAddress = 0x1600000000248 DD 00008A00 ; SizeOfRawData = 0x8A000000024C DD 00013600 ; PointerToRawData = 0x1360000000250 DD 00000000 ; PointerToRelocations = 0x000000254 DD 00000000 ; PointerToLineNumbers = 0x000000258 DW 0000 ; NumberOfRelocations = 0x00000025A DW 0000 ; NumberOfLineNumbers = 0x00000025C DD 40000040 ; Characteristics = INITIALIZED_DATA|READ
|
||
| SECTION[0] |
00000400 EA 22 DD 77 D7 23 DD 77 C*"C.wC.#C.w00000408 9A 18 DD 77 00 00 00 00 E!.C.w....00000410 2E 1E C7 77 83 1D C7 77 ..C.wF..C.w00000418 FF 1E C7 77 00 00 00 00 C?.C.w....00000420 93 9F E7 77 D8 05 E8 77 b.E8C'wC..C(w00000428 FD A5 E7 77 AD A9 E9 77 C=B%C'w­B)C)w00000430 A3 36 E7 77 03 38 E7 77 B#6C'w.8C'w00000438 41 E3 E6 77 60 8D E7 77 AC#C&w`BC'w00000440 E6 1B E6 77 2B 2A E7 77 C&.C&w+*C'w00000448 7A 17 E6 77 79 C8 E6 77 z.C&wyC.C&w00000450 14 1B E7 77 C1 30 E7 77 ..C'wC.0C'wb&
|
||
| b& b& b& SECTION[n] |
b&0001BF00 63 00 2E 00 63 00 68 00 c...c.h.0001BF08 6D 00 0A 00 43 00 61 00 m...C.a.0001BF10 6C 00 63 00 75 00 6C 00 l.c.u.l.0001BF18 61 00 74 00 6F 00 72 00 a.t.o.r.0001BF20 11 00 4E 00 6F 00 74 00 ..N.o.t.0001BF28 20 00 45 00 6E 00 6F 00 .E.n.o.0001BF30 75 00 67 00 68 00 20 00 u.g.h. .0001BF38 4D 00 65 00 6D 00 6F 00 M.e.m.o.0001BF40 72 00 79 00 00 00 00 00 r.y.....0001BF48 00 00 00 00 00 00 00 00 ........0001BF50 00 00 00 00 00 00 00 00 ........0001BF58 00 00 00 00 00 00 00 00 ........0001BF60 00 00 00 00 00 00 00 00 ........0001BF68 00 00 00 00 00 00 00 00 ........0001BF70 00 00 00 00 00 00 00 00 ........0001BF78 00 00 00 00 00 00 00 00 ........
|
||
As mentioned in the preceding section, e_lfanew storage in the MS-DOS data structure refers to the location of the Windows NT information. Hence, if you assume that the pMem pointer relates the start point of the memory space for a selected portable executable file, you can retrieve the MS-DOS header and also the Windows NT headers by the following lines, which you also can perceive in the PE viewer sample (pelib.cpp, PEStructure::OpenFileName()):
IMAGE_DOS_HEADER image_dos_header;IMAGE_NT_HEADERS image_nt_headers;PCHAR pMem;b&memcpy(&image_dos_header, pMem, sizeof(IMAGE_DOS_HEADER));memcpy(&image_nt_headers, pMem+image_dos_header.e_lfanew, sizeof(IMAGE_NT_HEADERS));
IMAGE_NT_HEADERS structure definition. It makes it possible to grasp what the image NT header maintains to execute a code inside the Windows NT OS. Now, you are conversant with the Windows NT structure; it consists of the "PE" Signature, the File Header, and the Optional Header. Do not forget to take a glimpse at their comments in the MSDN Library and in Table 1.
It seems to be very simple, the retrieval of the headers information. I recommend inspecting the MSDN library regarding the
One the whole, I consider merely, in most circumstances, the following cells of the IMAGE_NT_HEADERS structure:
FileHeader->NumberOfSectionsOptionalHeader->AddressOfEntryPointOptionalHeader->ImageBaseOptionalHeader->SectionAlignmentOptionalHeader->FileAlignmentOptionalHeader->SizeOfImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] ->VirtualAddressOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] ->Size
You can observe the main purpose of these values clearly, and their role when the internal virtual memory space allocated for an EXE file by the Windows task manager if you pay attention to their explanations in MSDN library, so I am not going to repeat the MSDN annotations here.
I should make a brief comment regarding the PE data directories, or OptionalHeader-> DataDirectory[], because I think there are a few aspects of interest concerning them. When you come to survey the Optional header through the Windows NT information, you will find that there are 16 directories at the end of the Optional Header, where you can find the consecutive directories, including their Relative Virtual Address and Size. I just mention here the notes from <winnt.h> to clarify these information:
// Export Directory#define IMAGE_DIRECTORY_ENTRY_EXPORT 0// Import Directory#define IMAGE_DIRECTORY_ENTRY_IMPORT 1// Resource Directory#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2// Exception Directory#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3// Security Directory#define IMAGE_DIRECTORY_ENTRY_SECURITY 4// Base Relocation Table#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5// Debug Directory#define IMAGE_DIRECTORY_ENTRY_DEBUG 6// Architecture Specific Data#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7// RVA of GP#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8// TLS Directory#define IMAGE_DIRECTORY_ENTRY_TLS 9// Load Configuration Directory#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10// Bound Import Directory in headers#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11// Import Address Table#define IMAGE_DIRECTORY_ENTRY_IAT 12// Delay Load Import Descriptors#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13// COM Runtime descriptor#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
The last one (15) was reserved for use in the future; I have not yet seen any purpose for it, even in PE64.
For instance, if you want to perceive the relative virtual address (RVA) and the size of the resource data, it is enough to retrieve them by:
DWORD dwRVA = image_nt_headers.OptionalHeader-> DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]->VirtualAddress;DWORD dwSize = image_nt_headers.OptionalHeader-> DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]->Size;
To comprehend more regarding the significance of data directories, I forward you to Section 3.4.3 of the Microsoft Portable Executable and the Common Object File Format Specification document by Microsoft, and furthermore Section 6 of this document, where you discern the various types of sections and their applications. You will see the section’s advantage subsequently.
You currently observe how the portable executable files declare the location and the size of a section on a disk storage file and inside the virtual memory space allocated for the program with IMAGE_NT_HEADERS-> OptionalHeader->SizeOfImage by the Windows task manager, as well the characteristics to demonstrate the type of the section. To better understand the Section header as my previous declaration, I suggest having a brief look at the IMAGE_SECTION_HEADER structure definition in the MSDN library. For an EXE packer developer, VirtualSize, VirtualAddress, SizeOfRawData, PointerToRawData, and Characteristics cells have significant rules. When developing an EXE packer, you should be clever enough to play with them. There are somet hings to note when you modify them; you should take care to align the VirtualSize and VirtualAddress according to OptionalHeader->SectionAlignment, as well as SizeOfRawData and PointerToRawData in line with OptionalHeader->FileAlignment. Otherwise, you will corrupt your target EXE file and it will never run. Regarding Characteristics, I pay attention mostly to establish a section by IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA, I prefer that my new section has the ability to initialize such data during the running process, such as import table; besides, I need it to be able to modify itself by the loader with my settings in the section characteristics to read- and writeable.
Moreover, you should pay attention to the section names; you can know the purpose of each section by its name. I will just forward you to Section 6 of the Microsoft Portable Executable and the Common Object File Format Specification documents. I believe it represents the totality of sections by their names; this is also included in Table 2.
Table 2: Section names
| ".text" | Code Section |
| "CODE" | Code Section of file linked by Borland Delphi or Borland Pascal |
| ".data" | Data Section |
| "DATA" | Data Section of file linked by Borland Delphi or Borland Pascal |
| ".rdata" | Section for Constant Data |
| ".idata" | Import Table |
| ".edata" | Export Table |
| ".tls" | TLS Table |
| ".reloc" | Relocation Information |
| ".rsrc" | Resource Information |
To comprehend the section headers and also the sections, you can run the sample PE viewer. With this PE viewer, you can realize only the application of the section headers in a file image, so to observe the main significance in the Virtual Memory, you should try to load a PE file by a debugger. The next section represents the main idea of using the virtual address and size in the virtual memory by using a debugger. The last note is about IMAGE_NT_HEADERS-> FileHeader->NumberOfSections, that provides a number of sections in a PE file. Do not forget to adjust it whenever you remove or add some sections to a PE file. I am talking about section injection!
In this part, you will become familiar with the necessary and essential equipment to develop your PE tools.
The first essential prerequisite to become a PE tools developer is to have enough experience with bug tracer tools. Furthermore, you should know most of the assembly instructions. To me, the Intel documents are the best references. You can obtain them from the Intel site for IA-32, and on top of that IA-64; the future belongs to IA-64 CPUs, Windows XP 64-bit, and also PE64!
To trace a PE file, SoftICE by Compuware Corporation, I knew it also as named NuMega when I was at high school, is the best debugger in the world. It implements process tracing by using the kernel mode method debugging without applying Windows debugging application programming interface (API) functions. In addition, I will introduce one perfect debugger in user mode level. It utilizes the Windows debugging API to trace a PE file and also attaches itself to an active process. These API functions have been provided by Microsoft teams, inside the Windows Kernel32 library, to trace a specific process, by using Microsoft tools, or perhaps, to make your own debugger! Some of those API functions inlude:
It was in 1987; Frank Grossman and Jim Moskun decided to establish a company called NuMega Technologies in Nashua, NH, to develop some equipment to trace and test the reliability of Microsoft Windows software programs. Now, it is a part of Compuware Corporation and its product has participated to accelerate the reliability in Windows software, and additionally in Windows driver developments. Currently, everyone knows the Compuware DriverStudio that is used to establish an environment for implementing the elaboration of a kernel driver or a system file by aiding the Windows Driver Development Kit (DDK). It bypasses the involvement of DDK to implement a portable executable file of kernel level for a Windows system software developer. For us, only one instrument of DriverStudio is important, SoftICE; this debugger can be used to trace every portable executable file, a PE file for user mode level or a PE file for kernel mode level.
Figure 1: SoftICE Window
| EAX=00000000EBX=7FFDD000 ECX=0007FFB0 EDX=7C90EB94 ESI=FFFFFFFF EDI=7C919738 EBP=0007FFF0 ESP=0007FFC4 EIP=010119E0 o d i s z a p c CS=0008 DS=0023 SS=0010 ES=0023 FS=0030 GS=0000 SS:0007FFC4=87C816D4F |
| 0023:01013000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 0023:01013010 01 00 00 00 20 00 00 00-0A 00 00 00 0A 00 00 00 ……………. 0023:01013020 20 00 00 00 00 00 00 00-53 63 69 43 61 6C 63 00 ……..SciCalc. 0023:01013030 00 00 00 00 00 00 00 00-62 61 63 6B 67 72 6F 75 ……..backgrou 0023:01013040 6E 64 00 00 00 00 00 00-2E 00 00 00 00 00 00 00 nd………….. |
| 0010:0007FFC4 4F 6D 81 7C 38 07 91 7C-FF FF FF FF 00 90 FD 7F Om |8 b.| . 0010:0007FFD4 ED A6 54 80 C8 FF 07 00-E8 B4 F5 81 FF FF FF FF T . 0010:0007FFE4 F3 99 83 7C 58 6D 81 7C-00 00 00 00 00 00 00 00 Xm |…….. 0010:0007FFF4 00 00 00 00 E0 19 01 01-00 00 00 00 00 00 00 00 …. …. |
| 010119E0 PUSH EBP 010119E1 MOV EBP,ESP 010119E3 PUSH -1 010119E5 PUSH 01001570 010119EA PUSH 01011D60 010119EF MOV EAX,DWORD PTR FS:[0] 010119F5 PUSH EAX 010119F6 MOV DWORD PTR FS:[0],ESP 010119FD ADD ESP,-68 01011A00 PUSH EBX 01011A01 PUSH ESI 01011A02 PUSH EDI 01011A03 MOV DWORD PTR SS:[EBP-18],ESP 01011A06 MOV DWORD PTR SS:[EBP-4],0 |
| :_
|
It was about four years ago that I first saw this debugger by chance. For me, it was the best choice; I was not wealthy enough to purchase SoftICE, and at that time, SoftICE only had good functions for DOS, Windows 98, and Windows 2000. I found that this debugger supported all kinds of Windows versions. Therefore, I started to learn it very fast, and now it is my favorite debugger for the Windows OS. It is a debugger that can be used to trace all kinds of portable executable files except a Common Language Infrastructure (CLI) file format in user mode level, by using the Windows debugging API. Oleh Yuschuk, the author, is one of worthiest software developers I have seen in my life. He is a Ukrainian who now lives in Germany. I should mention here that his debugger is the best choice for hacker and cracker parties around the world! It is freeware! You can try it from the OllyDbg Homepage.
Figure 2: OllyDbg CPU Window I have introduced two debuggers without talking about how you can employ them, and also which parts you should pay attention to. Regarding using debuggers, I refer you to their instructions in help documents. However, I want to explain briefly the important parts of a debugger; of course, I am talking about low-level debuggers, or in other words, machine-language debuggers of the x86 CPU families. All of low-level debuggers consist of the following subdivisions: o d t s z a p c You can compare Figures 1 and 2 to distinguish the difference between SoftICE and OllyDbg. When you want to trace a PE file, you should mostly consider these five subdivisions. Furthermore, every debugger comprises of some other useful parts; you should discover them by yourself. You can consider OllyDbg and SoftICE to be excellent disassemblers, but I also want to introduce another disassembler tool that is famous in the reverse engineering world. Proview or PVDasm is an admirable disassembler by the Reverse-Engineering-Community; it is still under development and bug fixing. You can find its disassmbler source engine and employ it to create your own disassembler. W32DASM can disassemble both 16- and 32-bit executable file formats. In addition to its disassembling ability, you can employ it to analyze import, export, and resource data directories data. All reverse-engineering experts know that IDA Pro can be used to investigate, not only x86 instructions, but that of various kinds of CPU types like AVR, PIC, and so forth. It can illustrate the assembly source of a portable executable file by using colored graphics and tables, and is very useful for any newbie in this area. Furthermore, it has the capability to trace an executable file inside the user mode level in the same way as OllyDbg. A good PE tools developer is conversant with the tools that save his time, so I recommend that you select some appropriate instruments to investigate the base information under a portable executable file. LordPE by y0da is still the first choice to retrieve PE file information with the possibility to modify them. PE iDentifier is valuable to identify the type of compilers, packers, and cryptors of PE files. As of now, it can detect more than 500 different signature types of PE files. Resource Hacker can be employed to modify resource directory information; icon, menu, version info, string table, and so on. WinHex, it is clear what you can do with this tool. Eventually, CFF Explorer by Ntoskrnl is what you want to have as a PE Utility tool in your arsenal; it supports PE32/64, PE rebuild included Common Language Infrastructure (CLI) file. In other words, the .NET file, a resource modifier, and much more facilities which can not be found in others. Just try to discover every unimaginable option by hand. You are ready to do the first step of making your project. I have provided a library to add a new section and rebuild the portable executable file. Before starting, I wnat you to get familiar with the headers of a PE file, by using OllyDbg. You should first open a PE file; that pops up a menu, View->Executable file. Again, you get a popup menu: Special->PE header. You will observe a scene similar to Figure 3. Now, come to the Main Menu View->Memory, and try to distinguish the sections inside the Memory map window.
(3.1.3 Which parts are important in a debugger interface?
EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI
EIP
010119E0 PUSH EBP010119E1 MOV EBP,ESP010119E3 PUSH -1010119E5 PUSH 01001570010119EA PUSH 01011D60010119EF MOV EAX,DWORD PTR FS:[0]010119F5 PUSH EAX010119F6 MOV DWORD PTR FS:[0],ESP010119FD ADD ESP,-6801011A00 PUSH EBX01011A01 PUSH ESI01011A02 PUSH EDI01011A03 MOV DWORD PTR SS:[EBP-18],ESP01011A06 MOV DWORD PTR SS:[EBP-4],0
0023:01013000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 0023:01013010 01 00 00 00 20 00 00 00-0A 00 00 00 0A 00 00 00 ……………. 0023:01013020 20 00 00 00 00 00 00 00-53 63 69 43 61 6C 63 00 ……..SciCalc. 0023:01013030 00 00 00 00 00 00 00 00-62 61 63 6B 67 72 6F 75 ……..backgrou 0023:01013040 6E 64 00 00 00 00 00 00-2E 00 00 00 00 00 00 00 nd…………..
0010:0007FFC4 4F 6D 81 7C 38 07 91 7C-FF FF FF FF 00 90 FD 7F Om |8 b.| . 0010:0007FFD4 ED A6 54 80 C8 FF 07 00-E8 B4 F5 81 FF FF FF FF T . 0010:0007FFE4 F3 99 83 7C 58 6D 81 7C-00 00 00 00 00 00 00 00 Xm |…….. 0010:0007FFF4 00 00 00 00 E0 19 01 01-00 00 00 00 00 00 00 00 …. ….
Command
SoftICE
OllyDbg
Run
F5
F9
Step Into
F11
F7
Step Over
F10
F8
Set Break Point
F8
F2
3.2 Disassembler
3.2.1 Proview disassembler
3.2.2 W32Dasm
3.2.3 IDA Pro
3.3 Some Useful Tools
3.3.1 LordPE
3.3.2 PEiD
3.3.3 Resource Hacker
3.3.4 WinHex
3.3.5 CFF Explorer
4 Add a New Section and Change the OEP
Figure 3
00000000000000020000000400000006000000080000000A0000000C0000000E00000010000000120000001400000016000000180000001A0000001C0000001D0000001E0000001F000000200000002100000022000000230000002400000025000000260000002700000028000000290000002A0000002B0000002C0000002D0000002E0000002F000000300000003100000032000000330000003400000035000000360000003700000038000000390000003A0000003B0000003C
|
4D 5A 9000 0300 0000 0400 0000 FFFF 0000 B800 0000 0000 0000 4000 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0000000 |
ASCII "MZ" DW 0090 DW 0003 DW 0000 DW 0004 DW 0000 DW FFFF DW 0000 DW 00B8 DW 0000 DW 0000 DW 0000 DW 0040 DW 0000 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DB 00 DD 000000F0 |
DOS EXE Signature DOS_PartPag = 90 (144.) DOS_PageCnt = 3 DOS_ReloCnt = 0 DOS_HdrSize = 4 DOS_MinMem = 0 DOS_MaxMem = FFFF (65535.) DOS_ReloSS = 0 DOS_ExeSP = B8 DOS_ChkSum = 0 DOS_ExeIP = 0 DOS_ReloCS = 0 DOS_TablOff = 40 DOS_Overlay = 0 Offset to PE signature |
|
Virtual_Address = Image_Base + Relative_Virtual_Address |
DWORD OEP_RVA = image_nt_headers-> OptionalHeader.AddressOfEntryPoint ;// OEP_RVA = 0x00012475DWORD OEP_VA = image_nt_headers-> OptionalHeader.ImageBase + OEP_RVA ;// OEP_VA = 0x01000000 + 0x00012475 = 0x01012475
Download pemaker1.zip and test1.zip from the files at the end of this article.
DynLoader(), in loader.cpp, is reserved for the data of the new section—in other words, the Loader.
__stdcall void DynLoader(){_asm{//---------------------------------- DWORD_TYPE(DYN_LOADER_START_MAGIC)//---------------------------------- MOV EAX,01012475h // << Original OEP JMP EAX//---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC)//----------------------------------}}
Unfortunately, this source can only be applied for the sample test file. You should complete it by saving the value of the original OEP in the new section, and use it to reach the real OEP. I have accomplished it in Step 2 (Section 5).
I have made a simple class library to recover PE information and to use it in a new PE file.
//----------------------------------------------------------------class CPELibrary{private: //----------------------------------------- PCHAR pMem; DWORD dwFileSize; //-----------------------------------------protected: //----------------------------------------- PIMAGE_DOS_HEADER image_dos_header; PCHAR pDosStub; DWORD dwDosStubSize, dwDosStubOffset; PIMAGE_NT_HEADERS image_nt_headers; PIMAGE_SECTION_HEADER image_section_header[MAX_SECTION_NUM]; PCHAR image_section[MAX_SECTION_NUM]; //-----------------------------------------protected: //----------------------------------------- DWORD PEAlign(DWORD dwTarNum,DWORD dwAlignTo); void AlignmentSections(); //----------------------------------------- DWORD Offset2RVA(DWORD dwRO); DWORD RVA2Offset(DWORD dwRVA); //----------------------------------------- PIMAGE_SECTION_HEADER ImageRVA2Section(DWORD dwRVA); PIMAGE_SECTION_HEADER ImageOffset2Section(DWORD dwRO); //----------------------------------------- DWORD ImageOffset2SectionNum(DWORD dwRVA); PIMAGE_SECTION_HEADER AddNewSection(char* szName,DWORD dwSize); //-----------------------------------------public: //----------------------------------------- CPELibrary(); ~CPELibrary(); //----------------------------------------- void OpenFile(char* FileName); void SaveFile(char* FileName); //-----------------------------------------};
In Table 1, the usage of image_dos_header, pDosStub, image_nt_headers, image_section_header [MAX_SECTION_NUM], and image_section[MAX_SECTION_NUM] is clear. You use OpenFile() and SaveFile() to retrieve and rebuild a PE file. Furthermore, AddNewSection() is employed to create the new section, the important step.
You can comprehend the difference between incremental link and no-incremental link by looking at the following picture:
To acquire the virtual address of DynLoader(), you obtain the virtual address of JMP pemaker.DynLoader in the incremental link, but by no-incremental link, the real virtual address is gained by the following code:
DWORD dwVA= (DWORD) DynLoader;
This setting is more critical in the incremental link when you try to find the beginning and ending of the Loader, DynLoader(), by CPECryptor::ReturnToBytePtr():
void* CPECryptor::ReturnToBytePtr(void* FuncName, DWORD findstr){ void* tmpd; __asm { mov eax, FuncName jmp dfhjg: inc eaxdf: mov ebx, [eax] cmp ebx, findstr jnz hjg mov tmpd, eax } return tmpd;}
In pecrypt.cpp, I have represented another class, CPECryptor, to comprise the data of the new section. Nevertheless, the data of the new section is created by DynLoader() in loader.cpp, DynLoader Step 1. You use the CPECryptor class to enter this data in to the new section, and also some other stuff.
//----------------------------------------------------------------class CPECryptor: public CPELibrary{private: //---------------------------------------- PCHAR pNewSection; //---------------------------------------- DWORD GetFunctionVA(void* FuncName); void* ReturnToBytePtr(void* FuncName, DWORD findstr); //----------------------------------------protected: //----------------------------------------public: //---------------------------------------- void CryptFile(int(__cdecl *callback) (unsigned int, unsigned int)); //----------------------------------------};//----------------------------------------------------------------
image_section_header[i]->VirtualAddress= PEAlign(image_section_header[i]->VirtualAddress, image_nt_headers->OptionalHeader.SectionAlignment);image_section_header[i]->Misc.VirtualSize= PEAlign(image_section_header[i]->Misc.VirtualSize, image_nt_headers->OptionalHeader.SectionAlignment);
image_section_header[i]->PointerToRawData = PEAlign(image_section_header[i]->PointerToRawData, image_nt_headers->OptionalHeader.FileAlignment);image_section_header[i]->SizeOfRawData = PEAlign(image_section_header[i]->SizeOfRawData, image_nt_headers->OptionalHeader.FileAlignment);
image_nt_headers->OptionalHeader.SizeOfImage = image_section_header[LastSection]->VirtualAddress + image_section_header[LastSection]->Misc.VirtualSize;
image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]. VirtualAddress = 0;image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_ IMPORT].Size = 0;
(
Right now, we save the Original OEP and also the Image Base in order to reach to the virtual address of OEP. I have reserved a free space at the end of DynLoader() to store them, DynLoader Step 2.
Download the pemaker2.zip source files from the end of the article.
__stdcall void DynLoader(){_asm{//------------------------------------ DWORD_TYPE(DYN_LOADER_START_MAGIC)//------------------------------------Main_0: PUSHAD // get base ebp CALL Main_1Main_1: POP EBP SUB EBP,OFFSET Main_1 MOV EAX,DWORD PTR [EBP+_RO_dwImageBase] ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint] PUSH EAX RETN // >> JMP to Original OEP//---------------------------------- DWORD_TYPE(DYN_LOADER_START_DATA1)//----------------------------------//---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC)//----------------------------------}}_RO_dwImageBase: DWORD_TYPE(0xCCCCCCCC)_RO_dwOrgEntryPoint: DWORD_TYPE(0xCCCCCCCC)
The new function, CPECryptor::CopyData1(), will implement the copy of the Image Base value and the Offset of Entry Point value into 8 bytes of free space in the loader.
It is important to recover the Original Context of the thread. You have not yet done it in the DynLoader Step 2 source code. You can modify the source of DynLoader() to repossess the first Context.
__stdcall void DynLoader(){_asm{//------------------------------------ DWORD_TYPE(DYN_LOADER_START_MAGIC)//------------------------------------Main_0: PUSHAD// Save the registers context in stack CALL Main_1Main_1: POP EBP// Get Base EBP SUB EBP,OFFSET Main_1 MOV EAX,DWORD PTR [EBP+_RO_dwImageBase] ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint] MOV DWORD PTR [ESP+1Ch],EAX // pStack.Eax <- EAX POPAD // Restore the first registers context from stack PUSH EAX XOR EAX, EAX RETN // >> JMP to Original OEP//---------------------------------- DWORD_TYPE(DYN_LOADER_START_DATA1)//----------------------------------_RO_dwImageBase: DWORD_TYPE(0xCCCCCCCC)_RO_dwOrgEntryPoint: DWORD_TYPE(0xCCCCCCCC)//---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC)//----------------------------------}}
You also can recover the original stack by setting the value of the beginning stack + 0x34 to the Original OEP, but it is not very important. Nevertheless, in the following code, I have accomplished the loader code by a simple trick to reach the OEP in addition to redecorating the stack. You can observe the implementation by tracing using OllyDbg or SoftICE.
__stdcall void DynLoader(){_asm{//---------------------------------- DWORD_TYPE(DYN_LOADER_START_MAGIC)//----------------------------------Main_0: PUSHAD // Save the registers context in stack CALL Main_1Main_1: POP EBP SUB EBP,OFFSET Main_1 MOV EAX,DWORD PTR [EBP+_RO_dwImageBase] ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint] MOV DWORD PTR [ESP+54h],EAX // pStack.Eip <- EAX POPAD // Restore the first registers context from stack CALL _OEP_Jump DWORD_TYPE(0xCCCCCCCC)_OEP_Jump: PUSH EBP MOV EBP,ESP MOV EAX,DWORD PTR [ESP+3Ch] // EAX <- pStack.Eip MOV DWORD PTR [ESP+4h],EAX // _OEP_Jump RETURN pointer <- EAX XOR EAX,EAX LEAVE RETN//---------------------------------- DWORD_TYPE(DYN_LOADER_START_DATA1)//----------------------------------_RO_dwImageBase: DWORD_TYPE(0xCCCCCCCC)_RO_dwOrgEntryPoint: DWORD_TYPE(0xCCCCCCCC)//---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC)//----------------------------------}}
try-except statement in C++ clarifies the operation of structured exception handling. Besides the assembly code of this code, it elucidates the structured exception handler installation, the raise of an exception, and the exception handler function.
An exception is generated when a program falls into a fault code execution and an error happens, so in such a special condition, the program immediately jumps to a function called the exception handler from exception handler list of the Thread Information Block.
The next example of a
#include "stdafx.h"#include "windows.h"void RAISE_AN_EXCEPTION(){_asm{ INT 3 INT 3 INT 3 INT 3}}int _tmain(int argc, _TCHAR* argv[]){ __try { __try{ printf("1: Raise an Exception\n"); RAISE_AN_EXCEPTION(); } __finally { printf("2: In Finally\n"); } } __except( printf("3: In Filter\n"), EXCEPTION_EXECUTE_HANDLER ) { printf("4: In Exception Handler\n"); } return 0;}
; main()00401000: PUSH EBP00401001: MOV EBP,ESP00401003: PUSH -100401005: PUSH 00407160; __try {; the structured exception handler (SEH) installation 0040100A: PUSH _except_handler30040100F: MOV EAX,DWORD PTR FS:[0]00401015: PUSH EAX00401016: MOV DWORD PTR FS:[0],ESP0040101D: SUB ESP,800401020: PUSH EBX00401021: PUSH ESI00401022: PUSH EDI00401023: MOV DWORD PTR SS:[EBP-18],ESP; __try {00401026: XOR ESI,ESI00401028: MOV DWORD PTR SS:[EBP-4],ESI0040102B: MOV DWORD PTR SS:[EBP-4],100401032: PUSH OFFSET "1: Raise an Exception"00401037: CALL printf0040103C: ADD ESP,4; the raise a exception, INT 3 exception; RAISE_AN_EXCEPTION()0040103F: INT300401040: INT300401041: INT300401042: INT3; } __finally {00401043: MOV DWORD PTR SS:[EBP-4],ESI00401046: CALL 0040104D0040104B: JMP 004010800040104D: PUSH OFFSET "2: In Finally"00401052: CALL printf00401057: ADD ESP,40040105A: RETN; }; }; __except( 0040105B: JMP 004010800040105D: PUSH OFFSET "3: In Filter"00401062: CALL printf00401067: ADD ESP,40040106A: MOV EAX,1 ; EXCEPTION_EXECUTE_HANDLER = 10040106F: RETN; , EXCEPTION_EXECUTE_HANDLER ); {; the exception handler funtion00401070: MOV ESP,DWORD PTR SS:[EBP-18]00401073: PUSH OFFSET "4: In Exception Handler"00401078: CALL printf0040107D: ADD ESP,4; }00401080: MOV DWORD PTR SS:[EBP-4],-10040108C: XOR EAX,EAX; restore previous SEH0040108E: MOV ECX,DWORD PTR SS:[EBP-10]00401091: MOV DWORD PTR FS:[0],ECX00401098: POP EDI00401099: POP ESI0040109A: POP EBX0040109B: MOV ESP,EBP0040109D: POP EBP0040109E: RETN
Make a Win32 console project, and link and run the preceding C++ code, to perceive the result:
| 1: Raise an Exception 3: In Filter 2: In Finally 4: In Exception Handler _
|
This program runs the exception expression, printf("3: In Filter\n");, when an exception happens—in this example, the INT 3 exception. You can employ other kinds of exception too. In OllyDbg, Debugging options->Exceptions, you can see a short list of different types of exceptions.
You want to construct a structured exception handler to reach OEP. Now, I think you have distinguished the SEH installation, the exception raise, and the exception expression filter, by foregoing the assembly code. To establish your exception handler approach, you need to comprise the following codes:
LEA EAX,[EBP+_except_handler1_OEP_Jump]PUSH EAXPUSH DWORD PTR FS:[0]MOV DWORD PTR FS:[0],ESP
INT 3
_except_handler1_OEP_Jump: PUSH EBP MOV EBP,ESP ... // EXCEPTION_CONTINUE_SEARCH = 0 MOV EAX, EXCEPTION_CONTINUE_SEARCH LEAVE RETN
So, you yearn to make the ensuing C++ code in assembly language to inaugurate your engine to approach the Offset of the Entry Point by SEH.
__try // SEH installation{ __asm { INT 3 // An Exception Raise }}__except( ..., EXCEPTION_CONTINUE_SEARCH ){}// Exception handler expression filter
In assembly code…
; ---------------------------------------------------- ; the structured exception handler (SEH) installation ; __try { LEA EAX,[EBP+_except_handler1_OEP_Jump] PUSH EAX PUSH DWORD PTR FS:[0] MOV DWORD PTR FS:[0],ESP ; ---------------------------------------------------- ; the raise a INT 3 exception INT 3 INT 3 INT 3 INT 3 ; } ; __except( ... ; ---------------------------------------------------- ; exception handler expression filter_except_handler1_OEP_Jump: PUSH EBP MOV EBP,ESP ... MOV EAX, EXCEPTION_CONTINUE_SEARCH ; EXCEPTION_CONTINUE_SEARCH = 0 LEAVE RETN ; , EXCEPTION_CONTINUE_SEARCH ) { }
The exception value, __except(..., Value), determines how the exception is handled. It can have three values: 1, 0, -1. To understand them, refer to the try-except statement description in the MSDN library. You set it to EXCEPTION_CONTINUE_SEARCH (0), not to run the exception handler function; therefore, by this value, the exception is not recognized. It is simply ignored, and the thread continues its code execution.
As you perceived from the illustrated code, the SEH installation is done by the FS segment register. Microsoft Windows 32 bit uses the FS segment register as a pointer to the data block of the main thread. The first 0×1C bytes comprise the information of the Thread Information Block (TIB). Therefore, FS:[00h] refers to ExceptionList of the main thread, Table 3. In your code, you have pushed the pointer to _except_handler1_OEP_Jump in the stack and changed the value of ExceptionList, FS:[00h], to the beginning of the stack, ESP.
typedef struct _NT_TIB32 { DWORD ExceptionList; DWORD StackBase; DWORD StackLimit; DWORD SubSystemTib; union { DWORD FiberData; DWORD Version; }; DWORD ArbitraryUserPointer; DWORD Self;} NT_TIB32, *PNT_TIB32;
| DWORD PTR FS:[00h] | ExceptionList |
| DWORD PTR FS:[04h] | StackBase |
| DWORD PTR FS:[08h] | StackLimit |
| DWORD PTR FS:[0Ch] | SubSystemTib |
| DWORD PTR FS:[10h] | FiberData / Version |
| DWORD PTR FS:[14h] | ArbitraryUserPointer |
| DWORD PTR FS:[18h] | Self |
In this part, you effectuate your performance by accomplishing the OEP approach. You change the Context of the thread and ignore every simple exception handling, and let the thread continue the execution, but in the original OEP!
| Context Flags | 0×00000000 | ContextFlags | |
|
Context Debug Registers |
0×00000004 | Dr0 | |
| 0×00000008 | Dr1 | ||
| 0×0000000C | Dr2 | ||
| 0×00000010 | Dr3 | ||
| 0×00000014 | Dr6 | ||
| 0×00000018 | Dr7 | ||
|
Context Floating Point |
0×0000001C | FloatSave | StatusWord |
| 0×00000020 | StatusWord | ||
| 0×00000024 | TagWord | ||
| 0×00000028 | ErrorOffset | ||
| 0×0000002C | ErrorSelector | ||
| 0×00000030 | DataOffset | ||
| 0×00000034 | DataSelector | ||
| 0×00000038 … 0×00000087 |
RegisterArea [0x50] | ||
| 0×00000088 | Cr0NpxState | ||
| Context Segments | 0×0000008C | SegGs | |
| 0×00000090 | SegFs | ||
| 0×00000094 | SegEs | ||
| 0×00000098 | SegDs | ||
| Context Integer | 0×0000009C | Edi | |
| 0×000000A0 | Esi | ||
| 0×000000A4 | Ebx | ||
| 0×000000A8 | Edx | ||
| 0×000000AC | Ecx | ||
| 0×000000B0 | Eax | ||
| Context Control | 0×000000B4 | Ebp | |
| 0×000000B8 | Eip | ||
| 0×000000BC | SegCs | ||
| 0×000000C0 | EFlags | ||
| 0×000000C4 | Esp | ||
| 0×000000C8 | SegSs | ||
| Context Extended Registers |
0×000000CC |
ExtendedRegisters[0x200] | |
By the following code, you have accomplished the main purpose of coming to OEP by the structured exception handler:
__stdcall void DynLoader(){_asm{//---------------------------------- DWORD_TYPE(DYN_LOADER_START_MAGIC)//----------------------------------Main_0: PUSHAD // Save the registers context in stack CALL Main_1Main_1: POP EBP SUB EBP,OFFSET Main_1 // Get Base EBP MOV EAX,DWORD PTR [EBP+_RO_dwImageBase] ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint] MOV DWORD PTR [ESP+10h],EAX // pStack.Ebx <- EAX LEA EAX,[EBP+_except_handler1_OEP_Jump] MOV DWORD PTR [ESP+1Ch],EAX // pStack.Eax <- EAX POPAD // Restore the first registers context from stack //---------------------------------------------------- // the structured exception handler (SEH) installation PUSH EAX XOR EAX, EAX PUSH DWORD PTR FS:[0] // NT_TIB32.ExceptionList MOV DWORD PTR FS:[0],ESP // NT_TIB32.ExceptionList <-ESP //---------------------------------------------------- // the raise a INT 3 exception DWORD_TYPE(0xCCCCCCCC) //--------------------------------------------------------// -------- exception handler expression filter ----------_except_handler1_OEP_Jump: PUSH EBP MOV EBP,ESP //------------------------------ MOV EAX,DWORD PTR SS:[EBP+010h] // PCONTEXT: pContext <- EAX //============================== PUSH EDI // restore original SEH MOV EDI,DWORD PTR DS:[EAX+0C4h] // pContext.Esp PUSH DWORD PTR DS:[EDI] POP DWORD PTR FS:[0] ADD DWORD PTR DS:[EAX+0C4h],8 // pContext.Esp //------------------------------ // set the Eip to the OEP MOV EDI,DWORD PTR DS:[EAX+0A4h] // EAX <- pContext.Ebx MOV DWORD PTR DS:[EAX+0B8h],EDI // pContext.Eip <- EAX //------------------------------ POP EDI //============================== MOV EAX, EXCEPTION_CONTINUE_SEARCH LEAVE RETN//---------------------------------- DWORD_TYPE(DYN_LOADER_START_DATA1)//----------------------------------_RO_dwImageBase: DWORD_TYPE(0xCCCCCCCC)_RO_dwOrgEntryPoint: DWORD_TYPE(0xCCCCCCCC)//---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC)//----------------------------------}}
There are two ways to use the Windows dynamic link library (DLL) in Windows application programming:
(
Full Size Image)
// DLL function signaturetypedef HGLOBAL (*importFunction_GlobalAlloc)(UINT, SIZE_T);...importFunction_GlobalAlloc __GlobalAlloc;// Load DLL fileHINSTANCE hinstLib = LoadLibrary("Kernel32.dll");if (hinstLib == NULL){ // Error - unable to load DLL}// Get function pointer__GlobalAlloc = (importFunction_GlobalAlloc)GetProcAddress(hinstLib, "GlobalAlloc");if (addNumbers == NULL){ // Error - unable to find DLL function}FreeLibrary(hinstLib);
When you make a Windows application project, the linker includes at least kernel32.dll in the base dependencies of your project. Without LoadLibrary() and GetProcAddress() of Kernel32.dll, you cannot load a DLL at run time. The dependencies information is stored in the import table section. By using Dependency Walker, it is not so difficult to observe the DLL module and the functions that are imported into a PE file.
You attempt to establish your custom import table to conduct your project. Furthermore, you have to fix up the original import table at the end to run the real code of the program.
Download the pemaker3.zip source files from the end of the article.
I strongly advise that you to read Section 6.4 of the Microsoft Portable Executable and the Common Object File Format Specification document. This section contains the principal information to comprehend the import table performance. The import table data is accessible by a second data directory of the optional header from PE headers, so you can access it by using the following code:
DWORD dwVirtualAddress = image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. VirtualAddress;DWORD dwSize = image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. Size;
The VirtualAddress refers to structures by IMAGE_IMPORT_DESCRIPTOR. This structure contains the pointer to the imported DLL name and the relative virtual address of the first thunk.
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; // the imported DLL name DWORD FirstThunk; // the relative virtual address of the // first thunk} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
When a program is running, the Windows Task Manager sets the thunks by the virtual address of the function. The virtual address is found by the name of the function. At first, the thunks hold the relative virtual address of the function name, as shown in Table 5; during execution, they are fixed up by the virtual address of the functions (see Table 6).
| IMAGE_IMPORT_ DESCRIPTOR[0] |
OriginalFirstThunk | ||||
| TimeDateStamp | |||||
| ForwarderChain | |||||
| Name_RVA | ——> | "kernel32.dll",0 | |||
| FirstThunk_RVA | ——> | proc_1_name_RVA | ——> | 0,0,"LoadLibraryA",0 | |
| proc_2_name_RVA | ——> | 0,0,"GetProcAddress",0 | |||
| proc_3_name_RVA | ——> | 0,0,"GetModuleHandleA",0 | |||
| … | |||||
| IMAGE_IMPORT_ DESCRIPTOR[1] |
|||||
| ... | |||||
| IMAGE_IMPORT_ DESCRIPTOR[n] |
|||||
| IMAGE_IMPORT_DESCRIPTOR[0] | OriginalFirstThunk | ||
| TimeDateStamp | |||
| ForwarderChain | |||
| Name_RVA | ------> | "kernel32.dll",0 | |
| FirstThunk_RVA | ------> | proc_1_VA | |
| proc_2_VA | |||
| proc_3_VA | |||
| ... | |||
| IMAGE_IMPORT_DESCRIPTOR[1] | |||
| ... | |||
| IMAGE_IMPORT_DESCRIPTOR[n] | |||
You want to make a simple import table to import LoadLibrary(), and GetProcAddress() from Kernel32.dll. You need these two essential API functions to cover other API functions in run-time. The following assembly code shows how easily you can reach your solution:
0101F000: 00000000 ; OriginalFirstThunk0101F004: 00000000 ; TimeDateStamp0101F008: 00000000 ; ForwarderChain0101F00C: 0001F034 ; Name; ImageBase + 0001F034 -> 0101F034 -> "Kernel32.dll",00101F010: 0001F028 ; FirstThunk; ImageBase + 0001F028 -> 0101F0280101F014: 000000000101F018: 000000000101F01C: 000000000101F020: 000000000101F024: 000000000101F028: 0001F041 ; ImageBase + 0001F041 -> 0101F041 -> 0,0,"LoadLibraryA",00101F02C: 0001F050 ; ImageBase + 0001F050 -> 0101F050 -> 0,0,"GetProcAddress",00101F030: 000000000101F034: 'K' 'e' 'r' 'n' 'e' 'l' '3' '2' '.' 'd' 'l' 'l' 0001F041: 00 00 'L' 'o' 'a' 'd' 'L' 'i' 'b' 'r' 'a' 'r' 'y' 'A'0001F050: 00 00 'G' 'e' 't' 'P' 'r' 'o' 'c' 'A' 'd' 'd' 'r' 'e' 's' 's' 00 0000
After running…
0101F000: 00000000 ; OriginalFirstThunk0101F004: 00000000 ; TimeDateStamp0101F008: 00000000 ; ForwarderChain0101F00C: 0001F034 ; Name; ImageBase + 0001F034 -> 0101F034 -> "Kernel32.dll",00101F010: 0001F028 ; FirstThunk; ImageBase + 0001F028 -> 0101F0280101F014: 000000000101F018: 000000000101F01C: 000000000101F020: 000000000101F024: 000000000101F028: 7C801D77 ; -> Kernel32.LoadLibrary()0101F02C: 7C80AC28 ; -> Kernel32.GetProcAddress()0101F030: 000000000101F034: 'K' 'e' 'r' 'n' 'e' 'l' '3' '2' '.' 'd' 'l' 'l' 0001F041: 00 00 'L' 'o' 'a' 'd' 'L' 'i' 'b' 'r' 'a' 'r' 'y' 'A'0001F050: 00 00 'G' 'e' 't' 'P' 'r' 'o' 'c' 'A' 'd' 'd' 'r' 'e' 's' 's' 00 0000
I have prepared a class library to make every import table by using a client string table. The CITMaker class library in itmaker.h; it will build an import table by sz_IT_EXE_strings and also the relative virtual address of the import table.
static const char *sz_IT_EXE_strings[]={ "Kernel32.dll", "LoadLibraryA", "GetProcAddress", 0,, 0,};
You subsequently employ this class library to establish an import table to support DLLs and OCXs, so this is a general library to present all possible import tables easily. The next step is clarified in the following code.
CITMaker *ImportTableMaker = new CITMaker( IMPORT_TABLE_EXE );...pimage_section_header=AddNewSection( ".xxx", dwNewSectionSize );// build import table by the current virtual addressImportTableMaker->Build( pimage_section_header->VirtualAddress );memcpy( pNewSection, ImportTableMaker->pMem,ImportTableMaker->dwSize );...memcpy( image_section[image_nt_headers->FileHeader.NumberOfSections-1], pNewSection, dwNewSectionSize );...image_nt_headers->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = pimage_section_header->VirtualAddress;image_nt_headers->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = ImportTableMaker->dwSize;...delete ImportTableMaker;
The import table is copied at the beginning of the new section, and the relevant data directory is adjusted to the relative virtual address of the new section and the size of the new import table.
At this time, you can load other DLLs and find the process address of other functions by using LoadLibrary() and GetProcAddress():
lea edi, @"Kernel32.dll"//-------------------push edimov eax,offset _p_LoadLibrarycall [ebp+eax] //LoadLibrary(lpLibFileName);//-------------------mov esi,eax // esi -> hModulelea edi, @"GetModuleHandleA"//-------------------push edipush esimov eax,offset _p_GetProcAddresscall [ebp+eax] //GetModuleHandle=GetProcAddress(hModule, lpProcName);//--------------------
LoadLibrary() and GetProcAddress() aid you in your effort to reach your intention.
I want to have a complete imported function table similar in performance done in a real EXE file. If you look inside a PE file, you will discover that an API call is done by an indirection jump through the virtual address of the API function:
...0101F028: 7C801D77 ; Virtual Address of kernel32.LoadLibrary()...0101F120: JMP DWORD PTR [0101F028]...0101F230: CALL 0101F120 ; JMP to kernel32.LoadLibrary...
It makes it easy to expand the other part of your project by this performance, so you construct two data tables: the first for API virtual addresses, and the second for the JMP [XXXXXXXX].
#define __jmp_api byte_type(0xFF) byte_type(0x25)__asm{...//----------------------------------------------------------------_p_GetModuleHandle: dword_type(0xCCCCCCCC)_p_VirtualProtect: dword_type(0xCCCCCCCC)_p_GetModuleFileName: dword_type(0xCCCCCCCC)_p_CreateFile: dword_type(0xCCCCCCCC)_p_GlobalAlloc: dword_type(0xCCCCCCCC)//----------------------------------------------------------------_jmp_GetModuleHandle: __jmp_api dword_type(0xCCCCCCCC)_jmp_VirtualProtect: __jmp_api dword_type(0xCCCCCCCC)_jmp_GetModuleFileName: __jmp_api dword_type(0xCCCCCCCC)_jmp_CreateFile: __jmp_api dword_type(0xCCCCCCCC)_jmp_GlobalAlloc: __jmp_api dword_type(0xCCCCCCCC)//----------------------------------------------------------------...}
In the succeeding code, you have concluded your ambition to install a custom internal import table! (You cannot call it import table.)
... lea edi,[ebp+_p_szKernel32] lea ebx,[ebp+_p_GetModuleHandle] lea ecx,[ebp+_jmp_GetModuleHandle] add ecx,02h_api_get_lib_address_loop: push ecx push edi mov eax,offset _p_LoadLibrary call [ebp+eax] //LoadLibrary(lpLibFileName); pop ecx mov esi,eax // esi -> hModule push edi call __strlen add esp,04h add edi,eax_api_get_proc_address_loop: push ecx push edi push esi mov eax,offset _p_GetProcAddress //GetModuleHandle=GetProcAddress(hModule, lpProcName); call [ebp+eax] pop ecx mov [ebx],eax mov [ecx],ebx // JMP DWORD PTR [XXXXXXXX] add ebx,04h add ecx,06h push edi call __strlen add esp,04h add edi,eax mov al,byte ptr [edi] test al,al jnz _api_get_proc_address_loop inc edi mov al,byte ptr [edi] test al,al jnz _api_get_lib_address_loop ...
To run the program again, you should fix up the thunks of the actual import table; otherwise, you have a corrupted target PE file. Your code must correct all of the thunks the same as Table 5 to Table 6. Once more,
... mov ebx,[ebp+_p_dwImportVirtualAddress] test ebx,ebx jz _it_fixup_end mov esi,[ebp+_p_dwImageBase] add ebx,esi // dwImageBase + dwImportVirtualAddress_it_fixup_get_lib_address_loop: mov eax,[ebx+00Ch] // image_import_descriptor.Name test eax,eax jz _it_fixup_end mov ecx,[ebx+010h] // image_import_descriptor.FirstThunk add ecx,esi mov [ebp+_p_dwThunk],ecx // dwThunk mov ecx,[ebx] // image_import_descriptor.Characteristics test ecx,ecx jnz _it_fixup_table mov ecx,[ebx+010h]_it_fixup_table: add ecx,esi mov [ebp+_p_dwHintName],ecx // dwHintName add eax,esi // image_import_descriptor.Name + dwImageBase = ModuleName push eax // lpLibFileName mov eax,offset _p_LoadLibrary call [ebp+eax] // LoadLibrary(lpLibFileName); test eax,eax jz _it_fixup_end mov edi,eax_it_fixup_get_proc_address_loop: mov ecx,[ebp+_p_dwHintName] // dwHintName mov edx,[ecx] // image_thunk_data.Ordinal test edx,edx jz _it_fixup_next_module test edx,080000000h // .IF( import by ordinal ) jz _it_fixup_by_name and edx,07FFFFFFFh // get ordinal jmp _it_fixup_get_addr_it_fixup_by_name: add edx,esi // image_thunk_data.Ordinal // + dwImageBase = OrdinalName inc edx inc edx // OrdinalName.Name_it_fixup_get_addr: push edx //lpProcName push edi // hModule mov eax,offset _p_GetProcAddress call [ebp+eax] // GetProcAddress(hModule, lpProcName); mov ecx,[ebp+_p_dwThunk] // dwThunk mov [ecx],eax // correction the thunk // dwThunk => next dwThunk add dword ptr [ebp+_p_dwThunk], 004h // dwHintName => next dwHintName add dword ptr [ebp+_p_dwHintName],004h jmp _it_fixup_get_proc_address_loop_it_fixup_next_module: add ebx,014h // sizeof(IMAGE_IMPORT_DESCRIPTOR) jmp _it_fixup_get_lib_address_loop_it_fixup_end: ...
7 Support DLL and OCX
Now, you intend to include the dynamic link library (DLL) and OLE-ActiveX Control in your PE builder project. Supporting them is very easy if you pay attention to the two-time arrival into the Offset of Entry Point, the relocation table implementation, and the client import table.PE Maker: Step 4
LoadLibrary(), or an OCX is registered by using LoadLibrary() and GetProcAddress() through calling DllRegisterServer(), the first of the OEP arrival is done.hinstDLL = LoadLibrary( "test1.dll" );hinstOCX = LoadLibrary( "test1.ocx" );_DllRegisterServer = GetProcAddress( hinstOCX, "DllRegisterServer" );_DllRegisterServer(); // ocx registerDownload the pemaker4.zip source files from the end of the article.
7.1 Twice OEP approach
The Offset of Entry Point of a DLL file or an OCX file is touched by the main program atleast twice:
FreeLibrary( hinstDLL );FreeLibrary( hinstOCX );
To perform this, I have employed a trick that causes in the second time again, the instruction pointer (EIP) traveling towards the original OEP by the structured exception handler.
_main_0: pushad // save the registers context in stack call _main_1_main_1: pop ebp sub ebp,offset _main_1 // get base ebp //---------------- support dll, ocx -----------------_support_dll_0: jmp _support_dll_1 // nop; nop; // << trick // in the second time OEP jmp _support_dll_2_support_dll_1: //---------------------------------------------------- ... //---------------- support dll, ocx 1 --------------- mov edi,[ebp+_p_dwImageBase] add edi,[edi+03Ch] // edi -> IMAGE_NT_HEADERS mov ax,word ptr [edi+016h] // edi -> image_nt_headers-> // FileHeader.Characteristics test ax,IMAGE_FILE_DLL jz _support_dll_2 mov ax, 9090h // << trick mov word ptr [ebp+_support_dll_0],ax_support_dll_2: //---------------------------------------------------- ... into OEP by SEH ...
I hope you caught the trick in the preceding code, but this is not all of it. You have a problem in ImageBase, when the library has been loaded in different image bases by the main program. You should write some code to find the real image base and store it to use forward.
mov eax,[esp+24h] // the real imagebase mov ebx,[esp+30h] // oep cmp eax,ebx ja _no_dll_pe_file_0 cmp word ptr [eax],IMAGE_DOS_SIGNATURE jne _no_dll_pe_file_0 mov [ebp+_p_dwImageBase],eax_no_dll_pe_file_0:
This code finds the real image base by investigating the stack information. By using the real image base and the formal image base, you should correct all memory calls inside the image program!! Don't be afraid; it will be done simply by the relocating the table information.
To understand the relocation table better, you can take a look at Section 6.6 of the Microsoft Portable Executable and Common Object File Format Specification document. The relocation table contains many packages to relocate the information related to the virtual address inside the virtual memory image. Each package is comprised of an 8-byte header to exhibit the base virtual address and the number of data, demonstrated by the IMAGE_BASE_RELOCATION data structure.
typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock;} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
| Block[1] | VirtualAddress | |||
| SizeOfBlock | ||||
| type:4 | offset:12 | type:4 | offset:12 | |
| type:4 | offset:12 | type:4 | offset:12 | |
| type:4 | offset:12 | type:4 | offset:12 | |
| ... | ... | ... | ... | |
| type:4 | offset:12 | 00 | 00 | |
| Block[2] | VirtualAddress | |||
| SizeOfBlock | ||||
| type:4 | offset:12 | type:4 | offset:12 | |
| type:4 | offset:12 | type:4 | offset:12 | |
| type:4 | offset:12 | type:4 | offset:12 | |
| ... | ... | ... | ... | |
| type:4 | offset:12 | 00 | 00 | |
| ... |
...
|
|||
| Block[n] | VirtualAddress | |||
| SizeOfBlock | ||||
| type:4 | offset:12 | type:4 | offset:12 | |
| type:4 | offset:12 | type:4 | offset:12 | |
| type:4 | offset:12 | type:4 | offset:12 | |
| ... | ... | ... | ... | |
| type:4 | offset:12 | 00 | 00 | |
Table 7 illustrates the main idea of the relocation table. Furthermore, you can upload a DLL or an OCX file in OllyDbg to observe the relocation table, the ".reloc" section through Memory map window. By the way, you find the position of the relocation table by using the following code in your project:
DWORD dwVirtualAddress = image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]. VirtualAddress;DWORD dwSize = image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
By OllyDbg, you have the same as the following for the ".reloc" section, by using the Long Hex viewer mode. In this example, the base virtual address is 0x1000 and the size of the block is 0x184.
008E1000 : 00001000 00000184 30163000 30403028008E1010 : 30683054 308C3080 30AC309C 30D830CC008E1020 : 30E030DC 30E830E4 30F030EC 310030F4008E1030 : 3120310D 315F3150 31A431A0 31C031A8008E1040 : 31D031CC 31F431EC 31FC31F8 32043200008E1050 : 320C3208 32143210 324C322C 32583254008E1060 : 3260325C 32683264 3270326C 32B03274
It relocates the data in the subsequent virtual addresses:
0x1000 + 0x0000 = 0x10000x1000 + 0x0016 = 0x10160x1000 + 0x0028 = 0x10280x1000 + 0x0040 = 0x10400x1000 + 0x0054 = 0x1054...
Each package performs the relocation by using consecutive 4 bytes form its internal information. The first byte refers to the type of relocation and the next three bytes are the offset that must be used with the base virtual address and the image base to correct the image information.
| type | offset | ||
| 03 | 00 | 00 | 00 |
The type can be one of the following values:
By relocation, some values inside the virtual memory are corrected according to the current image base by the ".reloc" section packages.
| delta_ImageBase = current_ImageBase - image_nt_headers->OptionalHeader.ImageBase |
mem[ current_ImageBase + 0x1000 ] = mem[ current_ImageBase + 0x1000 ] + delta_ImageBase ;mem[ current_ImageBase + 0x1016 ] = mem[ current_ImageBase + 0x1016 ] + delta_ImageBase ;mem[ current_ImageBase + 0x1028 ] = mem[ current_ImageBase + 0x1028 ] + delta_ImageBase ;mem[ current_ImageBase + 0x1040 ] = mem[ current_ImageBase + 0x1040 ] + delta_ImageBase ;mem[ current_ImageBase + 0x1054 ] = mem[ current_ImageBase + 0x1054 ] + delta_ImageBase ;...
I have employed the following code from Morphine packer to implement the relocation.
..._reloc_fixup: mov eax,[ebp+_p_dwImageBase] mov edx,eax mov ebx,eax add ebx,[ebx+3Ch] // edi -> IMAGE_NT_HEADERS // edx ->image_nt_headers->OptionalHeader.ImageBase mov ebx,[ebx+034h] sub edx,ebx // edx -> reloc_correction // delta_ImageBase je _reloc_fixup_end mov ebx,[ebp+_p_dwRelocationVirtualAddress] test ebx,ebx jz _reloc_fixup_end add ebx,eax_reloc_fixup_block: mov eax,[ebx+004h] //ImageBaseRelocation.SizeOfBlock test eax,eax jz _reloc_fixup_end lea ecx,[eax-008h] shr ecx,001h lea edi,[ebx+008h]_reloc_fixup_do_entry: movzx eax,word ptr [edi]//Entry push edx mov edx,eax shr eax,00Ch //Type = Entry >> 12 mov esi,[ebp+_p_dwImageBase]//ImageBase and dx,00FFFh add esi,[ebx] add esi,edx pop edx_reloc_fixup_HIGH: // IMAGE_REL_BASED_HIGH dec eax jnz _reloc_fixup_LOW mov eax,edx shr eax,010h //HIWORD(Delta) jmp _reloc_fixup_LOW_fixup_reloc_fixup_LOW: // IMAGE_REL_BASED_LOW dec eax jnz _reloc_fixup_HIGHLOW movzx eax,dx //LOWORD(Delta)_reloc_fixup_LOW_fixup: add word ptr [esi],ax// mem[x] = mem[x] + delta_ImageBase jmp _reloc_fixup_next_entry_reloc_fixup_HIGHLOW: // IMAGE_REL_BASED_HIGHLOW dec eax jnz _reloc_fixup_next_entry add [esi],edx // mem[x] = mem[x] + delta_ImageBase_reloc_fixup_next_entry: inc edi inc edi //Entry++ loop _reloc_fixup_do_entry_reloc_fixup_next_base: add ebx,[ebx+004h] jmp _reloc_fixup_block_reloc_fixup_end: ...
To support the OLE-ActiveX Control registration, you should present an appropriate import table to your target OCX and DLL file. Therefore, I have established an import table by the following string:
const char *sz_IT_OCX_strings[]={ "Kernel32.dll", "LoadLibraryA", "GetProcAddress", "GetModuleHandleA", 0, "User32.dll", "GetKeyboardType", "WindowFromPoint", 0, "AdvApi32.dll", "RegQueryValueExA", "RegSetValueExA", "StartServiceA", 0, "Oleaut32.dll", "SysFreeString", "CreateErrorInfo", "SafeArrayPtrOfIndex", 0, "Gdi32.dll", "UnrealizeObject", 0, "Ole32.dll", "CreateStreamOnHGlobal", "IsEqualGUID", 0, "ComCtl32.dll", "ImageList_SetIconSize", 0, 0,};
Without these API functions, the library can not be loaded, and moreover the DllregisterServer() and DllUregisterServer() will not operate. In CPECryptor::CryptFile, I have distinguished between EXE files and DLL files in the initialization of the new import table object during creation:
if(( image_nt_headers->FileHeader.Characteristics & IMAGE_FILE_DLL ) == IMAGE_FILE_DLL ){ ImportTableMaker = new CITMaker( IMPORT_TABLE_OCX );}else{ ImportTableMaker = new CITMaker( IMPORT_TABLE_EXE );}
By using Thread Local Storage (TLS), a program is able to execute a multithreaded process, This performance mostly is used by Borland linkers: Delphi and C++ Builder. When you pack a PE file, you should take care to keep the TLS clean; otherwise, your packer will not support Borland Delphi and C++ Builder linked EXE files. To comprehend TLS, I refer you to Section 6.7 of the Microsoft Portable Executable and Common Object File Format Specification document. You can observe the TLS structure by IMAGE_TLS_DIRECTORY32 in winnt.h.
typedef struct _IMAGE_TLS_DIRECTORY32 { DWORD StartAddressOfRawData; DWORD EndAddressOfRawData; DWORD AddressOfIndex; DWORD AddressOfCallBacks; DWORD SizeOfZeroFill; DWORD Characteristics;} IMAGE_TLS_DIRECTORY32, * PIMAGE_TLS_DIRECTORY32;
MessageBox() from user32.dll.
To keep the TLS directory safe, I have copied it in a special place inside the loader:
..._tls_dwStartAddressOfRawData: dword_type(0xCCCCCCCC)_tls_dwEndAddressOfRawData: dword_type(0xCCCCCCCC)_tls_dwAddressOfIndex: dword_type(0xCCCCCCCC)_tls_dwAddressOfCallBacks: dword_type(0xCCCCCCCC)_tls_dwSizeOfZeroFill: dword_type(0xCCCCCCCC)_tls_dwCharacteristics: dword_type(0xCCCCCCCC)...
It is necessary to correct the TLS directory entry in the Optional Header:
if(image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]. VirtualAddress!=0){ memcpy(&pDataTable->image_tls_directory, image_tls_directory, sizeof(IMAGE_TLS_DIRECTORY32)); dwOffset=DWORD(pData1)-DWORD(pNewSection); dwOffset+=sizeof(t_DATA_1)-sizeof(IMAGE_TLS_DIRECTORY32); image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]. VirtualAddress=dwVirtualAddress + dwOffset;}
You are ready to place your code inside the new section. Your code is a "Hello World!" message by
...push MB_OK | MB_ICONINFORMATIONlea eax,[ebp+_p_szCaption]push eaxlea eax,[ebp+_p_szText]push eaxpush NULLcall _jmp_MessageBox// MessageBox(NULL, szText, szCaption, MB_OK | MB_ICONINFORMATION) ;...
Download the pemaker5.zip source files from the end of the article.
By reading this article, you have perceived how easily you can inject code to a portable executable file. You can complete the code by using the source of other packers, create a packer in the same way as Yoda's Protector, and make your packer undetectable by mixing up with Morphine source code. I hope that you have enjoyed this brief discussion of one part of the reverse engineering field. See you again in the next discussion!
EXCEPTION_POINTERS, you have access to the pointer of ContextRecord. The ContextRecord has the CONTEXT data structure, as seen in Table 4. This is the thread context during the exception time. When you ignore the exception by EXCEPTION_CONTINUE_SEARCH (0), the instruction pointer, as well as the context, will be set to ContextRecord to return to the previous condition. Therefore, if you change the Eip of the Win32 Thread Context to the Original Offset of Entry Point, it will come clearly into OEP.Full Size Image)
Tags: class, debug, html, linux, lua, php, ror, server, windows
转至: http://hi.baidu.com/%CC%EC%CD%E2%C3%AB%B3%E6/blog
【前言】
本人初学脱壳,以前只会用自动脱壳器,手动只脱过upx的壳,实在是菜鸟一个。日前有幸拿到一个共享软件,用peid一查,Armadillo 1.xx – 2.xx -> Silicon Realms Toolworks [Overlay],俺出生牛犊不怕虎,见壳就有脱的冲动,用od载入一看,傻了眼,这壳和upx的完全不同,入口点和一般程序差不多嘛。遂知道自己水平不济,驱猫上看雪拜读各位前辈的文章。无奈本人水平实在有限,看了大半天还是没理清楚过程。想起马gg曾经说过,实践是检验真理的唯一标准,于是操起工具对该软件大卸七块。经过2天的仔细研习,终于悟得精要,成功把壳干掉了。鉴于感觉入门门槛确实有点高,因此特写此文以帮助菜鸟们迈过刀山火海,飞向光明之巅:D
【术语解释】
为什么我要写这一段呢?因为我发现看高手们的脱文,最难逾越的一关是术语。高手们脱文中的各种说法,并不能马上就明白过来究竟是什么东西。因此,希望在这里把Armadillo脱文经常遇到的几个术语稍作解释。本人水平有限,如果解释有误请指出。
[OEP] 这个是Original Entry Point的缩写,中文字面意思就是程序的原入口地址。为什么叫“原”呢?通常加壳软件会把原来的程序编码存放,以防止静态反汇编分析,并在执行前先运行一段解码的程序。所以,加壳后的程序,其入口地址是直接指向解码部分的代码,而非原来的程序入口。我们脱壳所要做的工作,就是还原出原来的程序,并且每次执行时直接从原入口地址开始执行(而不需要再运行用于解码的“壳”),因此需要得知原入口地址是什么,即OEP。
[Armadillo] 传说中的猛壳,因为拼写太长,也有人缩写为arm壳。它使用多种加密手段以防止脱壳,比如检测debugger、修改IAT、还有高级版本的stolen byte和双线程解码。
[IAT] Import Address Table的缩写,也有叫输入表,引入表。它用来保存程序用到的API函数的入口地址。
[RVA] Relative Virtual Address,相对虚拟地址。win32系统会把进程读入到内存中执行,所以存在着内存地址和文件偏移的转换关系。PE文件头里面会有一个内存基址base,原来在文件中偏移为x的内容,在内存里面的偏移就变成base+x。为了区分这两种地址偏移,通常叫文件中的偏移为RVA
[magic jump](一般破文是按10多次或者20多次F9,就来到magicjump。我根本不知道怎么去判断一个新软件的magicjump在哪里,也不知道那个次数是怎么得来的,不怕,下面我会教一种我认为比较好的方法)其实所谓magic jump,是指跳过改写IAT的代码段。Armadillo的解壳过程有一个特点,就是会改写IAT。(这里我用的是“改写”而不是某些文章中的“破坏”是有原因的。曾经我在这里也困惑过,破坏带有不可恢复的意思,事实上IAT对应的地址并没有完全破坏,只是被改写成更难辨认的形式。这里举一个具体例子)
00E6E0E1 mov edx,[EA01B8] // ~= kernel32.dll/00D4/FindNextFileA
00E6E0E7 add edx,64
00E6E0EA call edx
00E6E0EC mov edx,[EA0144] // ~= kernel32.dll/016F/GetModuleFileNameW
00E6E0F2 add edx,64
00E6E0F5 mov ecx,5
这个是被改写后的IAT指向的一端程序段。里面实际工作是作还原工作。先取出edx(这里对应一个假API),然后加上64偏移才得到真正的API,再进行函数调用。FindNextFileA后面的偏移64是GetTickCount,GetModuleFileNameW后面是GetModuleHandleA,所以上面的代码相当于
call kernel32.GetTickCount
nop
…
call kernel32.GetModuleHandleA
这样可以使得手动脱壳过程中把IAT表弄坏(因为无法识别出正确的API),但是加壳程序却可以正常运行。是不是很狡猾?解决办法也简单,在脱壳的过程中避开执行改写IAT表的代码段,只需要修改一条指令,这条指令,正是magic jump!
能够坚持看到这里是否已经有点烦闷了?基本理论就这么多了。准备好工具了吗?让我们马上开始。
【工具】OD、LordPE、ImportREC
【过程】
【Action 1】 明察暗访OEP
OD载入程序,用插件隐藏OD,忽略所有异常,alt_m查看内存映射,在00401000处下内存读取断点,F9
程序停下来了,看到没有,熟悉的开头。没有看到?肯定你遇到异常了,shift_F9试试?
005E14E4 55 push ebp
005E14E5 8BEC mov ebp,esp
005E14E7 83C4 E4 add esp,-1C
我们找到OEP了,马上记下吧。Action 1目标完成:OEP=001E14E4
等等,是不是打错字了?不是005E14E4吗?还记得RVA吗?通常来说,windows会把程序读到从00400000开始的连续内存空间(当然也不是一成不变,只是通常碰到的情况都是这样),也就是说你看到的OEP 005E14E4是内存的地址,它的RVA是001E14E4。明白了吗?
【Action 2】攻下桥头堡
运行到OEP预示着解码阶段的完成了。所以理论上现在内存中的是已解码的程序。先不要动OD,保持在OEP入口。运行LordPE,选刚刚运行的程序的线程,full dump,Action 2完成!
先别对着dump出来的exe笑啊,如果现在那个是最终的脱壳结果,Armadillo就不叫猛壳了,我刚刚写的一堆理论也就白费劲。喝口水再继续吧。下面才到重点。
【Action 3】扫清地雷阵
OD没有关掉吧?恩,别动它,继续保持。运行ImportREC,选择程序进程,在下面的IAT Infos needed填入刚才拿到的OEP。AutoSearch,看到RVA框变了,那个就是IAT的地址和大小了。我这里找到的数值是001ED240
回到OD,d 5ed240(还记得刚刚说过的内存偏移的换算关系吗?),看到什么了?那个就是IAT呀。记下它的样子。然后分别在第一个项目和最后一个项目下硬件写入断点。(为什么用硬件捏?因为它不影响速度,而且重新运行的时候不会没掉,呵呵)
下面重新运行吧。F9,碰到硬件断点了。还记得IAT的样子吗?继续F9,直到第一个条目和你刚刚记下的一样。现在按page up,一直向上找GetModuleHandleA
00E86AB1 6A 00 push 0
00E86AB3 FF15 D400E900 call dword ptr ds:[E900D4] ; kernel32.GetModuleHandleA
00E86AB9 3985 90C4FFFF cmp dword ptr ss:[ebp-3B70],eax
00E86ABF 75 0F jnz short 00E86AD0
00E86AC1 C785 8CC4FFFF 8>mov dword ptr ss:[ebp-3B74],0E95180
00E86ACB E9 C4000000 jmp 00E86B94
00E86AD0 83A5 68C2FFFF 0>and dword ptr ss:[ebp-3D98],0
00E86AD7 C785 64C2FFFF C>mov dword ptr ss:[ebp-3D9C],0E957C0
00E86AE1 EB 1C jmp short 00E86AFF
00E86AE3 8B85 64C2FFFF mov eax,dword ptr ss:[ebp-3D9C]
00E86AE9 83C0 0C add eax,0C
00E86AEC 8985 64C2FFFF mov dword ptr ss:[ebp-3D9C],eax
00E86AF2 8B85 68C2FFFF mov eax,dword ptr ss:[ebp-3D98]
00E86AF8 40 inc eax
00E86AF9 8985 68C2FFFF mov dword ptr ss:[ebp-3D98],eax
00E86AFF 8B85 64C2FFFF mov eax,dword ptr ss:[ebp-3D9C]
00E86B05 8338 00 cmp dword ptr ds:[eax],0
00E86B08 0F84 86000000 je 00E86B94
00E86B0E 8B85 64C2FFFF mov eax,dword ptr ss:[ebp-3D9C]
00E86B14 8B40 08 mov eax,dword ptr ds:[eax+8]
00E86B17 83E0 01 and eax,1
00E86B1A 85C0 test eax,eax
00E86B1C 74 25 je short 00E86B43
00E86B1E A1 2800EA00 mov eax,dword ptr ds:[EA0028]
00E86B23 8B0D 2800EA00 mov ecx,dword ptr ds:[EA0028]
00E86B29 8B40 20 mov eax,dword ptr ds:[eax+20]
00E86B2C 3341 40 xor eax,dword ptr ds:[ecx+40]
00E86B2F 8B0D 2800EA00 mov ecx,dword ptr ds:[EA0028]
00E86B35 3341 28 xor eax,dword ptr ds:[ecx+28]
00E86B38 25 80000000 and eax,80
00E86B3D 85C0 test eax,eax
00E86B3F 74 02 je short 00E86B43
00E86B41 ^ EB A0 jmp short 00E86AE3
00E86B43 8B85 68C2FFFF mov eax,dword ptr ss:[ebp-3D98]
00E86B49 8B0D 74B7E900 mov ecx,dword ptr ds:[E9B774]
00E86B4F 8B15 2800EA00 mov edx,dword ptr ds:[EA0028]
00E86B55 8B0481 mov eax,dword ptr ds:[ecx+eax*4]
00E86B58 3342 24 xor eax,dword ptr ds:[edx+24]
00E86B5B 8B0D 2800EA00 mov ecx,dword ptr ds:[EA0028]
00E86B61 3341 28 xor eax,dword ptr ds:[ecx+28]
00E86B64 8B0D 2800EA00 mov ecx,dword ptr ds:[EA0028]
00E86B6A 3341 44 xor eax,dword ptr ds:[ecx+44]
00E86B6D 8B0D 2800EA00 mov ecx,dword ptr ds:[EA0028]
00E86B73 3341 6C xor eax,dword ptr ds:[ecx+6C]
00E86B76 3985 90C4FFFF cmp dword ptr ss:[ebp-3B70],eax
你看到的这段可能会跟我给出的有点差异,不过很好认,会有一段mov和xor交错出现的地方,并且mov语句是完全相同的。这里是00E86B1E-00E86B76
向上找可以跳过这段代码的转跳语句,这里是
00E86B08 0F84 86000000 je 00E86B94 ;magic jump!!!!!!!!
我看到其他教程,这句是je short的,所以可能是Armadillo版本不同。不过道理都是一样,避开对IAT表的改写。
把这句改成jmp,再按F9,遇到第二个硬件断点,这时IAT转换完成。这个可是没有被做过手脚的完整IAT啊~
哦,这里别忘了,回到刚刚修改的jmp那里,改回je啊。不然后面的解码会出错导致程序异常终止了。(至少我脱的这个壳会这样。好像只有很少的脱文提到要恢复指令,反正恢复也没坏,多做一步吧,不然异常了可能还要重新来过)
再bp 005e14e4,在OEP处下断,F9,运行到OEP
【Action 4】长驱直入,胜利会师
拿出ImportREC出来吧,重新选一下进程,autosearch,步骤应该都熟悉了。这次可以点Get Imports了。如果还有unresolved pointer,就点Show Invalid,Trace Level1试试,剩下的用Cut thunk全部干掉。然后fix dump,选择Action 2 dump出来的exe,应该就多出来一个文件名后面多带一个_的exe文件。这个是脱壳后独立运行的exe啊。试试能不能运行,不行的话,调整一下ImportREC的参数再试试,有些软件不能用Add new section的。把auto search的结果填到New Import Infos,去掉Add new section再试一遍。
【Action 5】清除残余势力
脱壳出来的程序很大,因为里面包含了很多已经没用的解码程序段。为了做到完美脱壳,我们可以把没用的代码清理掉。主要过程可以参考《脱壳后软件减肥大法》http://www.pediy.com/bbshtml/BBS6/pediy6313.htm,这里就不赘述了。不过调整.idata保证VA连续的那一步我不是按它的,不需要手动调整,直接用LordPE的rebuild PE就可以了,有现成工具干吗还要造轮子了?
【后记】
当我看到脱壳出来的程序正常运行时,有一种兴奋的感觉。也许我是脱壳菜鸟,这些对高手们确实不值得一提。可是正如fly斑竹所说,我知道自己进阶了。把过程和大家共享,希望大家可以跨过入门的门槛,共同进步。
Tags: blog, debug, html, windows
在windows 9x、NT、2000下,所有的可执行文件都是基于Microsoft设计的一种新的文件格式Portable Executable File Format(可移植的执行体),即PE格式。有一些时候,我们需要对这些可执行文件进行修改,下面文字试图详细的描述PE文件的格式及对PE格式文件的修改。
1、PE文件框架构成
DOS MZ header
DOS stub
PE header
Section table
Section 1
Section 2
Section …
Section n
上表是PE文件结构的总体层次分布。所有 PE文件(甚至32位的 DLLs) 必须以一个简单的 DOS MZ header 开始,在偏移0处有DOS下可执行文件的“MZ标志”,有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随 MZ header 之后的 DOS stub。DOS stub实际上是个有效的EXE,在不支持 PE文件格式的操作系统中,它将简单显示一个错误提示,类似于字符串 " This program cannot run in DOS mode " 或者程序员可根据自己的意图实现完整的 DOS代码。通常DOS stub由汇编器/编译器自动生成,对我们的用处不是很大,它简单调用中断21h服务9来显示字符串"This program cannot run in DOS mode"。
紧接着 DOS stub 的是 PE header。 PE header 是PE相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多PE装载器用到的重要域。可执行文件在支持PE文件结构的操作系统中执行时,PE装载器将从 DOS MZ header的偏移3CH处找到 PE header 的起始偏移量。因而跳过了 DOS stub 直接定位到真正的文件头 PE header。
PE文件的真正内容划分成块,称之为sections(节)。每节是一块拥有共同属性的数据,比如“.text”节等,那么,每一节的内容都是什么呢?实际上PE格式的文件把具有相同属性的内容放入同一个节中,而不必关心类似“.text”、“.data”的命名,其命名只是为了便于识别,所有,我们如果对PE格式的文件进行修改,理论上讲可以写入任何一个节内,并调整此节的属性就可以了。
PE header 接下来的数组结构 section table(节表)。 每个结构包含对应节的属性、文件偏移量、虚拟偏移量等。如果PE文件里有5个节,那么此结构数组内就有5个成员。
以上就是PE文件格式的物理分布,下面将总结一下装载一PE文件的主要步骤:
1、 PE文件被执行,PE装载器检查 DOS MZ header 里的 PE header 偏移量。如果找到,则跳转到 PE header。
2、PE装载器检查 PE header 的有效性。如果有效,就跳转到PE header的尾部。
3、紧跟 PE header 的是节表。PE装载器读取其中的节信息,并采用文件映射方法将这些节映射到内存,同时付上节表里指定的节属性。
4、PE文件映射入内存后,PE装载器将处理PE文件中类似 import table(引入表)逻辑部分。
上述步骤是一些前辈分析的结果简述。
2、PE文件头概述
我们可以在winnt.h这个文件中找到关于PE文件头的定义:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
//PE文件头标志 :“PE\0\0”。在开始DOS header的偏移3CH处所指向的地址开始
IMAGE_FILE_HEADER FileHeader; //PE文件物理分布的信息
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //PE文件逻辑分布的信息
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //该文件运行所需要的CPU,对于Intel平台是14Ch
WORD NumberOfSections; //文件的节数目
DWORD TimeDateStamp; //文件创建日期和时间
DWORD PointerToSymbolTable; //用于调试
DWORD NumberOfSymbols; //符号表中符号个数
WORD SizeOfOptionalHeader; //OptionalHeader 结构大小
WORD Characteristics; //文件信息标记,区分文件是exe还是dll
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; //标志字(总是010bh)
BYTE MajorLinkerVersion; //连接器版本号
BYTE MinorLinkerVersion; //
DWORD SizeOfCode; //代码段大小
DWORD SizeOfInitializedData; //已初始化数据块大小
DWORD SizeOfUninitializedData; //未初始化数据块大小
DWORD AddressOfEntryPoint; //PE装载器准备运行的PE文件的第一个指令的RVA,若要改变整个执行的流程,可以将该值指定到新的RVA,这样新RVA处的指令首先被执行。(许多文章都有介绍RVA,请去了解)
DWORD BaseOfCode; //代码段起始RVA
DWORD BaseOfData; //数据段起始RVA
DWORD ImageBase; //PE文件的装载地址
DWORD SectionAlignment; //块对齐
DWORD FileAlignment; //文件块对齐
WORD MajorOperatingSystemVersion;//所需操作系统版本号
WORD MinorOperatingSystemVersion;//
WORD MajorImageVersion; //用户自定义版本号
WORD MinorImageVersion; //
WORD MajorSubsystemVersion; //win32子系统版本。若PE文件是专门为Win32设计的
WORD MinorSubsystemVersion; //该子系统版本必定是4.0否则对话框不会有3维立体感
DWORD Win32VersionValue; //保留
DWORD SizeOfImage; //内存中整个PE映像体的尺寸
DWORD SizeOfHeaders; //所有头+节表的大小
DWORD CheckSum; //校验和
WORD Subsystem; //NT用来识别PE文件属于哪个子系统
WORD DllCharacteristics; //
DWORD SizeOfStackReserve; //
DWORD SizeOfStackCommit; //
DWORD SizeOfHeapReserve; //
DWORD SizeOfHeapCommit; //
DWORD LoaderFlags; //
DWORD NumberOfRvaAndSizes; //
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
//IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA,比如引入地址表等
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; //表的RVA地址
DWORD Size; //大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
PE文件头后是节表,在winnt.h下如下定义
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节表名称,如“.text”
union {
DWORD PhysicalAddress; //物理地址
DWORD VirtualSize; //真实长度
} Misc;
DWORD VirtualAddress; //RVA
DWORD SizeOfRawData; //物理长度
DWORD PointerToRawData; //节基于文件的偏移量
DWORD PointerToRelocations; //重定位的偏移
DWORD PointerToLinenumbers; //行号表的偏移
WORD NumberOfRelocations; //重定位项数目
WORD NumberOfLinenumbers; //行号表的数目
DWORD Characteristics; //节属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
以上结构就是在winnt.h中关于PE文件头的定义,如何我们用C/C++来进行PE可执行文件操作,就要用到上面的所有结构,它详细的描述了PE文件头的结构。
3、修改PE可执行文件
现在让我们把一段代码写入任何一个PE格式的可执行文件,代码如下:
– test.asm –
.386p
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.code
start:
INVOKE MessageBoxA,0,0,0,MB_ICONINFORMATION or MB_OK
ret
end start
以上代码只显示一个MessageBox框,编译后得到二进制代码如下:
unsigned char writeline[18]={
0×6a,0×40,0×6a,0×0,0×6a,0×0,0×6a,0×0,0xe8,0×01,0×0,0×0,0×0,0xe9,0×0,0×0,0×0,0×0
};
好,现在让我们看看该把这些代码写到那。现在用Tdump.exe显示一个PE格式得可执行文件信息,可以发现如下描述:
Object table:
# Name VirtSize RVA PhysSize Phys off Flags
– ——– ——– ——– ——– ——– ——–
01 .text 0000CCC0 00001000 0000CE00 00000600 60000020 [CER]
02 .data 00004628 0000E000 00002C00 0000D400 C0000040 [IRW]
03 .rsrc 000003C8 00013000 00000400 00010000 40000040 [IR]
Key to section flags:
C – contains code
E – executable
I – contains initialized data
R – readable
W – writeable
上面描述此文件中存在3个段及每个段得信息,实际上我们的代码可以写入任何一个段,这里我选择“.text”段。
用如下代码得到一个PE格式可执行文件的头信息:
//writePE.cpp
#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <time.h>
#include <SYS\STAT.H>
unsigned char writeline[18]={
0×6a,0×40,0×6a,0×0,0×6a,0×0,0×6a,0×0,0xe8,0×01,0×0,0×0,0×0,0xe9,0×0,0×0,0×0,0×0
};
DWORD space;
DWORD entryaddress;
DWORD entrywrite;
DWORD progRAV;
DWORD oldentryaddress;
DWORD newentryaddress;
DWORD codeoffset;
DWORD peaddress;
DWORD flagaddress;
DWORD flags;
DWORD virtsize;
DWORD physaddress;
DWORD physsize;
DWORD MessageBoxAadaddress;
int main(int argc,char * * argv)
{
HANDLE hFile, hMapping;
void *basepointer;
FILETIME * Createtime;
FILETIME * Accesstime;
FILETIME * Writetime;
Createtime = new FILETIME;
Accesstime = new FILETIME;
Writetime = new FILETIME;
if ((hFile = CreateFile(argv[1], GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)//打开要修改的文件
{
puts("(could not open)");
return EXIT_FAILURE;
}
if(!GetFileTime(hFile,Createtime,Accesstime,Writetime))
{
printf("\nerror getfiletime: %d\n",GetLastError());
}
//得到要修改文件的创建、修改等时间
if (!(hMapping = CreateFileMapping(hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
{
puts("(mapping failed)");
CloseHandle(hFile);
return EXIT_FAILURE;
}
if (!(basepointer = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)))
{
puts("(view failed)");
CloseHandle(hMapping);
CloseHandle(hFile);
return EXIT_FAILURE;
}
//把文件头映象存入baseointer
CloseHandle(hMapping);
CloseHandle(hFile);
map_exe(basepointer);//得到相关地址
UnmapViewOfFile(basepointer);
printaddress();
printf("\n\n");
if(space<50)
{
printf("\n空隙太小,数据不能写入.\n");
}
else
{
writefile();//写文件
}
if ((hFile = CreateFile(argv[1], GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)
{
puts("(could not open)");
return EXIT_FAILURE;
}
if(!SetFileTime(hFile,Createtime,Accesstime,Writetime))
{
printf("error settime : %d\n",GetLastError());
}
//恢复修改后文件的建立时间等
delete Createtime;
delete Accesstime;
delete Writetime;
CloseHandle(hFile);
return 0;
}
void map_exe(const void *base)
{
IMAGE_DOS_HEADER * dos_head;
dos_head =(IMAGE_DOS_HEADER *)base;
#include <pshpack1.h>
typedef struct PE_HEADER_MAP
{
DWORD signature;
IMAGE_FILE_HEADER _head;
IMAGE_OPTIONAL_HEADER opt_head;
IMAGE_SECTION_HEADER section_header[];
} peHeader;
#include <poppack.h>
if (dos_head->e_magic != IMAGE_DOS_SIGNATURE)
{
puts("unknown type of file");
return;
}
peHeader * header;
header = (peHeader *)((char *)dos_head + dos_head->e_lfanew);//得到PE文件头
if (IsBadReadPtr(header, sizeof(*header))
{
puts("(no PE header, probably DOS executable)");
return;
}
DWORD mods;
char tmpstr[4]={0};
DWORD tmpaddress;
DWORD tmpaddress1;
if(strstr((const char *)header->section_header[0].Name,".text")!=NULL)
{
virtsize=header->section_header[0].Misc.VirtualSize;
//此段的真实长度
physaddress=header->section_header[0].PointerToRawData;
//此段的物理偏移
physsize=header->section_header[0].SizeOfRawData;
//此段的物理长度
peaddress=dos_head->e_lfanew;
//得到PE文件头的开始偏移
peHeader peH;
tmpaddress=(unsigned long )&peH;
//得到结构的偏移
tmpaddress1=(unsigned long )&(peH.section_header[0].Characteristics);
//得到变量的偏移
flagaddress=tmpaddress1-tmpaddress+2;
//得到属性的相对偏移
flags=0×8000;
//一般情况下,“.text”段是不可读写的,如果我们要把数据写入这个段需要改变其属性,实际上这个程序并没有把数据写入“.text”段,所以并不需要更改,但如果你实现复杂的功能,肯定需要数据,肯定需要更改这个值,
space=physsize-virtsize;
//得到代码段的可用空间,用以判断可不可以写入我们的代码
//用此段的物理长度减去此段的真实长度就可以得到
progRAV=header->opt_head.ImageBase;
//得到程序的装载地址,一般为400000
codeoffset=header->opt_head.BaseOfCode-physaddress;
//得到代码偏移,用代码段起始RVA减去此段的物理偏移
//应为程序的入口计算公式是一个相对的偏移地址,计算公式为:
//代码的写入地址+codeoffset
entrywrite=header->section_header[0].PointerToRawData+header->section_header[0].Misc.VirtualSize;
//代码写入的物理偏移
mods=entrywrite%16;
//对齐边界
if(mods!=0)
{
entrywrite+=(16-mods);
}
oldentryaddress=header->opt_head.AddressOfEntryPoint;
//保存旧的程序入口地址
newentryaddress=entrywrite+codeoffset;
//计算新的程序入口地址
return;
}
void printaddress()
{
HINSTANCE gLibMsg=NULL;
DWORD funaddress;
gLibMsg=LoadLibrary("user32.dll");
funaddress=(DWORD)GetProcAddress(gLibMsg,"MessageBoxA");
MessageBoxAadaddress=funaddress;
gLibAMsg=LoadLibrary("kernel32.dll");
//得到MessageBox在内存中的地址,以便我们使用
}
void writefile()
{
int ret;
long retf;
DWORD address;
int tmp;
unsigned char waddress[4]={0};
ret=_open(filename,_O_RDWR | _O_CREAT | _O_BINARY,_S_IREAD | _S_IWRITE);
if(!ret)
{
printf("error open\n");
return;
}
retf=_lseek(ret,(long)peaddress+40,SEEK_SET);
//程序的入口地址在PE文件头开始的40处
if(retf==-1)
{
printf("error seek\n");
return;
}
address=newentryaddress;
tmp=address>>24;
waddress[3]=tmp;
tmp=address<<8;
tmp=tmp>>24;
waddress[2]=tmp;
tmp=address<<16;
tmp=tmp>>24;
waddress[1]=tmp;
tmp=address<<24;
tmp=tmp>>24;
waddress[0]=tmp;
retf=_write(ret,waddress,4);
//把新的入口地址写入文件
if(retf==-1)
{
printf("error write: %d\n",GetLastError());
return;
}
retf=_lseek(ret,(long)entrywrite,SEEK_SET);
if(retf==-1)
{
printf("error seek\n");
return;
}
retf=_write(ret,writeline,18);
if(retf==-1)
{
printf("error write: %d\n",GetLastError());
return;
}
//把writeline写入我们计算出的空间
retf=_lseek(ret,(long)entrywrite+9,SEEK_SET);
//更改MessageBox函数地址,它的二进制代码在writeline[10]处
if(retf==-1)
{
printf("error seek\n");
return;
}
address=MessageBoxAadaddress-(progRAV+newentryaddress+9+4);
//重新计算MessageBox函数的地址,MessageBox函数的原地址减去程序的装载地址加上新的入口地址加9(它的二进制代码相对偏移)加上4(地址长度)
tmp=address>>24;
waddress[3]=tmp;
tmp=address<<8;
tmp=tmp>>24;
waddress[2]=tmp;
tmp=address<<16;
tmp=tmp>>24;
waddress[1]=tmp;
tmp=address<<24;
tmp=tmp>>24;
waddress[0]=tmp;
retf=_write(ret,waddress,4);
//写入重新计算的MessageBox地址
if(retf==-1)
{
printf("error write: %d\n",GetLastError());
return;
}
retf=_lseek(ret,(long)entrywrite+14,SEEK_SET);
//更改返回地址,用jpm返回原程序入口地址,其它的二进制代码在writeline[15]处
if(retf==-1)
{
printf("error seek\n");
return;
}
address=0-(newentryaddress-oldentryaddress+4+15);
//返回地址计算的方法是新的入口地址减去老的入口地址加4(地址长度)加15(二进制代码相对偏移)后取反
tmp=address>>24;
waddress[3]=tmp;
tmp=address<<8;
tmp=tmp>>24;
waddress[2]=tmp;
tmp=address<<16;
tmp=tmp>>24;
waddress[1]=tmp;
tmp=address<<24;
tmp=tmp>>24;
waddress[0]=tmp;
retf=_write(ret,waddress,4);
//写入返回地址
if(retf==-1)
{
printf("error write: %d\n",GetLastError());
return;
}
_close(ret);
printf("\nall done…\n");
return;
}
//end
由于在PE格式的文件中,所有的地址都使用RVA地址,所以一些函数调用和返回地址都要经过计算才可以得到,以上是我在实践中的心得,如果你有更好的办法,真心的希望你能告诉我。
如果存在错误,请告诉我,以免误导看这篇文章的人。
写的较乱,请原谅。
ilsy@netguard.com.cn
内容
实现和IE浏览器交互的几种方法的介绍
—- 1.引言
—- 如何实现对IE浏览器中对象的操作是一个很有实际意义问题,通过和IE绑定的DLL我们可以记录IE浏览过的网页的顺序,分析用户的使用行为和模式。我们可以对网页的内容进行过滤和翻译,可以自动填写网页中经常需要用户填写的Form内容等等,我们所有的例子代码都是通过VC来表示的,采用的原理是通过和IE对象的接口的交互来实现对IE的访问。实际上是采用COM的技术,我们知道COM是和语言无关的一种二进制对象交互的模式,所以实际上我们下面所描述的内容都可以用其他的语言来实现,比如VB,DELPHI,C++ Builder等等。
—- 2.IE实例遍历实现
—- 首先我们来看系统是如何知道当前有多少个IE的实例在运行。
—- 我们知道在Windows体系结构下,一个应用程序可以通过操作系统的运行对象表来和这些应用的实例进行交互。但是IE当前的实现机制是不在运行对象表中进行注册,所以需要采用其他的方法。我们知道可以通过ShellWindows集合来代表属于shell的当前打开的窗口的集合,而IE就是属于shell的一个应用程序。
—- 下面我们描述一下用VC实现对当前 IE实例的进行遍历的方法。IShellWindows是关于系统shell的一个接口,我们可以定义一个如下的接口变量:
SHDocVw::IShellWindowsPtr m_spSHWinds;
然后创建变量的实例:
m_spSHWinds.CreateInstance
(__uuidof(SHDocVw::ShellWindows));
通过IShellWindows接口的方法GetCount
可以得到当前实例的数目:
long nCount = m_spSHWinds- >GetCount();
通过IShellWindows接口的方法Item
可以得到每一个实例对象
IDispatchPtr spDisp;
_variant_t va(i, VT_I4);
spDisp = m_spSHWinds->Item(va);
然后我们可以判断实例对象是不是
属于IE浏览器对象,通过下面的语句实现:
SHDocVw::IWebBrowser2Ptr spBrowser(spDisp);
assert(spBrowser != NULL)
—-在得到了IE浏览器对象以后,我们可以调用IWebBrowser2Ptr接口的方法来得到当前的文档对象的指针: MSHTML::IHTMLDocument2Ptr spDoc(spBrowser->GetDocument());
—- 然后我们就可以通过这个接口对这个文档对象进行操作,比如通过Gettitle得到文档的标题。
—- 我们在浏览网络的时候,一般总会同时开很多IE的实例,如果这些页面都是很好的话,我们可能想保存在硬盘上,这样,我们需要对每一个实例进行保存,而如果我们采用上面的原理,我们可以得到每一个IE的实例及其网页对象的接口,这样就可以通过一个简单的程序来批量的保存当前的所有打开的网页。采用上面介绍的方法实现了对当前IE实例的遍历,但是我们希望得到每一个IE实例所产生的事件,这就需要通过DLL的机制来实现。
—- 3.和IE相绑定的DLL的实现
—- 我们介绍一下如何建立和IE进行绑定的DLL的实现的过程。为了和IE的运行实例进行绑定,我们需要建立一个能够和每一个IE实例进行绑定的DLL。IE的启动过程是这样的,当每一个IE的实例启动的时候,它都会在注册表中去寻找这个的一个CLSID,具体的注册表的键位置为:
HKEY_LOCALL_MACHINESOFTWAREMicrosoftWindows
CurrentVersionExplorerBrowser Helper Objects
—- 当在这个键位置下存在CLSIDs的时候,IE会通过使用CoCreateInstance()方法来创建列在该键位置下的每一个对象的实例。注意对象的CLSIDs必须用子键而非启动过程是这样的,当每一个IE的实例启动的时候名字值的形式表现,比如{DD41D66E-CE4F-11D2-8DA9-00A0249EABF4} 就是一个有效的子键。我们使用DLL的形式而非EXE的形式的原因是因为DLL和IE实例运行在同一个进程空间里面。每一个这种形式的DLL必须实现接口IObjectWithSite,其中方法SetSite必须被实现。通过这个方法,我们自己的DLL就可以得到一个指向IE COM对象的IUnknown的指针,实际上通过这个指针我们就可以通过COM对象中的方法QueryInterface来遍历所有可以得到的接口,这是COM的基本的机制。当然我们需要的只是IWebBrowser2这个接口。
—- 实际上我们建立的是一个COM对象,DLL只不过是COM对象的一种表现形式。我们建立的COM对象需要建立和实现的方法有:
—-1. IOleObjectWithSite接口的方法SetSite必须实现。实际上IE实例通过这个方法向我们的COM对象传递一个接口的指针。假设我们有一个接口指针的变量,不妨设为:
—-CComQIPtr< IWebBrowser2, &IID_IWebBrowser2 > m_myWebBrowser2;
—- 我们就可以在方法SetSite中把这个传进来的接口指针赋给m_myWebBrowser2。 2. 在我们得到了指向IE COM对象的接口后,我们需要把自己的DLL和IE实例所发生的事件相关连,为了实现这个目的,需要介绍两个接口:
—-(1) IConnectionPointContainer。:
—-CComQIPtr< IWebBrowser2, &这里使用这个接口的目的是用来根据它得到的IID来建立和DLL的一个特定的连接。比如我们可以进行如下的定义:
CComQIPtr< IConnectionPointContainer,
&IID_IConnectionPointContainer >
spCPContainer(m_myWebBrowser2);
—-然后,我们需要把所有IE中发生的事件和我们的DLL进行通讯,可以使用 IConnectPoint。
—-(2) IConnectPoint。通过这个接口,客户可以对连接的对象开始或者是终止一个advisory循环。IConnectPoint有两个主要的方法,一个为Advice,另一个为Unadvise。对于我们的应用来说,Advise是用来在每一个IE发生的事件和DLL之间建立一个通道。而Unadvise就是用来终止以前用Advise建立的通知关系。比如我们可以定义IConnectPoint接口如下: CComPtr< IConnectionPoint > spConnectionPoint;
—- 然后,我们要使所有在IE实例中发生的事件和我们的DLL相关,可以使用 如下的方法:
hr = spCPContainer->FindConnectionPoint(
DIID_DWebBrowserEvents2, &spConnectionPoint);
—-然后我们通过IConnectPoint接口的方法Advice使每当IE有一个新的事件发生的时候,都能够让我们的DLL知道。可以用如下的语句实现:
hr = spConnectionPoint- >Advise(
(IDispatch*)this, &m_dwIDCode);
—-在把IE实例中的事件和我们的DLL之间建立联系以后,我们可以通过IDispatch接口的Invoke()方法来处理所有的IE的事件。
—-3. IDispatch接口的Invoke()方法。IDispatch是从IUnknown中继承的一个接口的类型,通过COM接口提供的任何服务都可以通过IDispatch接口来实现。IDispatch::Invoke的工作方式同vtbl幕后的工作方式是类似的,Invoke将实现一组按索引来访问的函数,我们可以对Invoke方法进行动态的定制以提供不同的服务。Invoke方法的表示如下:
STDMETHOD(Invoke)(DISPID dispidMember,REFIID
riid, LCID lcid, WORD wFlags,
DISPPARAMS * pdispparams, VARIANT * pvarResult,
EXCEPINFO * pexcepinfo, UINT * puArgErr);
—-其中,DISPID是一个长整数,它标识的是一个函数。对于IDispatch的某一个特定的实现,DISPID都是唯一的。IDispatch的每一个实现都有其自己的IID,这里dispidMemeber实际上是可以认为是和IE实例所发生的每一个事件相关的方法,比如:DISPID_BEFORENAVIGATE2,DISPID_NAVIGATECOMPLETE2等等。 这个方法中另外一个比较重要的参数是DISPPARAMS,它的结构如下:
typedef struct tagDISPPARAMS
{
VARIANTARG* rgvarg;
//VARIANTARG是同VARAIANT相同的,可以在
//OAIDL.IDL中找到。所以实际上rgvarg是一个参数数
//组
DISPID* rgdispidNameArgs; //命名参数的DISPID
unsigned int cArgs; //表示数组中元素的个数
unsigned int CnameArgs; //命名元素的个数
}DISPPARAMS
—-要注意的是每一个参数的类型都是VARIANTARG,所以在IE和我们DLL之间可以传递的参数类型的数目是有限的。只有那些能够被放到VARIANTARG结构中的类型才可以通过调度接口进行传递。 比如对于事件DISPID_NAVIGATECOMPLETE2来说:第一个参数表示IE在访问的URL的值,类型是VT_BYREF|VT_VARIANT。注意DISPID_NAVIGATECOMPLETE2等DISPID已经在VC中被定义,我们可以直接进行使用。 如上说述,我们在方法Invoke中可以得到所有IE实例所发生的事件,我们可以把这些数据放到文件中进行事后的分析,也可以放到一个列表框中实时的显示。
—- 4.微软的HTML文档对象模型和应用分析
—- 下面我们来看如何得到网页文档的接口:网页文档的接口为IHTMLDocument2,可以通过调用IE COM对象的get_Document方法来得到网页的接口。使用如下的语句:
hr = m_spWebBrowser2- >get_Document(&spDisp);
CComQIPtr< IHTMLDocument2,
&IID_IHTMLDocument2 > spHTML;
spHTML = spDisp;
—- 这样我们就得到了网页对象的接口,然后我们就可以对网页进行分析,比如通过IHTMLDocument2提供的方法get_URL我们可以得到和该网页相关的URL的地址值,通过get_forms方法可以该网页中所有的Form对象的集合。实际上W3C组织已经制定了一个DOM(Document Objec Model)标准,当然这个标准不仅仅是针对HTML,同时还是针对XML制定的。W3C组织只是定义了网页对象的接口,不同的公司可以采用不同的语言和方法进行具体的实现。按照W3C组织定义的网页对象被认为是动态的,即用户可以动态的对网页对象里面所包含的每一个对象进行操作。这里的对象可以是指一个输入框,也可以是图象和声音等对象。同时按照W3C的正式文档的说明,网页对象是可以动态增加和删除的。事实上,很少有厂商实现了DOM定义的所有功能。微软对网页对象的定义也基本上是按照这个标准实现的。但是当前的接口还不支持动态的增加和删除元素,但是可以对网页中的基本元素进行属性的修改。比如IHTMLElementCollection表示网页中一些基本的元素的集合,IHTMLElement表示网页中的一个基本的元素。而象IHTMLOptionElement接口就表示一个特定的元素Option。基本的元素都有setAttribute和geAttribute方法来动态的设置和得到元素的名称和值。
—- 较为常见的一个应用是我们能够分析网页中是否有需要填写的Forms,如果这个网址的Forms以前已经填写过而且数据我们已经保存下来的话,我们就可以把数据自动放到和该URL下的Forms的相关的位置中去。另外,我们可以总结网页上需要填写的Form的数据项,先对这些数据项进行赋值,以后碰到有相同的数据项的时候就自动把我们赋值的内容填写进去。实际上Form是对象,Form中包含的元素,比如INPUT,OPTION,SELECT等类型的输入元素都是对象。
—- 另外一个可以想到的应用是自动对网页中的文本进行翻译,因为我们可以修改网页中任何对象的属性,所以我们可以把里面不属于本国语言的部分自动翻译成本国语言,当然真正的实现还要靠自然语言理解方面技术的突破,但是IE浏览器的接口和对象的形式使我们能够灵活的控制整个IE,无论是从事件对象还是到网页对象。
—- 5.小结
—- 上面我们分析了如何得到所有IE的实例,同时介绍了和IE实例相捆绑的DLL的详细的实现机制,同时对网页的对象化进行了分析。并且介绍了几个相关的应用和实现的方法及存在的技术问题。IE是一个组件化的以COM为基础的浏览器,它具有强大的功能,同时为应用开发者留下了广阔的空间,当然它也存在体积比较大,速度相对比较慢的缺点。但是它的体系结构代表了微软先进的创新的技术,因此具有强大的生命力。
Tags: html, ie, shell, web, windows, 开发, 技术, 浏览器, 类
转至: http://www.codeproject.com

This module is based on the ATL DeskBand Wizard article that was created by Erik Thompson. My thanks goes to him for producing a great wizard that saves you the nity grity of creating DeskBands.
Please note that this project does not use MFC, for all those MFC die hards that want to use MFC in their Tool Bands I suggest you down load the KKBar sample from microsofts MSDN site.
The KBBar Sample can be downloaded from here
You will need to install the ATL DeskBand Wizard in order to create ToolBands. Please follow the instruction in this article.
The best way to kick start a Tool band project is to use the ATL COM App Wizard. The COM Object needs to be in-proc therefore choose ‘DLL’ as the Server Type. The rest of the options can be kept in there default state. (Note this project does not use MFC, as the project is based on ATL and WTL)
Once you have a ATL COM Project, You can use the ATL DeskBand Wizard to create the Initial Tool Band.
You will require the WTL Libraries, these can be downloaded from the microsoft site
The following header files needs to be added to stdafx.h file
atlapp.hatlwin.hatlctrls.hatlmisc.h
You can create your toolbar in the usual way via the resource editor, Once you have your toolbar you need to create the toolbar dynamically. The CBandToolBarCtrl is inherited from CToolBarCtrl and is used to create the toolbar dynamically.
DWORD dStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT; HWND hWnd = m_wndToolBar.CreateSimpleToolBarCtrl(hWndChild, IDR_TOOLBAR_TEST, FALSE, dStyle);
This is done in the RegisterAndCreateWindow function that is called from SetSite method.
The best way to handle the messages of the toolbar is to let the control handle its own messages via "Message Reflection". The best way to reflect the messages is to create an invisible control that acts as the parent for the toolbar. The invisible control will reflect the toolbar messages back to itself. See CBandToolBarReflectorCtrl class for the invisible control that will be used.
The following code shows the parent child relationship that is used to achieve Message Reflection.
BOOL CToolBandObj::RegisterAndCreateWindow(){ RECT rectClientParent; ::GetClientRect(m_hWndParent, &rectClientParent); // We need to create an Invisible Child Window using the Parent Window, // this will also be used to reflect Command // messages from the rebar HWND hWndChild = m_wndInvisibleChildWnd.Create(m_hWndParent, rectClientParent, NULL, WS_CHILD); // Now we can create the Tool Bar, using the Invisible Child DWORD dStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT; HWND hWnd = m_wndToolBar.CreateSimpleToolBarCtrl(hWndChild,
IDR_TOOLBAR_TEST, FALSE, dStyle); m_wndToolBar.SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); m_wndToolBar.m_ctlBandEdit.m_pBand = this; return ::IsWindow(m_wndToolBar.m_hWnd);}
The following Macros are used to identify the reflected messages from the ordinary messages. e.g WM_COMMAND reflected comes back as OCM_COMMAND.
#define OCM_COMMAND_CODE_HANDLER(code, func) \if(uMsg == OCM_COMMAND && code == HIWORD(wParam)) \{ \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \}#define OCM_COMMAND_ID_HANDLER(id, func) \if(uMsg == OCM_COMMAND && id == LOWORD(wParam)) \{ \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \}#define OCM_NOTIFY_CODE_HANDLER(cd, func) \if(uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code) \{ \ bHandled = TRUE; \ lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \ if(bHandled) \ return TRUE; \}
In order to Navigate on the browser you need to instantiate the IWebBrowser2 COM Object. This is usually done on the SetSite Method e.g.
IServiceProviderPtr pServiceProvider = pUnkSite;if (_Module.m_pWebBrowser) _Module.m_pWebBrowser = NULL; if(FAILED(pServiceProvider->QueryService(SID_SWebBrowserApp, IID_IWebBrowser, (void**)&_Module.m_pWebBrowser)))return E_FAIL;
Once you have the COM Object Instantiated, you can move to your URL using the navigate method
_variant_t varURL = _bstr_t(www.codeproject.com); _variant_t varEmpty;_Module.m_pWebBrowser->Navigate2(&varURL, &varEmpty, &varEmpty, &varEmpty, &varEmpty);
The CBandEditCtrl class is inherited from a WTL CEdit control that has drag and drop facility. i.e. you can drag text from the browser straight to the CEdit Control.
The CBandComboBoxCtrl class is inherited from a WTL CComboBox control that has drag and drop facility. i.e. you can drag text from the browser straight to the CComboBox Control.

The Edit control in this sample module allows you to drag a URL from Explorer or any other Dragable enabled container. Once you have dropped the URL the toolband will go to that site.
The CBandToolBarCtrl class allows you to have the follwoing button styles on the toolbar



A pop up menu is used to get to the configuration options

This has been taken from MSDN to explain why you need to handle the tooltips on the toolbar your self.
Tool tips are automatically displayed for buttons and other controls contained in a parent window derived from CFrameWnd. This is because CFrameWnd has a default handler for the TTN_GETDISPINFO notification, which handles TTN_NEEDTEXT notifications from tool tip controls associated with controls.
However, this default handler is not called when the TTN_NEEDTEXT notification is sent from a tool tip control associated with a control in a window that is not a CFrameWnd, such as a control on a dialog box or a form view. Therefore, it is necessary for you to provide a handler function for the TTN_NEEDTEXT notification message in order to display tool tips for child controls.
This can be easily done in WTL by using the following Message Handler in the overriden ToolBar Control
NOTIFY_CODE_HANDLER(TTN_NEEDTEXT, OnToolbarNeedText)
The following is the code that loads the tool tips from the resources and sets the tool tip text.
LRESULT CBandToolBarCtrl::OnToolbarNeedText(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { CString sToolTip; //-- make sure this 1is not a seperator if (idCtrl != 0) { if (!sToolTip.LoadString(idCtrl)) { bHandled = FALSE; return 0; } } LPNMTTDISPINFO pttdi = reinterpret_cast<LPNMTTDISPINFO> (pnmh);pttdi->lpszText = MAKEINTRESOURCE(idCtrl); pttdi->hinst = _Module.GetResourceInstance(); pttdi->uFlags = TTF_DI_SETITEM; //-- message processed return 0;}
You can update the status bar using the browser method put_StatusText, this method can be used typically in the WM_MENUSELECT event
The following is the code that loads up the menu text from the resources and displays it on the browser status bar.
LRESULT CBandToolBarCtrl::OnMenuSelect(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { WORD nID = LOWORD(wParam); WORD wFlags = HIWORD(wParam); //-- make sure this is not a seperator CString sStatusBarDesc; if ( !(wFlags & MF_POPUP) ) { if (nID != 0) { if (!sStatusBarDesc.LoadString(nID)) { bHandled = FALSE; return 0; } int nPos = sStatusBarDesc.Find(_T("\n")); if (nPos != -1) { sStatusBarDesc = sStatusBarDesc.Left(nPos+1); _Module.m_pWebBrowser-> put_StatusText(_bstr_t(sStatusBarDesc)); return 0; } } } return 0;}
In order to add a chevron to your toolband you need to add the DBIMF_USECHEVRON flags to your GetBandInfo method in the DBIM_MODEFLAGS mask.
... if(pdbi->dwMask == DBIM_MODEFLAGS) { //AddChevron pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT | DBIMF_USECHEVRON | DBIMF_BREAK; }...
This basically add the ability to show the chevron, if you want to see it appear on your toolband, then you must make sure the pdbi->ptMinSize.x value is less then your pdbi->ptActual.x value (Again this values can be set in the GetBandInfo method.
In order to handle the events of the button in the chevron menu, you must subclass the rebar control which is hosting your toolbar. The following steps have been used to sublass the rebar control
1. Find the rebar control – This can be achieved by getting the browsers window handle and searching for all its child windows, until you find the rebar control.
2. Once you have found the rebar control you can simply subclass it using an ATL CContainedWindow.
BOOL CBandToolBarCtrl::SetBandRebar() { HWND hWnd(NULL); _Module.m_pWebBrowser->get_HWND((long*)&hWnd); if (hWnd == NULL) return FALSE; m_ctlRebar.m_hWnd = FindRebar(hWnd); if (m_ctlRebar.m_hWnd == NULL) return FALSE; m_RebarContainer.SubclassWindow(m_ctlRebar); return TRUE; }
Once you have subclass the window, the events should reach the toolbar class.

I tried to duplicate the google and codeproject toolband right mouse browser context menu searches without any luck, until I looked at the binary resources, I found that you can extend the internet explorer menu by adding the option in the registry. This can easily be achieved by adding an entry in your .rgs file.
HKCU{ NoRemove Software { NoRemove Microsoft { NoRemove 'Internet Explorer' { NoRemove MenuExt { ForceRemove '&Sample Toolband Serach' = s'res://%MODULE%/MENUSEARCH.HTM' { val Contexts = b '10' } } } } }}
Note the value of the registry item (a html script that exist in the resources of the module).
see MENUSERACH.HTM under HTML in the module resource to see what the script is doing
Tags: class, html, ie, server, toolbar, web, windows, wtl
一.什么是字节对齐,为什么要对齐?
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。
二.字节对齐对程序的影响:
先让我们看几个例子吧(32bit,x86环境,gcc编译器):
设结构体如下定义:
struct A
{
int a;
char b;
short c;
};
struct B
{
char b;
int a;
short c;
};
现在已知32位机器上各种数据类型的长度如下:
char:1(有符号无符号同)
short:2(有符号无符号同)
int:4(有符号无符号同)
long:4(有符号无符号同)
float:4 double:8
那么上面两个结构大小如何呢?
结果是:
sizeof(strcut A)值为8
sizeof(struct B)的值却是12
结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个,B也一样;按理说A,B大小应该都是7字节。
之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。上面是按照编译器的默认设置进行对齐的结果,那么我们是不是可以改变编译器的这种默认对齐设置呢,当然可以.例如:
#pragma pack (2) /*指定按2字节对齐*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct C)值是8。
修改对齐值为1:
#pragma pack (1) /*指定按1字节对齐*/
struct D
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct D)值为7。
后面我们再讲解#pragma pack()的作用.
三.编译器是按照什么样的原则进行对齐的?
先让我们看四个重要的基本概念:
1.数据类型自身的对齐值:
对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
3.指定对齐值:#pragma pack (value)时的指定对齐值value。
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。
有 了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是 表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数 据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数 倍,结合下面例子理解)。这样就不能理解上面的几个例子的值了。
例子分析:
分析例子B;
struct B
{
char b;
int a;
short c;
};
假 设B从地址空间0×0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定 对齐值4小,所以其有效对齐值为1,所以其存放地址0×0000符合0×0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4, 所以只能存放在起始地址为0×0004到0×0007这四个连续的字节空间中,复核0×0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为 2,所以有效对齐值也是2,可以存放在0×0008到0×0009这两个字节空间中,符合0×0008%2=0。所以从0×0000到0×0009存放的 都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求, 0×0009到0×0000=10字节,(10+2)%4=0。所以0×0000A到0×000B也为结构体B所占用。故B从0×0000到0×000B 共有12个字节,sizeof(struct B)=12;其实如果就这一个就来说它已将满足字节对齐了, 因为它的起始地址是0,因此肯定是对齐的,之所以在后面补充2个字节,是因为编译器为了实现结构数组的存取效率,试想如果我们定义了一个结构B的数组,那 么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都是紧挨着的,如果我们不把结构的大小补充为4的整数倍,那么下一 个结构的起始地址将是0×0000A,这显然不能满足结构的地址对齐了,因此我们要把结构补充成有效对齐大小的整数倍.其实诸如:对于char型数据,其 自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,这些已有类型的自身对齐值也是基于数组考虑的,只 是因为这些类型的长度已知了,所以他们的自身对齐值也就已知了.
同理,分析上面例子C:
#pragma pack (2) /*指定按2字节对齐*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
第 一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0×0000开始,那么b存放在0×0000,符合0×0000%1= 0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0×0002、0×0003、0×0004、0×0005四个连续 字节中,符合0×0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放
在0×0006、0×0007中,符合 0×0006%2=0。所以从0×0000到0×00007共八字节存放的是C的变量。又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C 只占用0×0000到0×0007的八个字节。所以sizeof(struct C)=8.
四.如何修改编译器的默认对齐值?
1.在VC IDE中,可以这样修改:[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改,默认是8字节。
2.在编码时,可以这样动态修改:#pragma pack .注意:是pragma而不是progma.
五.针对字节对齐,我们在编程中如何考虑?
如果在编程的时候要考虑节约空间的话,那么我们只需要假定结构的首地址是0,然后各个变量按照上面的原则进行排列即可,基本的原则就是把结构中的变量按照 类型大小从小到大声明,尽量减少中间的填补空间.还有一种就是为了以空间换取时间的效率,我们显示的进行填补空间进行对齐,比如:有一种使用空间换时间做 法是显式的插入reserved成员:
struct A{
char a;
char reserved[3];//使用空间换时间
int b;
}
reserved成员对我们的程序没有什么意义,它只是起到填补空间以达到字节对齐的目的,当然即使不加这个成员通常编译器也会给我们自动填补对齐,我们自己加上它只是起到显式的提醒作用.
六.字节对齐可能带来的隐患:
代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如:
unsigned int i = 0×12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;
p=&i;
*p=0×00;
p1=(unsigned short *)(p+1);
*p1=0×0000;
最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。
在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐.
七.如何查找与字节对齐方面的问题:
如果出现对齐或者赋值问题首先查看
1. 编译器的big little端设置
2. 看这种体系本身是否支持非对齐访问
3. 如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。
八.相关文章:转自http://blog.csdn.net/goodluckyxl/archive/2005/10/17/506827.aspx
ARM下的对齐处理
from DUI0067D_ADS1_2_CompLib
3.13 type qulifiers
有部分摘自ARM编译器文档对齐部分
对齐的使用:
1.__align(num)
这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时
就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。
这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节
对齐,但是不能让4字节的对象2字节对齐。
__align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。
2.__packed
__packed是进行一字节对齐
1.不能对packed的对象进行对齐
2.所有对象的读写访问都进行非对齐访问
3.float及包含float的结构联合及未用__packed的对象将不能字节对齐
4.__packed对局部整形变量无影响
5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定
义为packed。
__packed int* p; //__packed int 则没有意义
6.对齐或非对齐读写访问带来问题
__packed struct STRUCT_TEST
{
char a;
int b;
char c;
} ; //定义如下结构此时b的起始地址一定是不对齐的
//在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]
//将下面变量定义成全局静态不在栈上
static char* p;
static struct STRUCT_TEST a;
void Main()
{
__packed int* q; //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以
p = (char*)&a;
q = (int*)(p+1);
*q = 0×87654321;
/*
得到赋值的汇编指令很清楚
ldr r5,0×20001590 ; = #0×12345678
[0xe1a00005] mov r0,r5
[0xeb0000b0] bl __rt_uwrite4 //在此处调用一个写4byte的操作函数
[0xe5c10000] strb r0,[r1,#0] //函数进行4次strb操作然后返回保证了数据正确的访问
[0xe1a02420] mov r2,r0,lsr #8
[0xe5c12001] strb r2,[r1,#1]
[0xe1a02820] mov r2,r0,lsr #16
[0xe5c12002] strb r2,[r1,#2]
[0xe1a02c20] mov r2,r0,lsr #24
[0xe5c12003] strb r2,[r1,#3]
[0xe1a0f00e] mov pc,r14
*/
/*
如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败
[0xe59f2018] ldr r2,0×20001594 ; = #0×87654321
[0xe5812000] str r2,[r1,#0]
*/
//这样可以很清楚的看到非对齐访问是如何产生错误的
//以及如何消除非对齐访问带来问题
//也可以看到非对齐访问和对齐访问的指令差异导致效率问题
}