Показаны сообщения с ярлыком arm64. Показать все сообщения
Показаны сообщения с ярлыком arm64. Показать все сообщения

среда, 8 мая 2024 г.

asm injection stub

Lets check what this stub should do being injected in some linux process via __malloc_hook/__free_hook (btw this implicitly means than you cannot use this dirty hack for processes linked with musl or uClibc - they just don't have those hooks)
  • bcs our stub can be called from two different hooks we should store somewhere via which entry point we was called
  • restore old hooks values
  • call dlopen/dlsym and then target function (and pass it address of injection stub for delayed munmap. No, you can't free those memory directly in your target function - try to guess why)
  • get right old hook and jump to it if it was installed or just return to code called __malloc_hook somewhere in libc

So I collected all parameters to do job in table dtab consisting from 6 pointers

  1. __malloc_hook address
  2. old value of __malloc_hook
  3. __free_hook address
  4. old value of __free_hook
  5. pointer to dlopen
  6. pointer to dlsym
after those table we also has couple of string constants for injected.so full path and function name. Also bcs we must setup 2 entry point I decided to put 1 byte with distance between first and second (to make injection logic more universal) right after dtab. Sounds easy, so lets check how this logic can be implemented on some still living processors (given that RIP alpha, sparc, hp-pa etc)

вторник, 29 ноября 2022 г.

linux drivers cross-compilation

Just reminder for myself how to build driver for arm64 having x64 based machine with ubuntu

Install right gcc

for arm64 we need gcc-aarch64-linux-gnu:

sudo apt-get install gcc-aarch64-linux-gnu

Build Kernel

You cannot use installed kernel and must build one for appropriate architecture - in my case for arm64 (note - gcc has prefix aarch64, C - consistency). Clone or unpack kernel source tree to some directory KROOT and then

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules

Patch Makefile

Usual trick is to use something like

MACHINE ?= $(shell uname -m)
ifeq ($(MACHINE),x86_64)

but this gives you arch of host machine, so you must rewrite all such cases to use ARCH variable (and to setup make -C $(KROOT))

Building

and finally you can cross-compile your driver with something like

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -f Makefile.arm64

понедельник, 31 октября 2022 г.

BTI incompatible exported functions in kernel 5.15.0-53

if BTI is enabled, the first instruction encountered after an indirect jump must be a special BTI instruction

from here

I downloaded Ubuntu for arm64 (jammy-desktop-arm64.iso) and decided to check if there are some functions with don`t contain BTI c at start

17804 such functions. System.map-5.15.0-53-generic contains 62819 functions in total. Next I just intersected them with exported - 1269

This is obvious bug - maybe in gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0

at least some of this functions are really important - like register_ftrace_function

пятница, 27 августа 2021 г.

arm64 disasm for linux kernel

I added today disassembler for arm64 linux kernel to search pointers. It turned out to be surprisingly difficult to do for several reasons (disasm for x64 is only 383 LOC vs 618 for arm64)

One of them is poor code produced by some gcc versions

But the main problem is arm64 opcodes. Lets see simple indirect call:
  ADRP            X27, #mh_filter@PAGE
  CMP             W22, #0x3A ; ':'
  B.EQ            loc_FFFFFFC010CC7140
  CMP             W22, #0x87
  B.NE            loc_FFFFFFC010CC7188
  LDR             X2, [X27,#mh_filter@PAGEOFF]
  CBZ             X2, loc_FFFFFFC010CC7188
  MOV             X1, skb
  MOV             X0, X28
  BLR             X2
    

compare this with code to call list of funcs from tracepoints:
  ADRP            __data, #__tracepoint_cpu_idle@PAGE
  ADD             X0, X0, #__tracepoint_cpu_idle@PAGEOFF
  MOV             X29, SP
  STR             X19, [SP,#var_s10]
  LDR             X19, [X0,#(__tracepoint_powernv_throttle.funcs - 0xFFFFFFC011A562C0)]
 ...
loc_FFFFFFC01011FC60:
  LDR             X4, [X19]
  MOV             W3, W20
  LDR             X0, [X19,#8]
  MOV             X2, X21
  MOV             W1, W22
  BLR             X4
  LDR             X0, [X19,#0x18]!
  CBNZ            X0, loc_FFFFFFC01011FC60

In second case register X4 was loaded from X19, which in turn was loaded from some memory, so I need to track how many times content of register was loaded

Anyway results is +34 newly discovered functions pointers

четверг, 18 февраля 2021 г.

poorgcc: IDA Pro plugin to fix poor gcc code on arm64

Lets see what generates gcc for arm64 - for example gcc7.5 and linux kernel
Function do_sysinstr:

ADRP            X0, #__func__.48604@PAGE ; "arm64_show_signal"
ADD             X0, X0, #__func__.48604@PAGEOFF
ADRP            X3, #ctr_read_handler@PAGE
ADD             X0, X0, #0x218
ADD             X3, X3, #ctr_read_handler@PAGEOFF

Wtf happened here? Instead of loading x0 with address of sys64_hooks we have two consecutive loads and no value x0 used between. You can peek some random functions - this is very common pattern, I personally think this is bug in gcc arm64 codegen. Anyway, it does not allow to see right xrefs so I wrote simple plugin for IDA Pro to fix this

Plugin just try to find instructions "add add reg, reg, imm" without data xref and backtrack if this register was loaded somewhere above - sure code is not sample of elegance. You can add to plugins.cfg string like this

process_all_poor_gcc_functions    poorgcc64     0      1

to process all functions

Some results - after applying plugin to function do_sysinstr code looks like:

ADRP            X0, #__func__.48604@PAGE ; "arm64_show_signal"
ADD             X0, X0, #__func__.48604@PAGEOFF
ADRP            X3, #ctr_read_handler@PAGE
ADD             X0, X0, #0x218 ; FFFFFFC010C116C8
ADD             X3, X3, #ctr_read_handler@PAGEOFF


FFFFFFC010C116C8 is address of sys64_hooks and now it has right xref

понедельник, 15 февраля 2021 г.

fsm rules for rpcrt4!GlobalRpcServer

I already described how you can extract address of GlobalRpcServer and offset to some RPC_SERVER_T fields. Lets do it for arm64 in declarative manner using FSM

Start again with I_RpcServerRegisterForwardFunction function - we can get address of RpcHasBeenInitialized (will be stored with index 1), GlobalRpcServer (with index 2) and RPC_SERVER_T.pRpcForwardFunction offset (with index 3):

section .data
func I_RpcServerRegisterForwardFunction
# 1 - RpcHasBeenInitialized
stg1 load
# 2 - GlobalRpcServer
stg2 load
# 3 - ForwardFunction offset
stg3 strx

Next we can get size of RPC_SERVER_T - from function InitializeRpcServer as argument to AllocWrapper. But InitializeRpcServer is surprisingly hard to find - it is not exported and called one time from InitializeServerDLL (which also non-exported). It using lots of unicode strings but unfortunately they all have common prefix "NT AUTHORITY" what makes them indistinguishable for signature 16 bytes. But you can notice that inside this function registering some RPC_SERVER_INTERFACE - so we can use its content as GUID: 

понедельник, 8 февраля 2021 г.

fsm rules syntax

I added saving and loading of FSM rules in file - so now you can edit them (or perhaps even write new manually) and then apply with new tool afsm. So lets see how it works

  1. We must make functions distinguishable. Functions must be either exported or contain loading of some constant - from constant pool or from .rdata section
  2. Then this functions disassembling and FSM rules applied to code-flow graph. There may be several results, so I added global storage - it can be accessed by index from any rules (but sure this storage belongs to each processed file). Storage logic cannot be auto-derived so you should write such rules manually - storing states must have "stg" prefix with index
Each rule starts with "section" keyword - it is section where located address which you want to find (you can use comments starting with '#'). Then you must pick function. If functions is exported it`s easy - "func" export_name, if not - just pick section where this function located with "fsection" section_name
Then follow one or more states of FSM:
  • load - loading from "section". Can have prefix stg N to remember this address
  • store - storing to "section". Can have prefix stg N to remember this address
  • ldrb - like "load" but for 1 byte
  • ldrh - like "load" but for 2 bytes
  • strb - like "store" but for 1 byte
  • strh - like "store" but for 2 byte
  • gload index - load address from storage with index
  • gstore index - store to address from storage with index
  • const - load some constant from constant pool
  • rdata - load some 8 byte constant from .rdata section
  • guid - load 16 byte guid from .rdata section. Actually rdata and guid could be one state with variable size but I am too lazy
  • call_imp - call some imported function from IAT
  • call_dimp - call some function from delayed IAT
  • call_exp - call exported function
  • call - just some call, perhaps located in specific section. Can have prefix stg N to remember this address
  • gcall index - call function with address in storage

Lets see example - say we want to find MCGEN_TRACE_CONTEXTs in kernel - registered with non-exported function McGenEventRegister_EtwRegister, There are 3 functions where this call occurs:
  • FsRtlpHeatRegisterVolume
  • IoInitSystemPreDrivers
  • PnpDiagInitialize
none of them are exported. Try write rules for them

вторник, 26 января 2021 г.

auto-derived FSM for usermode dlls

As expected results of auto-derived FSM for usermode dlls are much worse - for example on rpcrt4.dll can be found only 76 symbols from 228. It's because code in usermode contains much fewer unique constants (like NTSTATUS or allocation tags in kernel). So we need to use some additional data to make edges more distinguishable. Lets consider several candidates

load_config

Contains addresses of SecurityCookie and ptrs to GuardCFCheckFunctionPointer & GuardCFDispatchFunctionPointer. At least knowing SecurityCookie we can distinguish loading of some address in .data section from loading of cookies in prolog/epilogue of functions. But results are almost the same - 78 from 228

delayed import

New source of data missing in kernel mode. So I added new state to FSM - call_dimp, almost the same as call_imp but for delayed IAT. As expected results have grown - 109 from 228

constants in .rdata section

arm64 code can use not only ldr from constant pool but regular const data in .rdata section - for example strings for GetProcAddress etc. Lets see how looks such code:

четверг, 14 января 2021 г.

using of auto-derived state machines

Let`s see what we can do with our auto-derived state-machines. All source code in my github repo

Simple case: KdLocalDebugEnabled

Assume that we want to find address of KdLocalDebugEnabled. On kernel 18345 RVA is 37CC18 and it located in section .data. Run
ldr.exe -se -t 8 -der D:\work\kernel\w10\18346\arm\ntoskrnl.exe 37CC18
to build rules. Option -t sets number of threads. Results:

found at 0076D850 - KdSystemDebugControl
 ldrb exorted KdDebuggerEnabled
 ldrb
apply return 37CC18, must_be 37CC18

This rule say that we must find exported function KdSystemDebugControl, wait for loading of exported symbol KdDebuggerEnabled and next loading operation will give us address of KdLocalDebugEnabled
Now apply this rule for kernel RTM 2004 (with option -T you can specify files on which to test rules):
ldr.exe -se -t 8 -der D:\work\kernel\w10\18346\arm\ntoskrnl.exe 37CC18 -T d:\work\kernel\w10\rtm\2004\arm\ntoskrnl.exe
 ldrb exorted KdDebuggerEnabled
 ldrb
Test[0]: C3F639

Lets check this address
// pubsym <rva 0xc3f639> KdLocalDebugEnabled

Second case: CmpTraceRoutine

IDA Pro shows 106 xrefs on kernel 18345, RVA is 8A8008. Lets see if rule for finding this address can be derived automatically:

воскресенье, 10 января 2021 г.

efficiency of auto-derived state machines

It`s time to measure how effective this state-machines. I made today simple perl script to measure how much symbols (located in sections .data, ALMOSTRO and PAGEDATA) can be found for arm64 windows kernel. The conditions for success are

  • found function is exported
  • or found function use some unique constant which is used no more than 3 times
Result on kernel build 18346:
total: 3493 symbols, found 1466

Simple state machine with states containing only loading/storing, call import/export and loading of some constant is able to retrieve almost 42% of symbols

PS: for adf.sys (which has no exported functions at all) results even better:
total: 164 symbols, found 73
44.5%

пятница, 8 января 2021 г.

(semi)auto building of state machine

Several days ago I made PoC to extract addresses of WSK data from windows 10 arm64 afd.sys - specifically AfdWskClientListHead and lock AfdWskClientSpinLock. Nothing special except fact that afd.sys has no exported functions. So you must find some rare constant, then find functions which use it and only then do some disasm applying state machine to each code block (see lambda passed to traverse_simple_state_graph)

While I was writing this code, I was not left with a question whether it is possible to employ computer to build such state machines. And now I know that this is possible (at least for code on plain C for RISC-like asm with predictable addresses of instructions etc etc)

Lets see how such algo can be arranged:

1) you must find all cross-refs to desired variable and collect list of functions which use it (exactly what deriv_hack::find_xrefs method does)

2) then you must disasm each such function and try to get some primitives - like loading of constants, calling imported/exported functions etc - see deriv_hack::make_path method. Sure set of this primitives will be different for each processor and perhaps will depends from your tasks

Results for afd.sys!AfdWskClientListHead:

вторник, 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

and
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

суббота, 18 апреля 2020 г.

PsKernelRangeList on arm64 kernel

can be found using the same old trick
Sure constants are now different, so now KUSER_SHARED_DATA.SystemCall is 0xFFFFF78000000308 and KUSER_SHARED_DATA.ProcessorFeatures is 0xFFFFF78000000274

Commited today simple logic to find and parse it in my armpatched

пятница, 17 апреля 2020 г.

IDA Pro plugin for arm64 switch tables processing

IDA Pro supports arm64 very poorly - it also cannot parse switch tables. Let`s see how they looks on arm64 - for example in function NtQueryInformationThread:
 CMP             W1, #0x2D           ; check index
 B.HI            loc_140673294
 ADR             X9, dword_14066E9EC ; switch tab address
 LDRSW           X8, [X9,W1,UXTW#2]  ; index in W1 << 2
 ADR             X9, loc_14066E358   ; base address
 ADD             X8, X9, X8,LSL#2    ; base address + offset << 2
 BR              X8

What happens here? first "ADR x9, addr" loads address of switch table
Next LDRSW is like "mov x8, [x9 + 4 * w1]" on Intel - load DWORD at x9 + index w1 left shifted by 2
Then second ADR loads address of base for this switch table
ADD x8, x9, x8 << 2 sets in x8 address of actual jumps
and finally BR go to this address

So I just wrote quick and dirty plugin arm64sw.p64 based on armpatched for switch tables processing

четверг, 16 апреля 2020 г.

KiTpExcludedRoutines

As you can guess from name this is array of functions for which you can`t set kernel tracepoint. Curious that this lists differs in x64 and arm64
x64

воскресенье, 12 апреля 2020 г.

bug in ida pro arm64 module

Lets see in ida pro some arm64 windows kernel, for example good old function PspSetCreateThreadNotifyRoutine:
 ADRP            X8, #PspNotifyEnableMask@PAGE
 ADD             X11, X8, #PspNotifyEnableMask@PAGEOFF
 TBNZ            W20, #0, loc_140690960
 ADD             X10, X11, #0x33C


register x11 contains address of PspNotifyEnableMask - in my case this is 0x1408AE6B0 and then x10 loading address of PspNotifyEnableMask + 0x33c = 0x1408AE9EC - this is actually PspCreateThreadNotifyRoutineCount. And no - you cannot fix last instruction with pressing O or Ctrl + O
Given that cross-refs in arm64 is highly dependent from correct code analysis - this is very annoing
Tested in ida pro 6.9 and 7.2

четверг, 9 апреля 2020 г.

armpatched

Several days ago I started my new pet project on GitHub, bcs
  • quarantine is boring
  • reading a book "ARM 64-Bit Assembly Language" without practice is useless
So I just forked arm64 disasm called armadillo, ported it on windows, added naïve pe loader (btw attempt to use MapViewOfFile function was unsuccessful with GetLastError 1132) and today add some practical usage of static code analysis to extract lists and lock of lookaside lists from arm64 windows kernel

Main magic happens in ntoskrnl_hack::find_lock_list function