Initial commit
This commit is contained in:
commit
5a9c66ea87
2 changed files with 238 additions and 0 deletions
7
build.sh
Executable file
7
build.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
fcx-as -o cause360error.o -mregnames -a32 -mppc64 cause360error.s
|
||||
|
||||
fcx-ld --accept-unknown-input-arch --oformat=pei-i386 --subsystem xbox --major-subsystem-version 1 --minor-subsystem-version 0 --major-os-version 0 --minor-os-version 0 --section-alignment=0x10000 --file-alignment 512 --disable-dynamicbase --image-base 0x82000000 --disable-long-section-names -e _start -o cause360error.exe cause360error.o
|
||||
|
||||
synthxex --skip-machine-check --input cause360error.exe --output cause360error.xex
|
231
cause360error.s
Normal file
231
cause360error.s
Normal file
|
@ -0,0 +1,231 @@
|
|||
# Assembly program to make the Xbox 360 display an error code
|
||||
#
|
||||
# Copyright (c) 2025 Aiden Isik
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
.global _start
|
||||
|
||||
# Code
|
||||
.text
|
||||
|
||||
# Read 32-bit little endian value and byte-swap it
|
||||
# IN: r4 == base address of little endian value
|
||||
# IN: LR == address to return to
|
||||
# OUT: r4 == read and byteswapped value
|
||||
get32BitLittleEndian:
|
||||
# r5 == how many bytes to left shift each value read
|
||||
# r6 == working register used to store currently retrieved bytes
|
||||
# r7 == register to store current byte before shift
|
||||
|
||||
li r5, 24 # Register storing how many bits to shift each value
|
||||
|
||||
# Most significant byte
|
||||
lbz r6, 3(r4)
|
||||
slw r6, r6, r5
|
||||
subi r5, r5, 8
|
||||
|
||||
lbz r7, 2(r4)
|
||||
slw r7, r7, r5
|
||||
subi r5, r5, 8
|
||||
or r6, r6, r7
|
||||
|
||||
lbz r7, 1(r4)
|
||||
slw r7, r7, r5
|
||||
or r6, r6, r7
|
||||
|
||||
# Least significant byte
|
||||
lbz r7, 0(r4)
|
||||
or r6, r6, r7
|
||||
|
||||
mr r4, r6 # Final value
|
||||
blr
|
||||
|
||||
|
||||
# If the function was not found, return 0 (set r3 = 0, then return)
|
||||
# IN: r12 == address to return to
|
||||
# OUT: r3 == 0
|
||||
kernelFunctionNotFound:
|
||||
li r3, 0
|
||||
mtlr r12
|
||||
blr
|
||||
|
||||
|
||||
# So I don't know if this works or not: it turns out the kernel does NOT have an export name table
|
||||
# Keeping this here in case it's useful in the future for something else
|
||||
|
||||
# Getting function address by name
|
||||
# IN: r3 == address of name string of function
|
||||
# IN: LR == address to return to
|
||||
# OUT: r3 == address of kernel function (or 0 if it could not be found)
|
||||
getKernelFunctionAddrByName:
|
||||
mflr r12
|
||||
|
||||
# Getting the address of the PE header
|
||||
lis r4, 0x8004
|
||||
ori r4, r4, 0x3C
|
||||
|
||||
bl get32BitLittleEndian
|
||||
addis r4, r4, 0x8004 # Final address (RVA + kernel base address)
|
||||
|
||||
# Getting the address of the export directory table
|
||||
addi r4, r4, 0x78
|
||||
bl get32BitLittleEndian
|
||||
addis r4, r4, 0x8004 # Final address (RVA + kernel base address)
|
||||
|
||||
# Get number of name pointers (and put it in CTR)
|
||||
mr r8, r4
|
||||
addi r4, r4, 0x18
|
||||
bl get32BitLittleEndian
|
||||
mtctr r4
|
||||
|
||||
# Get addresses of the name pointer table and the ordinal table
|
||||
addi r9, r8, 0x20 # Name pointer table RVA address
|
||||
addi r4, r8, 0x24 # Ordinal table RVA address
|
||||
|
||||
bl get32BitLittleEndian
|
||||
addis r10, r4, 0x8004 # Ordinal table address
|
||||
|
||||
mr r4, r9
|
||||
bl get32BitLittleEndian
|
||||
addis r4, r4, 0x8004 # Name pointer table address
|
||||
|
||||
# At this point, r4 contains the name pointer table address, and r10 contains the ordinal table address
|
||||
# r0 (with care), r8, r9 and r11 are free to use. r5, r6 and r7 are used by get32BitLittleEndian,
|
||||
# r12 is used for our return address, and r3 contains the function name's address.
|
||||
|
||||
# We *could* do a binary search here, since this table is alphabetically sorted.
|
||||
# However, I don't see the marginal speed increase being worth the effort in this case.
|
||||
# We're also looping backwards here, just because it's easier.
|
||||
|
||||
namePointerTableLoop:
|
||||
# Getting the address to the current name and putting it in r8
|
||||
mfctr r8
|
||||
subi r8, r8, 1
|
||||
mulli r8, r8, 4
|
||||
add r8, r4, r8
|
||||
|
||||
li r11, 0
|
||||
functionNameLoop:
|
||||
# Putting characters to compare in r8 and r9, respectively
|
||||
# r11 is our character offset
|
||||
lbzx r8, r4, r11
|
||||
lbzx r9, r3, r11
|
||||
addi r11, r11, 1
|
||||
|
||||
# Checking if characters match, if they don't, move onto the next entry
|
||||
cmpw cr0, r8, r9
|
||||
bne checkNextNameEntry
|
||||
|
||||
# If both characters are \0, we found our function in the table (as the whole string matches)
|
||||
cmpwi cr0, r8, 0
|
||||
beq getCorrespondingOrdinalToNameEntry
|
||||
|
||||
# Next character
|
||||
beq functionNameLoop
|
||||
|
||||
checkNextNameEntry:
|
||||
bdnz namePointerTableLoop
|
||||
|
||||
# If we get here, we didn't find the function name in the table.
|
||||
b kernelFunctionNotFound
|
||||
|
||||
getCorrespondingOrdinalToNameEntry:
|
||||
# At this point, we have our entry to the ordinal table in CTR.
|
||||
# Convert it to the address of the entry in the ordinal table
|
||||
mfctr r8
|
||||
subi r8, r8, 1
|
||||
mulli r8, r8, 4
|
||||
add r4, r8, r10
|
||||
|
||||
# Get the ordinal at that address in the ordinal table
|
||||
bl get32BitLittleEndian
|
||||
|
||||
# Finally, get the address of the function using the ordinal
|
||||
mr r3, r4
|
||||
mtlr r12
|
||||
b getKernelFunctionAddrByOrdinal
|
||||
|
||||
|
||||
# Getting function address by ordinal
|
||||
# IN: r3 == ordinal number
|
||||
# IN: LR == address to return to
|
||||
# OUT: r3 == address of kernel function (or 0 if it could not be found)
|
||||
getKernelFunctionAddrByOrdinal:
|
||||
mflr r12
|
||||
|
||||
# Getting the address of the PE header
|
||||
lis r4, 0x8004
|
||||
ori r4, r4, 0x3C
|
||||
|
||||
bl get32BitLittleEndian
|
||||
addis r4, r4, 0x8004 # Final address (RVA + kernel base address)
|
||||
|
||||
# Getting the address of the export directory table
|
||||
addi r4, r4, 0x78
|
||||
bl get32BitLittleEndian
|
||||
addis r4, r4, 0x8004 # Final address (RVA + kernel base address)
|
||||
|
||||
# Making sure the ordinal exists (ordinal is not greater than or equal to export count)
|
||||
mr r8, r4 # Putting export directory table address into a register unused by get32BitLittleEndian
|
||||
addi r4, r4, 0x14 # Address of export count
|
||||
|
||||
bl get32BitLittleEndian # Get export count
|
||||
cmpw r3, r4
|
||||
bgt kernelFunctionNotFound # Ordinal does not exist
|
||||
|
||||
# Get address of the export address table, and retrieve the address of our function
|
||||
subi r3, r3, 1 # Converting ordinal into kernel EAT index
|
||||
|
||||
addi r4, r8, 0x1C # Put address of RVA to EAT into r4
|
||||
bl get32BitLittleEndian # Get RVA to EAT
|
||||
addis r4, r4, 0x8004 # Address
|
||||
|
||||
# Finally, read our address from the export address table and return it
|
||||
mulli r3, r3, 4 # Multiply index by 4 to get export offset
|
||||
add r4, r4, r3 # Add export offset to EAT address to get export address
|
||||
bl get32BitLittleEndian
|
||||
addis r3, r4, 0x8004 # Finally, the address of our function
|
||||
|
||||
mtlr r12
|
||||
blr
|
||||
|
||||
|
||||
_start:
|
||||
# Getting address of VdDisplayFatalError function and putting it in CTR to call
|
||||
li r3, 434 # Ordinal 434 == VdDisplayFatalError
|
||||
bl getKernelFunctionAddrByOrdinal
|
||||
|
||||
mtctr r3 # Put our acquired address into CTR for calling
|
||||
|
||||
# Clearing the argument-passing registers so we don't pass anything we don't want
|
||||
# We don't use these for anything else, so it's safe.
|
||||
li r4, 0
|
||||
li r5, 0
|
||||
li r6, 0
|
||||
li r7, 0
|
||||
li r8, 0
|
||||
li r9, 0
|
||||
li r10, 0
|
||||
|
||||
# Call with E74
|
||||
lis r3, 0x0001
|
||||
ori r3, r3, 0x244A
|
||||
bctr
|
||||
|
||||
# Exit code
|
||||
# Not implemented yet, and not relevant here specifically, but this is probably what I'll do
|
||||
# Call XexGetModuleHandle(0 (current executable), address to put handle)
|
||||
# Then call XexUnloadImage(AndExitThread?)(handle address), to exit
|
||||
# Would that actually work to stop the program? If it does it could save a lot of effort.
|
Loading…
Add table
Reference in a new issue