I have the following frida javascript code, which find code caves for data and code then make sure that the code segment is rwx and at address 4012A5 patches a messagebox with a jump to a codecave code place and then jump back to the original code. The application has four threads, the script run fine up to the point that give access violation, I will include all the relevant code and output.
'use strict';
const currentModule = Process.getModuleByName(Process.mainModule.name);
const targetAddress = ptr('0x4012A5');
console.log('Module base:', currentModule.base);
console.log('Target address:', targetAddress);
function findCodeCaveDATA(size) {
const scanPattern = '00'.repeat(size);
for (const { base, size, protection } of currentModule.enumerateRanges('rw-'))
{
console.log(`Scanning range for data: base: ${base}, size: 0x${size.toString(16)}, protection: ${protection}`);
//if (protection.indexOf('w') !== -1 || protection.indexOf('x') !== -1)
//{
const results = Memory.scanSync(base, size, scanPattern);
if (results.length > 0)
{
console.log(`Found code cave for putting data at address ${results[0].address}`);
console.log(``);
return results[0].address;
}
//}
}
console.log('Module size: 0x' + currentModule.size.toString(16));
console.log('Code cave size needed: 0x' + size.toString(16));
return null;
}
function findCodeCaveCODE(size) {
const scanPattern = '00'.repeat(size);
for (const { base, size, protection } of currentModule.enumerateRanges('r-x'))
{
console.log(`Scanning range for code: base: ${base}, size: 0x${size.toString(16)}, protection: ${protection}`);
//if (protection.indexOf('w') !== -1 || protection.indexOf('x') !== -1)
//{
const results = Memory.scanSync(base, size, scanPattern);
if (results.length > 0)
{
console.log(`Found code cave for putting code at address ${results[0].address}`);
console.log(``);
return results[0].address;
}
//}
}
console.log('Module size: 0x' + currentModule.size.toString(16));
console.log('Code cave size needed: 0x' + size.toString(16));
return null;
}
// For code Segment
function ensureExecutePermissions(address, size)
{
Memory.protect(address, size, 'rwx');
}
function calculateRelativeJump(from, to)
{
const diff = to.sub(from).sub(5); // -5 for the size of the jmp instruction itself
return new Uint8Array(new Int32Array([diff.toInt32()]).buffer);
}
const caption = "Frida Message";
const text = "Hello from Frida!";
const trampolineSize = 5; // Size of a JMP instruction
const originalInstructions = Memory.readByteArray(targetAddress, trampolineSize);
// Calculate required size for code cave
const messageBoxCode = [
0x60, // pushad
0x6A, 0x00, // push 0
0x68, 0x00, 0x00, 0x00, 0x00, // push text (address to be filled)
0x68, 0x00, 0x00, 0x00, 0x00, // push caption (address to be filled)
0x6A, 0x00, // push 0
0xFF, 0x15, 0x00, 0x00, 0x00, 0x00, // call MessageBoxA (address to be filled)
0x61, // popad
];
// Required size for DATA
const requiredSizeDATA = caption.length + 1 + text.length + 1; //
// Required size for CODE
const requiredSizeCODE = messageBoxCode.length + trampolineSize + 5; // +5 for final jump back
// Find a code cave for Data
const codeCaveData = findCodeCaveDATA(requiredSizeDATA);
// Find a code cave for Code
const codeCaveCode = findCodeCaveCODE(requiredSizeCODE);
if (codeCaveData && codeCaveCode) {
console.log('Code cave for putting Data found at:', codeCaveData);
console.log('Code cave for putting Code found at:', codeCaveCode);
// Ensure execute permissions for code segment
ensureExecutePermissions(codeCaveCode, requiredSizeCODE);
// Write strings to code cave DATA
const captionAddress = codeCaveData;
const textAddress = codeCaveData.add(caption.length + 1);
Memory.writeUtf8String(captionAddress, caption);
Memory.writeUtf8String(textAddress, text);
// Prepare MessageBox code
const codeAddress = codeCaveCode.add(0); // 0 because in separate code segment
const messageBoxA = Module.getExportByName('user32.dll', 'MessageBoxA');
// Write address for push text
messageBoxCode[4] = textAddress.toInt32() & 0xFF;
messageBoxCode[5] = (textAddress.toInt32() >> 8) & 0xFF;
messageBoxCode[6] = (textAddress.toInt32() >> 16) & 0xFF;
messageBoxCode[7] = (textAddress.toInt32() >> 24) & 0xFF;
// Write address for push caption
messageBoxCode[9] = captionAddress.toInt32() & 0xFF;
messageBoxCode[10] = (captionAddress.toInt32() >> 8) & 0xFF;
messageBoxCode[11] = (captionAddress.toInt32() >> 16) & 0xFF;
messageBoxCode[12] = (captionAddress.toInt32() >> 24) & 0xFF;
// Write address for messageBoxA function
messageBoxCode[17] = messageBoxA.toInt32() & 0xFF;
messageBoxCode[18] = (messageBoxA.toInt32() >> 8) & 0xFF;
messageBoxCode[19] = (messageBoxA.toInt32() >> 16) & 0xFF;
messageBoxCode[20] = (messageBoxA.toInt32() >> 24) & 0xFF;
// Write MessageBox code to code cave
Memory.writeByteArray(codeAddress, messageBoxCode);
// Write jump back to original code
const jumpBackAddress = targetAddress.add(trampolineSize);
const jumpBackCode = [0xE9, ...calculateRelativeJump(codeAddress.add(messageBoxCode.length), jumpBackAddress)];
Memory.writeByteArray(codeAddress.add(messageBoxCode.length), jumpBackCode);
// Write jump to code cave at original address
const jumpToCodeCave = [0xE9, ...calculateRelativeJump(targetAddress, codeAddress)];
Memory.patchCode(targetAddress, trampolineSize, (code) => {
Memory.writeByteArray(code, jumpToCodeCave);
});
console.log('Code injected successfully');
// Verify permissions after writing all the code
console.log('Verifying permissions...');
const caveProt = Process.findRangeByAddress(codeCaveCode);
console.log('Code cave Code protection:', caveProt.protection);
if (caveProt.protection.indexOf('x') === -1) {
console.log('Warning: Code cave is not executable. Attempting to set execute permission...');
Memory.protect(codeCaveCode, requiredSize, 'rwx');
}
} else {
console.log('Failed to find a suitable code cave');
}
// Add exception handler to catch any errors
Process.setExceptionHandler(function(ex) {
console.log('Exception:', JSON.stringify(ex, null, 2));
return false;
});
// Wait for the patched code to be reached
Interceptor.attach(targetAddress,
{
onEnter: function(args)
{
console.log('Patched code reached');
try
{
const codeAddress = codeCaveData.add(caption.length + 1 + text.length + 1);
Interceptor.attach(codeAddress,
{
onEnter: function(args)
{
console.log('Injected code executed');
}
});
} catch (error)
{
console.log('Error attaching to injected code:', error);
}
}
});
The output of this script is:
Spawning `target.exe`...
Module base: 0x400000
Target address: 0x4012a5
Scanning range for data: base: 0x4a5000, size: 0x17000, protection: rw-
Found code cave for putting data at address 0x4a53d8
Scanning range for code: base: 0x401000, size: 0xa4000, protection: r-x
Found code cave for putting code at address 0x497371
Code cave for putting Data found at: 0x4a53d8
Code cave for putting Code found at: 0x497371
Code injected successfully
Verifying permissions...
Code cave Code protection: rwx
Spawned `target.exe`. Resuming main thread!
Exception: {
"message": "access violation accessing 0x923cc360",
"type": "access-violation",
"address": "0x401117",
"memory": {
"operation": "read",
"address": "0x923cc360"
},
"context": {
"pc": "0x401117",
"sp": "0x19ff48",
"eax": "0x64697246",
"ecx": "0x19ff64",
"edx": "0x96fa48",
"ebx": "0x19ff60",
"esp": "0x19ff48",
"ebp": "0x19ff50",
"esi": "0x401000",
"edi": "0x401000",
"eip": "0x401117"
},
"nativeContext": "0x19fae8"
}
[Local::target.exe ]-> Process terminated
[Local::target.exe ]->
Thank you for using Frida!
The code that causes the exception is:
.text:0040110C ___System__GetTls proc near ; CODE XREF: sub_48BA5C↓p
.text:0040110C ; sub_48BA5C+E↓p ...
.text:0040110C mov eax, dword_4A53D8
.text:00401111 mov edx, fs:2Ch
.text:00401117 mov eax, [edx+eax*4]
.text:0040111A retn
.text:0040111A ___System__GetTls endp
The application is a borland c++ builder 6 and there are four threads
I do not understand what is happening, all the code and locations checked carefully, and also patching the application manually clearly works with no issues.
I’m hoping that someone experts with threads can help me to troubleshoot this issue.
To sum up this part of the frida js script is not executed in full:
// Wait for the patched code to be reached
Interceptor.attach(targetAddress,
{
onEnter: function(args)
{
console.log('Patched code reached');
try
{
const codeAddress = codeCaveData.add(caption.length + 1 + text.length + 1);
Interceptor.attach(codeAddress,
{
onEnter: function(args)
{
console.log('Injected code executed');
}
});
} catch (error)
{
console.log('Error attaching to injected code:', error);
}
}
});
mario russo is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.