-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcause360error.s
More file actions
231 lines (181 loc) · 6.97 KB
/
cause360error.s
File metadata and controls
231 lines (181 loc) · 6.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
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.