forked from 0xDC00/scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibYuzu.js
201 lines (181 loc) · 8.15 KB
/
libYuzu.js
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
// @name Yuzu JIT Hooker
// @version 908+
// @author [DC]
// @description windows, linux
if (module.parent === null) {
throw "I'm not a text hooker!";
}
const isFastMem = true;
const DoJitPtr = getDoJitAddress();
const buildRegs = createFunction_buildRegs();
const operations = Object.create(null);
//let EmitX64_vftable;
/*
https://github.com/merryhime/dynarmic/blob/e6f9b08d495449e4ca28882c0cb4f12d83fd4549/src/dynarmic/backend/x64/emit_x64.cpp
EmitX64::BlockDescriptor EmitX64::RegisterBlock(
const IR::LocationDescriptor& descriptor,
CodePtr entrypoint,
CodePtr entrypoint_far,
size_t size
)
=> Win32
EmitX64::BlockDescriptor *__fastcall EmitX64::RegisterBlock(
EmitX64 *this, // rcx (vftable)
EmitX64::BlockDescriptor *result, // rdx
const LocationDescriptor *descriptor, // r8 <== 2
const void *entrypoint, // r9 <== 3
const void *entrypoint_far, // [rsp+230]
unsigned __int64 size) // [rsp+238]
=> Linux, macOS
EmitX64::BlockDescriptor *__fastcall EmitX64::RegisterBlock(
X64::BlockOfCode **a1_code, // rdi 0
void **a2_descriptor, // rsi <== 1
__int64 a3_entrypoint, // rdx <== 2
__int64 a4_entrypoint_far, // rcx
__int64 a5_size) // r8
TODO:
Arm64? https://github.com/merryhime/dynarmic/blob/arm64/src/dynarmic/backend/arm64/a32_address_space.cpp#L104
*/
const isVirtual = Process.arch === 'x64' && Process.platform === 'windows';
const idxDescriptor = isVirtual === true ? 2 : 1;
const idxEntrypoint = idxDescriptor + 1;
Interceptor.attach(DoJitPtr, {
onEnter: function (args) {
//EmitX64_vftable = args[0]; // rcx
//EmitX64_result = args[1]; // rdx
const descriptor = args[idxDescriptor]; // r8
const entrypoint = args[idxEntrypoint]; // r9
//const entrypoint_far = args[4];
//const size = args[5];
const em_address = descriptor.readU64().toNumber();
const op = operations[em_address];
if (op !== undefined) {
console.log('Attach:', ptr(em_address), entrypoint);
// Breakpoint.add (slower)
Breakpoint.add(entrypoint, function () {
const thiz = Object.create(null);
//thiz.returnAddress = 0;
thiz.context = Object.create(null);
thiz.context.pc = em_address;
//thiz.context.sp = 0;
const regs = buildRegs(this.context, thiz); // x0 x1 x2 ...
//console.log(JSON.stringify(thiz, (_, value) => { return typeof value === 'number' ? '0x' + value.toString(16) : value; }, 2));
op.call(thiz, regs);
});
// // Interceptor.attach (detach = hook removed, but freeze)
// Interceptor.attach(entrypoint, {
// onEnter: function () {
// const thiz = Object.create(null);
// //thiz.returnAddress = 0;
// thiz.context = Object.create(null);
// thiz.context.pc = em_address;
// //thiz.context.sp = 0;
// const regs = buildRegs(this.context, thiz); // x0 x1 x2 ...
// //console.log(JSON.stringify(thiz, (_, value) => { return typeof value === 'number' ? '0x' + value.toString(16) : value; }, 2));
// op.call(thiz, regs);
// }
// });
}
}
});
function getDoJitAddress() {
if (Process.platform !== 'windows') {
// Unix
// not _ZN8Dynarmic7Backend3X647EmitX6413RegisterBlockERKNS_2IR18LocationDescriptorEPKvS8_m.cold
const names = [
'_ZN8Dynarmic7Backend3X647EmitX6413RegisterBlockERKNS_2IR18LocationDescriptorEPKvS8_m', // linux x64
'__ZN8Dynarmic7Backend3X647EmitX6413RegisterBlockERKNS_2IR18LocationDescriptorEPKvS8_m' // macOS x64
];
for (const name of names) {
const addresss = DebugSymbol.findFunctionsNamed(name);
if (addresss.length !== 0) {
return addresss[0];
}
}
}
else {
const __e = Process.enumerateModules()[0];
// Windows MSVC x64
// TODO: DebugSymbol.fromName?
// yuzu win msvc ?RegisterBlock@EmitX64@X64@Backend@Dynarmic@@IEAA?AUBlockDescriptor@1234@AEBVLocationDescriptor@IR@4@PEBX1_K@Z
const RegisterBlockSig1 = 'E8 ?? ?? ?? ?? 48 8B ?? 08 E8 ?? ?? ?? ?? 4? 8B ?? 4? 8B ?? 4? 8B ?? E8 ?? ?? ?? ?? 4?';
const first = Memory.scanSync(__e.base, __e.size, RegisterBlockSig1)[0];
if (first) {
const beginSubSig1 = 'CC CC 40 5? 5? 5?';
const lookbackSize = 0x400;
const address = first.address.sub(lookbackSize);
const subs = Memory.scanSync(address, lookbackSize, beginSubSig1);
if (subs.length > 0) {
return subs[subs.length - 1].address.add(2);
}
}
}
throw new Error('RegisterBlock not found!');
}
// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A64)
// https://github.com/merryhime/dynarmic/blob/master/src/dynarmic/backend/x64/a64_jitstate.h
// https://github.com/merryhime/dynarmic/blob/master/src/dynarmic/backend/x64/a32_jitstate.h
function createFunction_buildRegs() {
let body = '';
// https://github.com/merryhime/dynarmic/blob/0c12614d1a7a72d778609920dde96a4c63074ece/src/dynarmic/backend/x64/a64_emit_x64.cpp#L481
body += 'const regs = context.r15;'; // x28
let getValue = '';
if (isFastMem === true) {
/* fastmem (host MMU) */
// https://github.com/merryhime/dynarmic/blob/master/src/dynarmic/backend/x64/a64_interface.cpp#L43
body += 'const base = context.r13;';
getValue = `get value() { return base.add(this._vm); },`; // host address
}
else {
/* pagetable */
// https://github.com/merryhime/dynarmic/blob/0c12614d1a7a72d778609920dde96a4c63074ece/src/dynarmic/backend/x64/a64_emit_x64.cpp#L831
body += 'const table = context.r14;';
const page_bits = 12 // 0xC
// const page_mask = (1 << page_bits) - 1; // 0xFFF
// https://github.com/merryhime/dynarmic/blob/0c12614d1a7a72d778609920dde96a4c63074ece/src/dynarmic/backend/x64/a64_emit_x64.cpp#L869
const page_table_pointer_mask_bits = 2;
body += `const mask_bits = NULL.not().shl(${page_table_pointer_mask_bits});`; // 0xFFFFFFFFFFFFFFFC
// const page = table.add((this._vm >>> ${page_bits}) * 8).readPointer(); // JS limitation (32bit only)
// page.add(this._vm & ${page_mask});
// const page = [table + (vaddr >> C)*8];
// const addr = (page+vaddr) & mask_bits
getValue = `get value() {
const page = table.add(ptr(this._vm).shr(${page_bits}).shl(3)).readPointer();
return page.isNull() === true ? page : page.add(this._vm).and(mask_bits);
},`; // host address, 0xFFFFFFFFF8000000 <=> invalid
}
// arm32: 0->15 (r0->r15)
// arm64: 0->30 (x0->lr) + sp (x31) + pc (x32)
body += 'const args = [';
for (let i = 0; i < 33; i++) {
let offset = i * 8;
body += '{';
body += `_vm: regs.add(${offset}).readU64().toNumber(),`;
body += getValue;
body += `set vm(val) { this._vm = val; },`;
body += `get vm() { return this._vm },`;
body += `save() {regs.add(${offset}).writeU64(this._vm); return this; }`;
body += '},';
}
body += '];';
//body += 'thiz.context.pc = regs.add(256).readU64().toNumber();' // x32 0x100 256 - where you are
//body += 'thiz.context.sp = regs.add(248).readU64().toNumber();'; // x31 0xF8 248; useless?
body += 'thiz.returnAddress = regs.add(240).readU64().toNumber();'; // x30 0xF0 240, lr - where you were
body += 'thiz.context.lr = args[30];';
body += 'thiz.context.fp = args[29];'; // x29 (FP): Frame pointer.
body += 'thiz.context.sp = args[31];'; // x31
body += 'return args;';
return new Function('context', 'thiz', body);
};
function setHook(object) {
//console.log(JSON.stringify(object, null, 2));
for (const key in object) {
if (Object.hasOwnProperty.call(object, key)) {
const element = object[key];
operations[key] = element;
}
}
}
module.exports = exports = {
setHook
}