nalsakas/pe

语言: Assembly

git: https://github.com/nalsakas/pe

pe
README.md (中文)

NASM ON MACROS

作者:Seyhmus AKASLAN 联系方式:nalsakas@gmail.com 许可证:GPL v2

目录

  1. 介绍
  2. VA()/ RVA()MACROS
  3. 平面模型
  4. 进口宏
  5. 出口宏
  6. 资源宏
  7. 菜单MACROS
  8. DIALOG MACROS
  9. STRINGTABLE MACROS
  10. 可加速的MACROS
  11. BITMAP MACRO

:顶:简介

很长一段时间我想直接从nasm汇编器输出PE32,DLL32,PE64和DLL64格式。 Nasm不支持直接输出到可执行文件或dll文件,而是支持原始二进制输出并具有高级宏支持。我在这个项目中的目标是使用nasm的宏功能直接输出可执行文件。为了实现这一点,我需要发明很多新的宏。其中一些是PE头结构,节表,数据目录,资源宏等。

为什么我创建这些宏集?答案很简单。因为我对可执行文件的内部工作方式充满热情。除此之外,我在使用nasm宏和pe文件格式时学到了很多东西。

使用这些宏集,您可以自己执行自定义可执行文件。您不需要任何对象链接器或资源编译器。您可以通过键入其名称来导入您想要的任何功能;你可以导出你想要的任何本地功能;你可以包括你想要的任何资源;您可以尝试并深入了解pe文件格式。

目前DLL32 / 64文件不支持重定位。资源数据目录也不支持ICON资源类型。未来的版本可能具有这种功能。

虽然引擎盖下有很多宏,但最终用户只需知道其中的一些。实际上只有3个足以满足非常基本的PE。

%include 'pe.inc'  
PE32

START    
  ret  
END

上面的示例是一个有效的pe文件。它只是在加载后立即返回。您需要记住只有3个宏。 PE32,START和END。

现在,看下面的例子。

示例PE32文件:

%include "pe.inc"  

; For 32-bit executable use PE32
; For 32-bit dll use DLL32
; For 64-bit executable use PE64
; For 64-bit dll use DLL64
PE32

; Data declarations
Title db 'Title of MessageBox',0

; enty point of executable  
START  

; machine intructions  
  ...  
  push VA(Title)  
  push 0  
  call [VA(MessageBoxA)]  

LocalFunction:  
     ...  
     ret  

; Setup import directory if you need to  
IMPORT / ENDIMPORT  

; Setup Export Directory if you need to  
EXPORT / ENDEXPORT  

; Setup Resource Directory (Resource Tree) if you need to    
RESOURCE / ENDRESOURCE  

; Setup Menu if defined in resource tree  
MENU / ENDMENU  

; Setup Dialog if defined in resource tree  
DIALOG / ENDDIALOG  

; Setup String Table if defined in resource tree  
STRINGTABLE / ENDSTRINGTABLE  

...

END  
; End of executable  

您可以在下面找到用户空间宏的详细分析。

:top:VA()/ RVA()MACROS

装配中的标签是基于偏移的。它们实际上不包含虚拟地址。发明了VA()和RVA()宏以将基于偏移的标签转换为虚拟地址。

例子:

Before After Description
push dword [label] push dword [VA(label)]
mov eax, dword [label] mov eax, [VA(label)]
call [label] call [VA(label)]
call label call label this line doesn't require VA()

请注意,有两种类型的呼叫指令。一个使用相对位移,其形式是呼叫标签。此表单不需要VA()宏。但是需要绝对虚拟地址的另一种形式有[label]形式。您希望此表单需要VA()宏。

:顶部:平板模型

PE32,PE64,DLL32和DLL64宏现在可选择排除FLAT参数。使用时,它消除了在标签上使用VA()宏的必要性。 PE32还有可选的第三个参数,它将段对齐和文件对齐设置为相同的常量。这样您的可执行文件就变得扁平了。它的大小在文件和内存中都会变得相等。如果您不提供任何默认对齐是1000h。

使用FLAT模型时,代码看起来更简单,因为可以删除所有VA()引用。他们不再需要了。唯一的缺点是,在默认的1000h对齐中,您的可执行文件大小会增加。

%include "../pe.inc"

PE32 FLAT

BYTE Title, "NASM PE MACROS",0
BYTE Text, "Flat memory works!",0

START
    push 0
    push Title          ;--> No VA() anymore
    push Text           ;--> No VA() anymore
    push 0
    call [MessageBoxA]  ;--> No VA() anymore
    ret

IMPORT
    LIB user32.dll
        FUNC MessageBoxA
    ENDLIB
ENDIMPORT

END

:top:IMPORT MACROS

如果要在代码中使用其他库中的外部函数,请使用IMPORT宏。 IMPORT宏具有以下形式。

IMPORT 
    ; write down imported dll's 
    LIB Libname 
        ; write down imported functions 
        FUNC Functionname  
        ...
    ENDLIB
    ...
    LIB kernel32.dll  
        FUNC ExitProcess  
    ENDLIB  
ENDIMPORT  

可以有多个LIB / ENDLIB以及多个FUNC。用法很简单。所有这个宏都是将import表放在声明它的位置。请注意,libname和函数名称是令牌形式。它们不是字符串形式。此宏将函数名称转换为标签。该标签的行为类似于该特定功能的IAT条目的地址。如果需要访问程序集内部的导入函数,请调用[VA(function_name)]。

:top:EXPORT MACROS

如果要导出可执行文件的本地函数,请使用此宏。根据msdn,EXE文件和DLL都可以导出函数。 EXPORT宏将导出目录放在声明它的位置。样品用法如下。 function_name是本地函数之一。导出目录可选地采用模块名称,即文件名。通常以这种形式libname.dll。您可以通过导入来访问库的导出功能。

; module name is optional

EXPORT module_name
    ; write down local functions to export
    FUNC function_name  
    ...  
ENDEXPORT  

:top:RESOURCE MACROS

资源是树状结构,因此这种结构也称为资源树。根据msdn,只有3个级别。第一级是TYPE级别。您在此处声明资源类型。 RT_MENU,RT_DATA,RT_DIALOG等。第二级是ID级别。您可以在此处定义资源ID。 ID_ICON,ID_MENU等。第三级是LANG,语言级别。您可以在此处定义语言和子语言ID。最后一级被称为LEAF级别。您可以使用叶子作为实际资源的指针。许多资源需要额外的结构。用户定义的资源和原始资源不需要任何特殊结构。首先定义资源树,它具有type,id,lang和指向实际资源的指针。其次定义实际资源,如MENU,DIALOG等。

示例资源树:

RESOURCE
    TYPE type_id  
        ID resource_id  
            LANG  lang_id, sublang_id / default is 0,0 for language neutral 
                LEAF RVA(resource_label), SIZEOF(resource_label)  
            ENDLANG  
        ENDID  
    ENDTYPE

    TYPE RT_MENU  
        ID 200h  
            LANG
                LEAF RVA(menu), SIZEOF(menu)  
            ENDLANG  
        ENDID

        ID resource_id2 
            ...  
        ENDID  
    ENDTYPE  

    TYPE RT_DATA  
        ID resource_id3  
            LANG  
                LEAF RVA(raw_data), SIZEOF(raw_data)  
            ENDLANG  
        ENDID  
    ENDTYPE  
ENDRESOURCE   

:top:MENU MACROS

为了使用MENU资源,首先在资源树中包含RT_MENU类型的资源。然后使用以下MENU宏定义菜单。

; Menu macro generates special format required by MENU resources.  
MENU menu_label  
    ; First parameter is name, second is id and optional third parameter is flags  
    MENUITEM 'name', menu_item_id  

    ; First parameter is name and optional second parameter is flags  
    POPUP 'name'  
       MENUITEM 'name', menu_item_id  
    ENDPOPUP    
ENDMENU  

MENU宏有助于创建菜单资源。内部声明的子宏只有两种类型。一个是MENUITEM,另一个是POPUP / ENDPOPUP。

:顶部:DIALOG MACROS

为了使用对话资源,首先在资源树中包含一个具有RT_DIALOG类型的资源。然后使用以下DIALOG宏来定义对话框。

DIALOG label, x, y, cx, cy  
  STYLE xxx                ; Optional  
  EXSTYLE xxx              ; Optional  
  CAPTION 'text'           ; Optional  
  MENU resource_id         ; Optional  
  FONT size, 'font face'   ; Optional

  ; Declare controls  
  CONTROL 'text', id, class_id, x, y, cx, cy, optional stye, optional exstyle  

  ; Predefined controls doesn't need class id  
  PUSHBUTTON 'text', id, x, y, cx, cy, optional style, optional exstyle  
  EDITTEXT id, x, y, cx, cy, style, exstyle  
  ...  
ENDDIALOG  

您不需要在DIALOG宏下放置STYLE,EXSTYLE,FONT和CAPTION宏。它们是可选的。如果需要对话框菜单,请将MENU放在DIALOG宏下。如果你需要一个对话框的标题,那么在DIALOG宏下面放一个CAPTION宏。如果您需要其他样式,请在DIALOG宏下放置STYLE和EXSTYLE。如果你没有放置STYLE,对话框使用默认样式;

WS_POPUP | WS_BORDER | WS_SYSMENU | WS_VISIBLE | DS_SETFONT | WS_CAPTION | DS_NOFAILCREATE

共有15种预定义的子控件​​。所有这些都基于CONTROL宏。这些子控件是DEFPUSHBUTTON,PUSHBUTTON,GROUPBOX,RADIOBUTTON,AUTOCHECKBOX,AUTO3STATE,AUTORADIOBUTTON,PUSHBOX,STATE3,COMBOBOX,LTEXT,RTEXT,CTEXT,CHECKBOX,EDITTEXT,LISTBOX和SCROLLBAR。

:top:STRINGTABLE MACROS

一个STRINGTABLE最多可容纳16个STRING。如果您有超过16个字符串,则需要打开另一个表。每个表由资源树中的一个资源ID引用。普通资源编译器需要您将字符串ID放在表中。我们这里不使用这种方法。相反,我们将字符串放在表中而没有ID但隐含索引。第一个字符串的索引为1,第二个字符串为2,依此类推。当您需要在表中引用字符串时,请使用SID()宏代表字符串ID。该宏表示2个参数。第一个是资源树中定义的表的资源ID,第二个是字符串的索引。 SID()宏返回表中每个字符串的计算ID。

push buffer_size  
push VA(buffer)              ; twice size of a char buffer
push SID(ID_TABLE, 1)        ; loads first string  
push dword [VA(hInstance)]  
call [VA(LoadStringA)]   

在使用STRINGTABLE宏之前,首先在资源树中包含RT_STRING类型的资源。表中的STRING存储为16位unicode字符串。

STRINGTABLE label  
  STRING 'First String'     ; stored as unicode strings.  
  STRING 'Second String'  
  STRING 'Third String'  
  ...  
  STRING '16th string'  
ENDSTRINGTABLE  

:top:ACCELERATORTABLE MACROS

使用ACCELERATORTABLE宏,您可以将加速器包含在资源中。然后,您可以在LoadAccelerator API的帮助下在asm中使用它们。首先,您需要在资源树中包含RT_ACCELERATOR类型的资源。然后添加下表。

ACCELERATORTABLE label
   ; %1 = ascii key or virtual key,  %2 = ID of key,  %3 = flags
   ACCELERATOR 'A', ID_ACTION_SHIFT_A, FSHIFT
   ACCELERATOR VK_F5, ID_ACTION_CONTROL_F5, FCONTROL | FVIRTKEY
   ACCELERATOR VK_RETURN, ID_ACTION_ALT_ENTER, FALT | FVIRTKEY

   ; default flag is shift key
   ACCELERATOR 'H', IDM_FILE_HELP
ENDACCELERATORTABLE

:顶部:BITMAP MACRO

使用BITMAP宏,您可以在您的资源中包含位图。然后,您可以使用LoadBitmap API在asm代码中使用它们。要从位图开始,首先在资源树中包含RT_BITMAP类型的资源。

; Resource Tree
RESOURCE
    ; Entry of bitmap in resource tree
    TYPE RT_BITMAP
        ID 100h
            LANG
                LEAF RVA(bitmap_label), SIZEOF(bitmap_label)
            ENDLANG
        ENDID
    ENDTYPE
ENDRESOURCE

; Bitmap
BITMAP bitmap_label, 'bitmap.bmp'

本文使用googletrans自动翻译,仅供参考, 原文来自github.com

en_README.md

NASM PE MACROS

Author: Seyhmus AKASLAN
Contact: nalsakas@gmail.com
License: GPL v2

TABLE OF CONTENTS

  1. INRODUCTION
  2. VA()/RVA() MACROS
  3. FLAT MODEL
  4. IMPORT MACROS
  5. EXPORT MACROS
  6. RESOURCE MACROS
  7. MENU MACROS
  8. DIALOG MACROS
  9. STRINGTABLE MACROS
  10. ACCELERATORTABLE MACROS
  11. BITMAP MACRO

:top:INTRODUCTION

For a long time I wanted to output PE32, DLL32, PE64 and DLL64 formats directly from nasm assembler. Nasm doesn't have support of direct output to executable files or dll files but instead it has support of raw binary output and has advanced macro support. My aim in this project is to use nasm's macro capability to directly output executables. In order to made that happen I needed to invent pretty a lot new macros. Some of them are PE header structures, section tables, data directories, resource macros, etc.

Why I created these macro sets? Answer is simple. Because I have a passion about inner workings of executables. Apart from that I have learned a lot while working with nasm macros and pe file format.

With these macro sets you can do custom executables by yourself. You don't need any object linker or resource compiler whatsoever. You can import any function you want just by typing its name; you can export any local function you want; you can include any resource you want; you can experiment and get a deeper insight of pe file format.

Currently DLL32/64 files doesn't support relocations. Resource data directory doesn't support ICON resource types also. Future versions may have that capabilities.

Although there are a lot of macros under the hood, end user only need to know a few of them. Actually only 3 of them suffice for a very basic PE.

%include 'pe.inc'  
PE32

START    
  ret  
END

Example above is a valid pe file. All it does is to return as soon as loaded. There are only 3 macros you need to remember. PE32, START and END.

Now, look at the below example.

Sample PE32 file:

%include "pe.inc"  

; For 32-bit executable use PE32
; For 32-bit dll use DLL32
; For 64-bit executable use PE64
; For 64-bit dll use DLL64
PE32

; Data declarations
Title db 'Title of MessageBox',0

; enty point of executable  
START  

; machine intructions  
  ...  
  push VA(Title)  
  push 0  
  call [VA(MessageBoxA)]  

LocalFunction:  
     ...  
     ret  

; Setup import directory if you need to  
IMPORT / ENDIMPORT  

; Setup Export Directory if you need to  
EXPORT / ENDEXPORT  

; Setup Resource Directory (Resource Tree) if you need to    
RESOURCE / ENDRESOURCE  

; Setup Menu if defined in resource tree  
MENU / ENDMENU  

; Setup Dialog if defined in resource tree  
DIALOG / ENDDIALOG  

; Setup String Table if defined in resource tree  
STRINGTABLE / ENDSTRINGTABLE  

...

END  
; End of executable  

You can find detailed analysis of user space macros below.

:top:VA()/RVA() MACROS

Labels in assembly are offset based. They don't actually contain virtual addresses. VA() together with RVA() macros are invented to convert offset based labels into virtual addresses.

Examples:

Before After Description
push dword [label] push dword [VA(label)]
mov eax, dword [label] mov eax, [VA(label)]
call [label] call [VA(label)]
call label call label this line doesn't require VA()

Beware there are two types of call instructions. One uses relative displacement whose form is call label. This form doesn't require VA() macro. But the other form which needs absolute virtual address has call [label] form. This form as you expect requires VA() macro.

:top:FLAT MODEL

PE32, PE64, DLL32 and DLL64 macros now optionally excepts FLAT parameter. When used it removes the necessity of using VA() macros at labels. PE32 also has optional third argument which sets both section alignment and file alignment to the same constant. This way your executable becomes flattened. It's size will become equal both in file and in memory. If you don't supply anything default alignment is 1000h.

When FLAT model used, code looks simpler because all VA() references can be removed. They are no longer needed. The only downside is that in default 1000h alignment your executables size's increases.

%include "../pe.inc"

PE32 FLAT

BYTE Title, "NASM PE MACROS",0
BYTE Text, "Flat memory works!",0

START
    push 0
    push Title          ;--> No VA() anymore
    push Text           ;--> No VA() anymore
    push 0
    call [MessageBoxA]  ;--> No VA() anymore
    ret

IMPORT
    LIB user32.dll
        FUNC MessageBoxA
    ENDLIB
ENDIMPORT

END

:top:IMPORT MACROS

If you want to use external functions from other libraries in your code use IMPORT macro. IMPORT macro has following form.

IMPORT 
    ; write down imported dll's 
    LIB Libname 
        ; write down imported functions 
        FUNC Functionname  
        ...
    ENDLIB
    ...
    LIB kernel32.dll  
        FUNC ExitProcess  
    ENDLIB  
ENDIMPORT  

There can be more than one LIB/ENDLIB as well as more than one FUNC. Usage is very simple. All this macro does is to put import table where it is declared. Notice that libname and function names are in token form. They are not in string form. This macro turns function names into labels. That labels behaves like addresses of IAT entry of that particular function. If you need to access imported function inside assembly use call [VA(function_name)].

:top:EXPORT MACROS

If you want to export local functions of your executable use this macro. According to msdn both EXE files and DLL's can have exported functions. EXPORT macro puts export directory where it is declared. Sample usage is given below. function_name is one of local functions. Export directory optionally takes module name which is it's file name. Usually in this form libname.dll. You can access exported functions of a library by importing it.

; module name is optional

EXPORT module_name
    ; write down local functions to export
    FUNC function_name  
    ...  
ENDEXPORT  

:top:RESOURCE MACROS

Resources are tree like structures hence this structure is also known as resource tree. According to msdn there are only 3-levels. First level is TYPE level. You declare type of resource here. RT_MENU, RT_DATA, RT_DIALOG etc. Second level is ID level. You define IDs of resources here. ID_ICON, ID_MENU etc. Third level is LANG, language level. You define language and sublanguage IDs here. Last level is known as LEAF level. You can use leafs as pointers to actual resources. Many resources require additional structures. User defined resources and raw resources doesn't require any special structures. First define resource tree, which has type, id, lang and pointer to actual resources. Second define actual resources like MENU's, DIALOGs, etc.

Sample Resource Tree:

RESOURCE
    TYPE type_id  
        ID resource_id  
            LANG  lang_id, sublang_id / default is 0,0 for language neutral 
                LEAF RVA(resource_label), SIZEOF(resource_label)  
            ENDLANG  
        ENDID  
    ENDTYPE

    TYPE RT_MENU  
        ID 200h  
            LANG
                LEAF RVA(menu), SIZEOF(menu)  
            ENDLANG  
        ENDID

        ID resource_id2 
            ...  
        ENDID  
    ENDTYPE  

    TYPE RT_DATA  
        ID resource_id3  
            LANG  
                LEAF RVA(raw_data), SIZEOF(raw_data)  
            ENDLANG  
        ENDID  
    ENDTYPE  
ENDRESOURCE   

:top:MENU MACROS

In order to use MENU resources first include a resource of type RT_MENU into resource tree. Then use following MENU macro to define your menu.

; Menu macro generates special format required by MENU resources.  
MENU menu_label  
    ; First parameter is name, second is id and optional third parameter is flags  
    MENUITEM 'name', menu_item_id  

    ; First parameter is name and optional second parameter is flags  
    POPUP 'name'  
       MENUITEM 'name', menu_item_id  
    ENDPOPUP    
ENDMENU  

MENU macros helps tou create menu resources. There are only 2 types of child macros declared inside. One is MENUITEM and other is POPUP/ENDPOPUP.

:top:DIALOG MACROS

In order to use dialog resources first include one resource with RT_DIALOG type into resource tree. Then use following DIALOG macro to define your dialog.

DIALOG label, x, y, cx, cy  
  STYLE xxx                ; Optional  
  EXSTYLE xxx              ; Optional  
  CAPTION 'text'           ; Optional  
  MENU resource_id         ; Optional  
  FONT size, 'font face'   ; Optional

  ; Declare controls  
  CONTROL 'text', id, class_id, x, y, cx, cy, optional stye, optional exstyle  

  ; Predefined controls doesn't need class id  
  PUSHBUTTON 'text', id, x, y, cx, cy, optional style, optional exstyle  
  EDITTEXT id, x, y, cx, cy, style, exstyle  
  ...  
ENDDIALOG  

You don't need to put STYLE, EXSTYLE, FONT and CAPTION macros beneath DIALOG macro. They are optional. If you need a dialog menu then put MENU beneath DIALOG macro. If you need a caption for your dialog then put a CAPTION macro beneath DIALOG macro. If you need additional styles put STYLE and EXSTYLE beneath DIALOG macro. If you don't put a STYLE, dialog uses default styles which are;

WS_POPUP | WS_BORDER | WS_SYSMENU | WS_VISIBLE | DS_SETFONT | WS_CAPTION | DS_NOFAILCREATE

There are total 15 kinds of predefined child controls. All of them based on CONTROL macro. These child controls are DEFPUSHBUTTON, PUSHBUTTON, GROUPBOX, RADIOBUTTON, AUTOCHECKBOX, AUTO3STATE, AUTORADIOBUTTON, PUSHBOX, STATE3, COMBOBOX, LTEXT, RTEXT, CTEXT, CHECKBOX, EDITTEXT, LISTBOX and SCROLLBAR.

:top:STRINGTABLE MACROS

One STRINGTABLE can hold up to 16 STRINGs. If you have more than 16 strings you need to open another table. Each table referenced by one resource ID in resource tree. Normal resource compilers needs you put string ID's in the table. We don't use this method here. Instead we put strings in table without ID but with implied index. First string has index 1, second is 2 and so on. When you need to reference a string in a table use SID() macro which stands for string ID. This macro excpects 2 parameters. First one is resource ID of table defined in resource tree and second one is index of string. SID() macro returns calculated ID of each string in a table.

push buffer_size  
push VA(buffer)              ; twice size of a char buffer
push SID(ID_TABLE, 1)        ; loads first string  
push dword [VA(hInstance)]  
call [VA(LoadStringA)]   

Before using STRINGTABLE macros first include a resource of type RT_STRING into resource tree. STRING's in tables are stored as 16-bit unicode strings.

STRINGTABLE label  
  STRING 'First String'     ; stored as unicode strings.  
  STRING 'Second String'  
  STRING 'Third String'  
  ...  
  STRING '16th string'  
ENDSTRINGTABLE  

:top:ACCELERATORTABLE MACROS

With ACCELERATORTABLE macros you can include accelerators into your resources. Then you can use them inside asm with the help of LoadAccelerator API. To start with accelerators first you need to include a resource of type RT_ACCELERATOR into resource tree. Then add following table.

ACCELERATORTABLE label
   ; %1 = ascii key or virtual key,  %2 = ID of key,  %3 = flags
   ACCELERATOR 'A', ID_ACTION_SHIFT_A, FSHIFT
   ACCELERATOR VK_F5, ID_ACTION_CONTROL_F5, FCONTROL | FVIRTKEY
   ACCELERATOR VK_RETURN, ID_ACTION_ALT_ENTER, FALT | FVIRTKEY

   ; default flag is shift key
   ACCELERATOR 'H', IDM_FILE_HELP
ENDACCELERATORTABLE

:top:BITMAP MACRO

With BITMAP macro you can include bitmaps into your resources. Then you can use them inside asm code with LoadBitmap API. To start with bitmaps first include a resource of type RT_BITMAP into resource tree.

; Resource Tree
RESOURCE
    ; Entry of bitmap in resource tree
    TYPE RT_BITMAP
        ID 100h
            LANG
                LEAF RVA(bitmap_label), SIZEOF(bitmap_label)
            ENDLANG
        ENDID
    ENDTYPE
ENDRESOURCE

; Bitmap
BITMAP bitmap_label, 'bitmap.bmp'