flat assembler
Message board for the users of flat assembler.

Index > Windows > '.reloc' section size alignment bug (with ASLR W7/8/10)

Author
Thread Post new topic Reply to topic
f2065



Joined: 01 Jan 2012
Posts: 11
Location: Russia,Moscow
f2065
The '.reloc' section is used for the ASLR mechanism in Windows 7/8/10.

If the section is incorrect, then the program works, but without ASLR, and therefore it is usually not noticeable that there is a problem.

Conditions for reproducing the bug:
1. The program must be compiled to the base address that is occupied on this computer by some system DLL and used in the program (for example, kernel32.dll or user32.dll). Using Sysinternals Process Explorer, we look where the system DLL is loaded. Then in ‘format at’ we specify this address and build.

2. The '.reloc' table size must not be a multiple of 4. For example, Virtual Size = 0x0000000E
Under these conditions, the program will not start, and the system will generate an error 0xC0000018.

Below is an example source code to reproduce the bug.
If there 'mov eax, [label2]', then the size of '.reloc' is 0x10 and everything works fine.
And if this 'mov eax, [label2]' is commented out, then the size of '.reloc' will be 0x0E, and if the address matches the system DLL, an error will occur during startup.

Code:
format PE GUI 5.01 NX at 0x771A0000

entry EntryTestASLR32
include '%fasm%\include\win32a.inc'
section '.idata' import data readable writeable
library kernel32,'KERNEL32.DLL', user32,'USER32.DLL'
import user32, MessageBoxA,'MessageBoxA'
import kernel32, ExitProcess,'ExitProcess'

section '.text' code readable executable

EntryTestASLR32:
; mov eax, [label2]
 invoke MessageBoxA, 0, mb_text, 0, 0
 invoke ExitProcess, 0

section '.data' data readable writeable
mb_text db "text",0
label2 dd 0

section '.reloc' data readable discardable fixups
    

In PE64, the situation is similar, if the base address matches the system DLLs and the size of '.reloc' is not a multiple of 4 - error 0xC0000018.


Description:
Filesize: 68.47 KB
Viewed: 421 Time(s)

reloc_size_bug_1.png


Post 31 Jul 2022, 16:03
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 595
Location: Russia
macomics
Post 31 Jul 2022, 17:36
View user's profile Send private message Reply with quote
f2065



Joined: 01 Jan 2012
Posts: 11
Location: Russia,Moscow
f2065
This is a different situation. There is nothing on the link related to the bug I described. And the error codes are different.
Post 31 Jul 2022, 17:43
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 3444
Location: vpcmipstrm
bitRAKE
Not a problem on Win11, afaict.
(Relocation happens as expected.)

_________________
¯\(°_o)/¯ unlicense.org
Post 01 Aug 2022, 01:07
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18727
Location: In your JS exploiting you and your system
revolution
Does this also affect DLLs? The relocation mechanism is the same for both.
Post 01 Aug 2022, 08:21
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8016
Location: Kraków, Poland
Tomasz Grysztar
revolution wrote:
Does this also affect DLLs? The relocation mechanism is the same for both.
ASLR is a bit more fussy, as mentioned in my notes from Twitter. I have been demonstrating a trick that exploited the fact that ASLR may be refused even when Windows is otherwise perfectly capable of relocating given image. This allows to enforce applying fixups with a predictable base, an opportunity for some obfuscation games, etc.

But ASLR not being applied usually did not mean that the image cannot be mapped at all - in fact, the trick relied on having the image properly relocated and executed, but with ASLR disabled. So perhaps the problem is not with ASLR, but with image mapping process, in which case I would suspect it might also affect DLLs.

If the size of fixups causes issues, you can adjust it manually with additional IF and DW, just like we do it to enforce non-zero length of fixups for some versions of Windows. Anything you put into section marked as FIXUPS (or into DATA FIXUPS block) is appended on top of the fixups directory data.
Post 01 Aug 2022, 10:37
View user's profile Send private message Visit poster's website Reply with quote
f2065



Joined: 01 Jan 2012
Posts: 11
Location: Russia,Moscow
f2065
bitRAKE wrote:
Not a problem on Win11, afaict.
(Relocation happens as expected.)
Checked in vmware on clean installs with all updates:

WinVistaSp2x64 with all updates - affected
Win7sp1x32 with all updates - affected
Win8.1x64 with all updates - affected
Win10x32 21H2 build 19044.1865 with all updates - not affected
Win11x64 21H2 build 22000.832 with all updates - not affected
Windows Server 2022 Standard 21H2 build 20348.587 - not affected



Tomasz Grysztar wrote:
Anything you put into section marked as FIXUPS (or into DATA FIXUPS block) is appended on top of the fixups directory data.

Does not work. It seems that for ASLR it is not the size of the entire section that is critical, but the size of the table in the section.

I adjust the section size by adding the required amount of DW:
Code:
section '.reloc' data readable discardable
data fixups
dw 0
end data    

I make the section size in this way, for example 0x0000000C
But it doesn't help, error on startup.
In Ghidra, I see that in '.reloc' there is a link to the table, there is a real value in table+4 (for example, 0x0000000A), I fix it to 0x0000000C - and the program starts normally.
Post 01 Aug 2022, 16:11
View user's profile Send private message Visit poster's website Reply with quote
f2065



Joined: 01 Jan 2012
Posts: 11
Location: Russia,Moscow
f2065
A more informative demonstration of the work of ASLR.
What should be written in '.reloc'-section to make it work (in Win7/8.1)?
Now, if the address matches with kernel32 - error 0xC0000018…

Code:
format PE GUI 5.01 NX at 0x10000000


; kernel32.dll base address on my virtual computers:
; 0x7C800000 - WinXPx32 - no ASLR
; 0x76F60000 - WinVistaSp2x64 with all updates - affected
; 0x75DA0000 - Win7sp1x32 with all updates - affected
; 0x771A0000 - Win8.1x64 with all updates - affected
; 0x75AF0000 - Win10x32 21H2 build 19044.1865 with all updates - not affected
; 0x76630000 - Win11x64 21H2 build 22000.832 with all updates - not affected
; 0x75BD0000 - Windows Server 2022 Standard 21H2 build 20348.587 with all updates - not affected


entry EntryTestASLR32

include '%fasm%\include\win32a.inc'

section '.idata' import data readable writeable

library kernel32,'KERNEL32.DLL',\
        user32,'USER32.DLL',\
        shlwapi,'SHLWAPI.DLL'

import  user32,\
        MessageBoxA,'MessageBoxA'
import  kernel32,\
        GetModuleHandleA,'GetModuleHandleA',\
        GetModuleFileNameW,'GetModuleFileNameW',\
        CreateFileW,'CreateFileW',\
        GetFileSizeEx,'GetFileSizeEx',\
        LocalAlloc,'LocalAlloc',\
        LocalFree,'LocalFree',\
        ReadFile,'ReadFile',\
        CloseHandle,'CloseHandle',\
        ExitProcess,'ExitProcess'
import  shlwapi,\
        wnsprintfA,'wnsprintfA'

section '.text' code readable executable

EntryTestASLR32:

; enable/disable this line for change '.reloc' size
        mov     eax, this_label_for_align_reloc
; enable - reloc size invalid
; disable - reloc size normal


        invoke  GetModuleHandleA, t_kernel32
        mov     [kernel32_address], eax 

        invoke  GetModuleHandleA, 0
        mov     [current_address_MEM], eax

        stdcall get_addr_from_PE_header
        mov     [current_address_PE], eax

        mov     eax, [current_address_PE]
        test    eax, eax
        jz      aslr_fail
        cmp     eax, [current_address_MEM]
        je      aslr_fail
        cinvoke wnsprintfA, text_buffer, size_text_buffer, t_mask_OK, [kernel32_address], [current_address_MEM], [current_address_PE]
        jmp     view_res
        
aslr_fail:
        cinvoke wnsprintfA, text_buffer, size_text_buffer, t_mask_ERR, [kernel32_address], [current_address_MEM], [current_address_PE], [kernel32_address]
        
view_res:
        invoke  MessageBoxA, 0, text_buffer, mb_header, 0
        invoke  ExitProcess, 0




proc get_addr_from_PE_header
locals
 dqFileSize dq ?
 dwTemp dd ?
 filename dw MAX_PATH dup (?)
endl
        push    edi
        push    esi
        push    ebx

        lea     edx, [filename]
        invoke  GetModuleFileNameW, 0, edx, MAX_PATH

        lea     ecx, [filename]
        invoke  CreateFileW, ecx, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
        cmp     eax, INVALID_HANDLE_VALUE
        je      .err0
        mov     ebx, eax ; hFile

        lea     esi, [dqFileSize]
        mov     dword [esi], 0
        mov     dword [esi+4], 0
        invoke  GetFileSizeEx, ebx, esi
        test    eax, eax
        jz      .err0
        cmp     dword [esi+4], 0
        jnz     .err0
        mov     esi, [esi+0]
        test    esi, esi
        jle     .err0

        invoke  LocalAlloc, LPTR, esi
        test    eax, eax
        jz      .err0
        mov     edi, eax

        lea     edx, [dwTemp]
        invoke  ReadFile, ebx, edi, esi, edx, 0
        test    eax, eax
        jz      .err0
        invoke  CloseHandle, ebx

        lea     ecx, [edi + 0x3C] ; IMAGE_DOS_HEADER.e_lfanew
        mov     ecx, [ecx]
        add     ecx, edi

        lea     ecx, [ecx + 0x34] ; IMAGE_NT_HEADERS32.OptionalHeader.ImageBase
        mov     esi, [ecx]
        
        invoke  LocalFree, edi
        mov     eax, esi
        jmp     .return

.err0:  xor     eax, eax

.return:
        pop     ebx
        pop     esi
        pop     edi
        ret
endp


section '.data' data readable writeable
kernel32_address dd ?
current_address_MEM dd ?
current_address_PE dd ?
this_label_for_align_reloc dd ?

t_kernel32 db "kernel32.dll",0

mb_header db "ASLR test2",0

t_mask_OK db    "kernel32 address in memory: 0x%08X",13,10,\
                "current process address in memory: 0x%08X",13,10,\
                "base address in the PE-header of the current process: 0x%08X",13,10,13,10,\
                "ASLR test result: OK",0

t_mask_ERR db   "kernel32 address in memory: 0x%08X",13,10,\
                "current process address in memory: 0x%08X",13,10,\
                "base address in the PE-header of the current process: 0x%08X",13,10,13,10,\
                "ASLR test result: Change the address in the first line of the source to '0x%08X' and rebuild the program.",0

size_text_buffer = 2048
text_buffer rb size_text_buffer

section '.reloc' data readable discardable fixups

    
Post 01 Aug 2022, 17:04
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8016
Location: Kraków, Poland
Tomasz Grysztar
I think it is a mistake to frame it as an ASLR-related issue, when the actual problem is that fixups fail to be applied when relocation is necessary because of conflicting base address. These are separate things, and failing ASLR normally does not prevent the program from starting. If the base of image is already reserved/used for other purpose, then relocation is always needed, and this applies to all versions of Windows since the PE format was introduced, way before ASLR. See my video about Win32s for example.

f2065 wrote:
In Ghidra, I see that in '.reloc' there is a link to the table, there is a real value in table+4 (for example, 0x0000000A), I fix it to 0x0000000C - and the program starts normally.
This is not the total size of the fixups structure, only the length of a single block of relocations, corresponding to a single page within the address space that needs to be patched. See the corresponding chapter in my PE tutorial to learn more. You can also use the templates constructed there to perform better tests, as these examples allow a complete control over all the header fields (as long as you're familiar with fasmg).

The PE specification states that each relocation block needs to start on a 32-bit boundary, which is not the same as each block needing to have an aligned size, but in practice this only makes a difference in the final block. The implementation in fasm 1 took the specification quite literally - not aligning the length of the final block.

But I'm pretty sure fasmg's PE formatter does align all the fixups block sizes, including the final one. If you move to fasmg, the issue may no longer be a concern. The formatter in fasm 1 is mostly preserved in a backward-compatible state, while with fasmg I could take a more liberal approach, considering that the formatter that is in form of an .INC file can be easily adjusted.
Post 01 Aug 2022, 18:21
View user's profile Send private message Visit poster's website Reply with quote
f2065



Joined: 01 Jan 2012
Posts: 11
Location: Russia,Moscow
f2065
Tomasz Grysztar wrote:
The PE specification states that each relocation block needs to start on a 32-bit boundary, which is not the same as each block needing to have an aligned size, but in practice this only makes a difference in the final block. The implementation in fasm 1 took the specification quite literally - not aligning the length of the final block.


However, for Windows 7/8.1 it is necessary that the size of this data be a multiple of 4 (and also the minimum section size is 8).

There is no bug in fasmg, it makes the block size up to 4 - I checked by adding and removing lines that affect the number of relocations.
But switching to fasmg is too complicated, and for example it is not clear how to make a .fas file (and whether it will be compatible?) - there is no -s switch ...


Is it possible to somehow solve this problem (with the size) in fasm1? Add some new argument for size alignment?


So far I have come up with only such a solution, but it's not right:
Code:
section '.reloc' data readable discardable fixups
if (($ - $$) mod 4) > 0
 display "invalid fixup size!",13,10
 err
end if    
Post 01 Aug 2022, 22:37
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18727
Location: In your JS exploiting you and your system
revolution
If fasm is updated with a fix what is a good way to pad the reloc section?
Post 02 Aug 2022, 05:42
View user's profile Send private message Visit poster's website Reply with quote
I



Joined: 19 May 2022
Posts: 31
I
If the upper 4 bits of the word are zero it's recognized as padding, no?

https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-reloc-section-image-only

Quote:
IMAGE_REL_BASED_ABSOLUTE | 0 | The base relocation is skipped. This type can be used to pad a block.
Post 02 Aug 2022, 06:37
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8016
Location: Kraków, Poland
Tomasz Grysztar
revolution wrote:
If fasm is updated with a fix what is a good way to pad the reloc section?
See the example in my PE tutorial. fasmg's formatter does the same (but with CALM-based implementation). The difference from fasm 1 is that all blocks are padded and not just the ones that need to be padded to start the next block on an aligned boundary (which is technically what the specification requests).
Post 02 Aug 2022, 07:20
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.