суббота, 11 июля 2020 г.

what`s wrong with Etw

Disclaimer: as I am aware that the given code examples can be dangerous for Etw-based EDR products - all code was made for least popular version of windows - for arm64

Let's assume that we have some application that wants to hide its activity from trace logs - not necessary evil or malicious, for example just to hide used algos or bit paranoid like crypto-wallet. Lets see how can it achieve this (I have no desire to consider trivial cases like removing records from eventlog)

Semiofficial ways

  1. Sure all you readed about COMPlus_ETWEnabled but there is also promising COMPlus_ETWFlags 
  2. You can switch off etw tracing for services.exe with registry key TracingDisabled in Software\Microsoft\Windows NT\CurrentVersion\Tracing\SCM\Regular
  3. And the same for rpcrt4.dll with registry key ExtErrorInformation in HKLM\Software\Policies\Microsoft\Windows NT\Rpc
Actually there are virtually countless ways to do it. And many perhaps not documented bcs was written in Ms by some poor intern who was kicked out in the cold after another review 10+ years ago. I struggled with temptation to make clickbait caption like "99% of windows dlls can disable etw logs" but it`s close to the truth


вторник, 30 июня 2020 г.

search references in arm64 code

Let's assume that you want to find address of non-exported CrashdmpCallTable in kernel. Usual old school way is to find unicode string "\SystemRoot\System32\Drivers\crashdmp.sys" and then to find reference to in .text section - if you are lucky enough this will be somewhere in middle of function IopLoadCrashdumpDriver. But on arm64 loading of address splitted in pair of instructions and even worse - values depends from address of instruction. Let`s see how it looks:

.text:00000001401AF700 6A 01 00 90     ADRP  X10, #aSystemrootSy_0@PAGE ; "\\SystemRoot\\System32\\Drivers\\crashd"...
.text:00000001401AF704 48 C1 09 91     ADD   X8, X10, #aSystemrootSy_0@PAGEOFF ; "\\SystemRoot\\System32\\Drivers\\crashd"...

Output from cmdline disasm for each of opcodes:
armpatched.exe 6A010090
Disassembled: adrp x10, 2c000

        This instruction is AD_INSTR_ADRP and is part of group AD_G_DataProcessingImmediate
        This instruction has 4 decode fields (from left to right):
                0x1, 0, 0xb, 0xa
        This instruction has 2 operands (from left to right):
                This operand is of type AD_OP_REG
                        Register: x10 size 40
                This operand is of type AD_OP_IMM
                        Immediate type: AD_IMM_ULONG
                        Value: 0x2c000

armpatched.exe 48C10991
Disassembled: add x8, x10, #0x270

        This instruction is AD_INSTR_ADD and is part of group AD_G_DataProcessingImmediate
        This instruction has 7 decode fields (from left to right):
                0x1, 0, 0, 0, 0x270, 0xa, 0x8
        This instruction has 3 operands (from left to right):
                This operand is of type AD_OP_REG
                        Register: x8 size 40
                This operand is of type AD_OP_REG
                        Register: x10 size 40
                This operand is of type AD_OP_IMM
                        Immediate type: AD_IMM_ULONG
                        Value: 0x270

What are the options?

пятница, 19 июня 2020 г.

sve/sve2 instructions for arm64

Today I finished adding of sve/sve2 instructions to my pet-project armpatched
I used as reference ISA_A64_xml_futureA-2019-09_OPT.pdf
Sure code is full of bugs, incomplete and has terrible style - all what you can expect from 7KLOC on plain C
Known problems:
  • since my main goal was code analysis disasm strings looks not very good - for example I totally emit register size suffixes (although size storing in op_reg.sz)
  • I don`t know for what used "fields" so they filled in voluntary random order
Zx registers marked as floating point with op_reg.fp == 2 and Px registers with op_reg.fp == 3
Code was tested on all possible instructions and looks like it does not crash. Bcs it seems that some instructions use floating point you must use KeSaveFloatingPointState/KeRestoreFloatingPointState in case of kernel mode

пятница, 12 июня 2020 г.

arm64 pc-relative literals

Lets see for example non-exported function function SepInitializeCodeIntegrity from arm64 kernel:
  STP             X19, X20, [SP,#-0x20+var_10]!
  STP             X21, X22, [SP,#0x10+var_s0]
  STR             X23, [SP,#0x10+var_s10]
  STP             X29, X30, [SP,#0x10+var_20]!
  MOV             X29, SP
  ADRP            X8, #SeCiCallbacks@PAGE
  ADD             X20, X8, #SeCiCallbacks@PAGEOFF
  ADD             X8, X20, #4
  MOV             W21, #6
  ADD             X9, X8, #0xC0

loc_1406A9C90                           ; CODE XREF: SepInitializeCodeIntegrity+30 j
  STP             XZR, XZR, [X8],#0x10
  CMP             X8, X9
  B.NE            loc_1406A9C90
  STR             WZR, [X8]
  MOV             W9, #0xD0
  LDR             X8, =0xA000007

qword_1406A9D30 DCQ 0xA000007

As you can see all DWORD constants under arm64 stored in some place after function, so you sure can find address 1406A9D30 but how to find function which using this constant? Instruction ldr can be located in range of 1Mb from this address

вторник, 9 июня 2020 г.

undocumented env vars in mscorwks.dll

There are lots of env vars with prefix COMPlus_
So lets see only not presented in this document

Main function constructing name of env var is ?EnvGetString@REGUTIL@@SAPEAGPEBGH@Z called from:
  • ?UnsafeGetConfigDWORD@@YAKPEBGK@Z
  • ?GetConfigDWORD@EEConfig@@SAKPEBGKKHW4ConfigSearch@1@@Z
  • ?GetConfigString@EEConfig@@SAJPEBGPEAPEAGHW4ConfigSearch@1@@Z
  • ?init@ConfigDWORD@@AEAAXPEAGK@Z
  • ?GetConfigString@REGUTIL@@SAPEAGPEBGHW4CORConfigLevel@1@@Z

and perhaps from several thousand more places so I wrote some script to collect names and filter out known. Final list is:
  • AllowStrongNameBypass - DWORD
  • AssemblyUsageLogImpersonateUser - DWORD
  • BBSweep - DWORD
  • BreakOnAV - DWORD
  • bypassTrustedAppStrongNames - DWORD
  • Cor_Enable_Profiling - DWORD
  • DbgJITDebugLaunchSetting - DWORD
  • DbgManagedDebugger - string
  • DbgThreadCreateOption - DWORD
  • DisableDoubleMapping - DWORD
  • DisableSecurityTransparency - DWORD
  • EagerWatsonBuckets - DWORD
  • EHGolden - DWORD
  • JumpStubReserve - DWORD
  • legacyAssemblyProvidedEvidence - DWORD
  • legacyHMACWarning - DWORD
  • legacyMyComputerZone - DWORD
  • legacyV1CASPolicy - DWORD
  • LOPFriendlyFrames - DWORD
  • MD_TrackColDesMisses - DWORD
  • MemoryReport - DWORD
  • MemoryReportBreakOnContext - string
  • MemoryReportOutput - string
  • MemoryReportStackFrames - DWORD
  • MemoryReportSweepInterval - DWORD
  • MultiDomainHostGAC - DWORD
  • NGenAllowMsiInstall - DWORD
  • PrecodeInCodeHeap - DWORD
  • ReaderWriterLock_UseFixedIdGeneration - DWORD
  • ShowMetaDataAccess - DWORD
  • StressLoadThreadCount - DWORD
  • useEnUSLcidWithComInvoke - DWORD
  • UseFileMappingForByteArrays - DWORD
  • UseMethodDataCacheInFindDispatchImpl - DWORD
  • UsePcRel32Calls - DWORD

понедельник, 8 июня 2020 г.


There is nice trick to hide that your .NET assemblies does not have ETW logging
Lets see if we can detect this not from environment vars
ETW logging inited in mscorwks.dll!CEtwTracer::Register:

четверг, 21 мая 2020 г.

kernel pte in windows 10 64bit

Quarantine is a good time to re-read some old but useful papers and check if you can catch trick with making KUSER_SHARED_DATA writable again So lets see what we need:
  • some logic to extract PTE base from MiGetPteAddress
  • primitive to check if some address is valid - I used MmIsAddressValid
  • primitive to read page from kernel memory
Start with some system info:
pSystemRangeStart: FFFF800000000000
PTE base address: FFFF880000000000
PTE shift: 9

Do some calculation
function to get offset to PTE looks like:
return (address >> 9) & 0x7FFFFFFFF8;

for pSystemRangeStart PTE will be
4000000000 + pte_base = FFFF884000000000
You may notice that this address located above pte_base

for last available address in system FFFFFFFFFFFFFFFF PTE will be
7FFFFFFFF8 + pte_base = FFFF887FFFFFFFF8

Also we need some inverse function to convert from PTE to page address. First we need sub pte_base, then divide on sizeof(MMPTE_HARDWARE) and then multiply to size of page. And add some base address. Try to calculate this base for example for PTE of pSystemRangeStart FFFF884000000000:
(FFFF884000000000 - FFFF880000000000) / sizeof(MMPTE_HARDWARE) * 0x1000 = 800000000000

Our base is FFFF800000000000 (value of pSystemRangeStart) - 800000000000 = FFFF000000000000

Notice that this base does not match with pSystemRangeStart
So our inverse function may looks like:
return 0xFFFF000000000000 + (ptr_addr - pte_base) / sizeof(MMPTE_HARDWARE) * 0x1000;

Ok, it seems that we have all puzzle pieces and can scan all PTE from FFFF884000000000 to FFFF887FFFFFFFF8. Yes, all 0x4000000000 bytes - 256Gb. Sure, we can wait for results before retirement