Using sendmmsg with a link layer socket in Go - linux

I'm trying to use Go to call the sendmmsg syscall to send a datagram multiple times over a link layer socket. I've used Gopacket to build the datagram, and when I send it using unix.Sendto() it works as expected. When I use sendmmsg the program panics and returns an error message too long. Here's what I've tried so far (error handling removed for brevity):
// Open an AF_PACKET type socket
fd, _ := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, unix.ETH_P_ALL)
// Create a link layer Sockaddr
sockaddr := &unix.SockaddrLinklayer{
Protocol: unix.ETH_P_ALL,
Ifindex: 5,
Hatype: 803,
Pkttype: 0,
Halen: 0,
}
sendmmsg takes a file descriptor (fd), a pointer to an array of mmsghdr structures, an unsigned int vlen which is the length of the array of mmsghdr structs, and a flags value. So using Go, assuming I'm not passing any flags and that I have an array of mmsghdr structs of length 2 called mymsgvec, I should be able to send my datagram with:
unix.Syscall6(unix.SYS_SENDMMSG,
uintptr(fd),
(uintptr)(unsafe.Pointer(mymsgvec)),
uintptr(2),
0, 0, 0)
To create mymsgvec I first define a mmsghdr type following its description in the sendmmsg manpage:
type mmsghdr struct {
msghdr unix.Msghdr
msglen uint32
}
I then create a msghdr from my buffer buf:
msghdr := &unix.Msghdr {
Name: (*byte)(unsafe.Pointer(sockaddr)),
Namelen: uint32(unsafe.Sizeof(*sockaddr)),
Iov: &unix.Iovec {
Base: &buf[0],
Len: uint64(len(buf)),
},
Iovlen: uint64(1),
}
Then finally I create mymsgvec with
mymsgvec := &[]mmsghdr{
{msghdr: *msghdr, msglen: uint32(len(msg))},
{msghdr: *msghdr, msglen: uint32(len(msg))},
}
When I try to send this using unix.Syscall6 as described above it fails with the error message too long, although the message is only 44 bytes. I assume the issue is something to do with how I'm constructing mmsghdrs, but I'm not sure. Could someone please point to where I'm going wrong here? Thanks!

Related

Why does this FTDI function return zero for the fthandle?

I have a FTDI USB3 development board and some FTDI provided code for accessing it. The code works fine for things like the Device number, VID/PID etc. but always returns zero for the 'ftHandle'. As the handle is required for driving the board, this is not helpful! Can anyone see why this should happen?
static FT_STATUS displayDevicesMethod2(void)
{
FT_STATUS ftStatus;
FT_HANDLE ftHandle = NULL;
// Get and display the list of devices connected
// First call FT_CreateDeviceInfoList to get the number of connected devices.
// Then either call FT_GetDeviceInfoList or FT_GetDeviceInfoDetail to display device
info.
// Device info: Flags (usb speed), device type (600 e.g.), device ID (vendor,
product),
handle for subsequent data access.
DWORD numDevs = 0;
ftStatus = FT_CreateDeviceInfoList(&numDevs); // Build a list and return number
connected.
if (FT_FAILED(ftStatus))
{
printf("Failed to create a device list, status = %d\n", ftStatus);
}
printf("Successfully created a device list.\n\tNumber of connected devices: %d\n",
numDevs);
// Method 2: using FT_GetDeviceInfoDetail
if (!FT_FAILED(ftStatus) && numDevs > 0)
{
ftHandle = NULL;
DWORD Flags = 0;
DWORD Type = 0;
DWORD ID = 0;
char SerialNumber[16] = { 0 };
char Description[32] = { 0 };
for(DWORD i = 0; i <numDevs; i++)
{
ftStatus = FT_GetDeviceInfoDetail(i, &Flags, &Type, &ID, NULL, SerialNumber,
Description, &ftHandle);
if (!FT_FAILED(ftStatus))
{
printf("Device[%d] (using FT_GetDeviceInfoDetail)\n", i);
printf("\tFlags: 0x%x %s | Type: %d | ID: 0x%08X | ftHandle=0x%p\n",
Flags,
Flags & FT_FLAGS_SUPERSPEED? "[USB 3]":
Flags & FT_FLAGS_HISPEED? "[USB 2]":
Flags & FT_FLAGS_OPENED? "[OPENED]": "",
Type,
ID,
ftHandle);
printf("\tSerialNumber=%s\n", SerialNumber);
printf("\tDescription=%s\n", Description);
}
}
}
return ftStatus;
}
This is indeed not super straight forward, but a short peek in the FTDI Knowledgebase yields:
This function builds a device information list and returns the number of D2XX devices connected to the system. The list contains information about both unopen and open devices.
A handle only exists for an opened device. Thus, I assume that your code does not already include that step. If so you need to open it first, e.g. using FT_Open. There are plenty of examples available. You can check their page or stackoverflow for a working example.

Does Malloc allocate the correct space?

I have a structure which is passed to a function. There some values should be changed. Back in the main I should read out the new values of my structure.
In a sample project it works EASILY!
In my main code in Visual Studio Code with ARM Cortex M3 and a crazy library it DOESNT WORK
(update: something must be wrong with compiler: I checked it hard and observed that not all variables are changed!!! )
My first big goal is to change chip_count within my structure.
Here the program flow:
I create a pointer to a special type of structure.
I pass this pointers address to a function.
The function creates a new adress with malloc.
The passed pointer gets this new address.
So it points now on the malloc.
Some variable are of the malloc structure are changed.
Return to main
Access the malloc space and read out the changed stuff
Result:
YES IT WORKS
Sry I added some more functions to understand const declarations better.
Pointer stuff:
vtss_state_t is the same as struct vtss_state_s
vtss_inst_t in turn is a pointer on struct vtss_state_s or vtss_state_t
the function vtss_create() has a parameter *vtss_inst_t const inst
This is a pointer on the pointer of of vtss_sate_s or a pointer to vtss_inst_t.
In the function at the end the adress of malloc is passed to (*inst).
(*inst) is the content of the of the pointer on which inst is pointing to.
So the pointer will point to malloc().
I hope this is right...
FIRST APPROACH ONLINE COMPILER
I just wanted to see if I can change values in my passed structure.
Result: YES
FILE:state.h
#include <stdint.h>
/******************************************************************************
Structures
*******************************************************************************/
typedef struct tests
{
int x;
} tests_t;
typedef uint32_t u32;
// This is the structure with chip_count
typedef struct vtss_state_s
{
int a;
int cookie;
tests_t u;
int port_count;
u32 chip_count // doesnt change in my real program :(
} vtss_state_t;
// typedef this structure so vtss_inst_t points to it...(?)
typedef struct vtss_state_s *vtss_inst_t;
FILE: main.c
/******************************************************************************
Online C Compiler.
Code, Compile, Run and Debug C program online.
Write your code in this editor and press "Run" button to compile and execute it.
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "state.h"
#define VTSS_PORT_COUNT 1
#define VTSS_PORTS VTSS_PORT_COUNT
//
// some global structure
vtss_inst_t testspace;
vtss_inst_t *testspace_ptr;
/******************************************************************************
functions
*******************************************************************************/
// vtss_inst_get ()
// const adress another access style
void
vtss_inst_get (const vtss_inst_t inst)
{
printf ("\ninst get()");
printf ("\ninst get() adress of inst:%d", inst);
inst->a = 1111;
}
// This function just should proof that it can change the content
void
vtss_inst_default_set (vtss_state_t * vtss_state)
{
vtss_state->port_count = VTSS_PORTS; // 1
}
//** This is the function which should change "chip_count"
// Man soll einen Pointer auf einen Pointer auf die Struktur C<bergeben (wo sich chip_count befindet )
void
vtss_inst_create (vtss_inst_t * const inst)
{
printf ("\r\n**vtss_inst_create()");
vtss_state_t *vtss_state; // temporary pointer ( just points to a structure )
vtss_state_t *vtss_state_2; // just a test compare
// pass temporary pointer a new memory space:
vtss_state = malloc (sizeof (*vtss_state)); // is this correct? anyway it also doesnt work without this malloc style
printf ("\r\n\t malloc=: %d", vtss_state);
// pass test temporary pointer a new memory space:
vtss_state_2 = malloc (sizeof (*vtss_state_2));
printf ("\r\n\t (test) malloc2: %d", vtss_state_2);
//Test: pass the temporary pointer even deeper & change content of structure where it points to
// (change variable port_count to 1)
vtss_inst_default_set (vtss_state);
vtss_inst_default_set (vtss_state_2);
//Test:Change values of the structure:
vtss_state->a = 1;
vtss_state->chip_count = 12121;
vtss_state_2->chip_count = 12121;
// ** Giving back the new adress from malloc:
// Test:(from vtss_state or vtss_state2)
(*inst) = vtss_state; //passing adress1
//Test: a should have the correct value:
printf ("\r\n\ta=1? a =%d ", (*inst)->a);
(*inst) = vtss_state_2; //passing adress2
// Some extra tests:
(testspace) = vtss_state_2; // extra Test: pass the adress to a test pointer
testspace_ptr = &testspace; // extra Test: pass the adress to a test pointer
(*testspace_ptr) = vtss_state_2; // extra Test: pass the adress to a test pointer
vtss_state_2->a = 22; // extra Test: change content of test pointer
printf ("\r\n\ta=22? a=%d ", (*inst)->a); //
}
/******************************************************************************
main
*******************************************************************************/
int
main ()
{
// **The main structure:
vtss_state_t state_test;
state_test.a = 1234; // pass some test value
// **pointer to type of vtss_state_t
vtss_inst_t inst;
// **pass the adress of the structure!:
inst = &state_test;
// Test: add a value to a structure of the structure:
// (is the same like inst->)
state_test.u.x = 123;
// Test:
inst->chip_count = 888;
vtss_inst_get (inst);
// Test: I declared two global structure to test global access:
printf ("\ntestspace points to: %d", testspace);
printf ("\ntestspace ptr points to: %d", testspace_ptr);
// Test variable a:
printf ("\n1234? inst->a=%d", inst->a);
// Test: show value of structure in a strucure:
printf ("\n123? state_test.u.x=%d", state_test.u.x);
// Test: before & after address is changed:
printf ("\r\n * Before create(): Adress inst:%d", inst);
printf ("\r\n******* Before create():chip_count: %d", inst->chip_count);
vtss_inst_create (&inst);
printf ("\r\n\n * After create(): Adress of inst:%d", inst);
printf ("\r\n\t 22? a:%d", inst->a);
printf
("\r\n even the layer2 function in create() worked:\n port_count=1?: %d",
inst->port_count);
printf
("\r\n\n\n******* chip_count has changes correctly to 12121: %d\n\n\n",
inst->chip_count);
//extra tests with the global structures:
printf ("\ntestspace points to: %d", testspace);
printf ("\n testspace has the correct value a: %d", testspace->a);
printf ("\n testspace_ptr has the correct value a:: %d",
(*testspace_ptr)->a);
printf ("\n adress of pointer testspace_ptr points to?:%d", testspace_ptr);
// extra test:
vtss_inst_get (inst);
printf ("\n there is even access to a:%d", inst->a);
printf ("\nPE-Hello World");
return 0;
}
SECOND APPROACH: ARM CORTEX M3 in VSC:
I just want to change chip_count but the library function doesn't do it!
vtss_rc **vtss_inst_create**(const vtss_inst_create_t *const create, vtss_inst_t *const inst, void (*print)(void))
{
vtss_state_t *vtss_state;
vtss_state = malloc(sizeof(*vtss_state));
vtss_state->chip_count = 777;
(*inst) = vtss_state;
I call it in the main: vtss_inst_create(&create, &inst);
( now I also deleted the third test parameter: I should change it here, but result is anyway same)
when I read chip_count out in the main :
sprintf(t2, "\r\n main: chipcount should be 777 %d ??", (int)(inst->chip_count));
print_uart(t2);
Then it prints:
482531848 !!!!!!
I copied this function:
void createtest2(vtss_inst_t *const inst)
{
vtss_state_t *vtss_state;
vtss_state = malloc(sizeof(*vtss_state));
vtss_state->chip_count = 888;
(*inst) = vtss_state;
}
it is called: createtest2(&inst);
sprintf(t2, "\r\n main: wie in create() chipcount 888?: %d", (int)(inst->chip_count));
print_uart(t2);
it prints 888 correctly i guess because it prints it ..
I copied it also with correct header:
vtss_rc vtss_inst_create3(const vtss_inst_create_t *const create, vtss_inst_t *const inst, void (*print)(void))
{
vtss_state_t *vtss_state;
vtss_state = malloc(sizeof(*vtss_state));
vtss_state->chip_count = 222;
(*inst) = vtss_state;
(void)create;
(void)print;
return 1;
}
I call it: vtss_inst_create3(&create, &inst, spitest);
(ignore please the third parameter)
it prints out 222 correctly
How is that possible?
Or is this simply magic :)
Update: I checked it and it is shocking to me.
lol
it works in every kind of function but not with that one I want to do it xD
First I just copied the function but only one parameter. It worked.
Then I copied it with all parameters. It worked.
But the original library func. doesn't work.
In the past I changed the library function a little bit:
Ok in the past a tuned the library function by adding one little parameter as a little test.
In combination, that it is a library function in not located in the same file it may cause some trouble.
I will now change the header back to normal:
Normal:
vtss_rc vtss_inst_create(const vtss_inst_create_t *const create,
vtss_inst_t *const inst)
How i tuned it:
vtss_rc vtss_inst_create(const vtss_inst_create_t *const create, vtss_inst_t *const inst, void (*print)(void))
{
update: lol I rechanged the function it back to normal.
Hmmm....
Another file?
Then it must be because the function is in another file or what.
I checked inside the functions they point to the same addresses as outside (main).. I mean the value of inst.
Todo I print out now the addresses of chip_count
Update: OMG something is wrong with the compiler. I compared it as described with similar functions.
Same function different result of chip_count.
But another variabale e.g. "cookie" can be changed!
So one variable is change another not.
This was really confusing to me because I didn't expect it!
CMAKE:
Maybe there is an error which is suppressed by my CMAKE. So the compiler ignores the error. At runtime the error comes to light.
add_target_compile_flags(base PRIVATE "" "-Wno-unused-parameter" "-Wno-pedantic" "-Wno-format" "-Wno-sign-conversion" "-Wno-switch-default" "-Wno-conversion" "-Wno-unused-variable" "-Wno-undef" "-Wno-unused-but-set-variable" "-Wno-unused-function" "-Wno-implicit-function-declaration" "-Wno-uninitialized")

why CreateRemoteThread cause inject target crash

Update on 2019/11/19:
I search google and find a lib to do this(I can't remember which is it now), and it works fine.
Update on 2019/6/19:
My env is win10, the reason is this code is not work on win10?
Origin:
I use this code to just inject int foo() {return 0} to a target process. But It cause target process crash.
The entire vs solution is here: https://github.com/huhuang03/test/tree/master/win/InjectHelloWorld. Include the InjectMe and InjectByCode.
char hand_asm[100] = {0xC3}; // 0xc3 is the retn assembly
if (!WriteProcessMemory(h_target, targetFuncSpace, &hand_asm, CODE_SPACE_SIZE, NULL)) {
showError(L"Cna't write targetFuncSpace");
return EXIT_FAILURE;
}
InjectFuncParam param;
LPVOID injectFuncParamSpace = VirtualAllocEx(h_target, NULL, sizeof(param), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!injectFuncParamSpace) {
showError(L"Can't alloc injectFuncParamSpace");
return EXIT_FAILURE;
}
system("pause");
DWORD remoteThreadId = 0;
HANDLE h_remoteThread = CreateRemoteThread(h_target, NULL, 0, (LPTHREAD_START_ROUTINE)targetFuncSpace, injectFuncParamSpace, 0, &remoteThreadId);
if (!h_remoteThread) {
VirtualFreeEx(h_target, injectFuncParamSpace, 0, MEM_RELEASE);
VirtualFreeEx(h_target, targetFuncSpace, 0, MEM_RELEASE);
showError(L"Cant' create rmeote Thread");
return EXIT_FAILURE;
this cause InjectMe crash, I can't find a way to debug this.
By the way, I use ollydbg to set a breakpoint at targetFuncSpace, but the ollydbg says it's not code segment... Why, I had use the PAGE_EXECUTE_READWRITE to alloc the space.
One problem I see is you can't VirtualFree targetFuncSpace until the remote thread in the target process has finished executing.
Also WriteProcessMemory is copying CODE_SPACE_SIZE (4096) bytes from hand_asm which is only 100 bytes.

segfault on Rcpp function when running with sanitizers (from rhub)

I am writing a package using Rcpp Function, the package compiles, and R CMD Check also works fine. Earlier, the input to package's cvode function was an XPtr, but now the input can be both XPtr or an R or Rcpp function (the implementation was based on a earlier post). Currently, input functions in R, Rcpp and Rcpp::XPtr work in the package.
The package had previously had clang-UBSAN issues, so I am trying to detect them beforehand now using rhub package. On running check_with_sanitizers() command from the rhub package, I am getting the following error:
eval.c:677:21: runtime error: member access within null pointer of type 'struct SEXPREC'
─ *** caught segfault ***
address (nil), cause 'memory not mapped'
Segmentation fault (core dumped)
I have been able to isolate the error to the line ydot1 = rhs_fun(t, y1); in the following piece of code, i.e., commenting/un-commenting the expression above reproduces the error.
My question is - is there a check I should be performing before calling the rhs_fun Function, to avoid the segmentation fault error?
Note - check_with_valgrind() produces no error.
Thanks!
// struct to use if R or Rcpp function is input as RHS function
struct rhs_func{
Function rhs_eqn;
};
int rhs_func(realtype t, N_Vector y, N_Vector ydot, void* user_data){
// convert y to NumericVector y1
int y_len = NV_LENGTH_S(y);
NumericVector y1(y_len); // filled with zeros
realtype *y_ptr = N_VGetArrayPointer(y);
for (int i = 0; i < y_len; i++){
y1[i] = y_ptr[i];
}
// convert ydot to NumericVector ydot1
// int ydot_len = NV_LENGTH_S(ydot);
NumericVector ydot1(y_len); // filled with zeros
// // cast void pointer to pointer to struct and assign rhs to a Function
struct rhs_func *my_rhs_fun = (struct rhs_func*)user_data;
if(my_rhs_fun){
Function rhs_fun = (*my_rhs_fun).rhs_eqn;
// use the function to calculate value of RHS ----
// Uncommenting the line below gives runtime error
// ydot1 = rhs_fun(t, y1);
}
else {
stop("Something went wrong, stopping!");
}
// convert NumericVector ydot1 to N_Vector ydot
realtype *ydot_ptr = N_VGetArrayPointer(ydot);
for (int i = 0; i< y_len; i++){
ydot_ptr[i] = ydot1[i];
}
// everything went smoothly
return(0);
}
An update - based on the comments below, I have added checks. So the check succeeds, but I can see the rhs_fun is NULL as the code goes to the stop message.
if(my_rhs_fun){
Function rhs_fun = (*my_rhs_fun).rhs_eqn;
// use the function to calculate value of RHS ----
if (rhs_fun){
ydot1 = rhs_fun(t, y1);
}
else{
stop("Something went wrong");
}
}
else {
stop("Something went wrong, stopping!");
}
An added check is added to the struct also
if (Rf_isNull(input_function)){
stop("Something is wrong with input function, stopping!");
}
The checks succeed, but I see that rhs_fun is NULL as the else message is printed
Error in cvode(time_vec, IC, ODE_R, reltol, abstol) :
Something went wrong
Execution halted
Not sure why, as the examples I have tried have worked without complaints.
The most likely candidate for a NULL reference is rhs_fun. So it makes sense to test that before executing the function:
if(rhs_fun) {
...
} else {
stop(...)
}
The R API function Rf_isNull would not be appropriate here, since that checks for an SEXP of type NILSXP and segfaults for an actual nullptr.
In addition I would check that you are not inserting a NULL reference into the struct, though I find it unlikely that anything is going wrong there.
Overall this is just a workaround. It would be interesting to know what triggers this behavior.

libMobileGestalt.dylib crashed when Hooking MGCopyAnswer for ARM64

When I try to hook MGCopyAnswer, I get a crash. I'm trying this on a jailbroken iPhone 5s in iOS 8.3, arm64 binaries.
#import <substrate.h>
extern "C" CFTypeRef MGCopyAnswer(CFStringRef);
MSHook(CFTypeRef, MGCopyAnswer, CFStringRef key)
{
  return _MGCopyAnswer(key);
}
%ctor
{
  NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
  if ( appID && [appID isEqualToString:#"com.test.test"])   {
    MSHookFunction(MGCopyAnswer, MSHake(MGCopyAnswer));
  }
}
Makefile:
ARCHS = armv7 armv7s arm64
TARGET = iphone:latest:8.0
test2_FRAMEWORKS = UIKit
include theos/makefiles/common.mk
TWEAK_NAME = test2
test2_FILES = Tweak.xm
test2_LIBRARIES = MobileGestalt
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
  install.exec "killall -9 SpringBoard"
Crash log:
Version: 1.44 (1.4)
Code Type: ARM-64 (Native)
Parent Process: launchd [1]
Date/Time:           2016-04-25 01:09:31.810 +0800
Launch Time:         2016-04-25 01:09:31.564 +0800
OS Version:          iOS 8.3 (12F70)
Report Version:      105
Exception Type:  EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x000000000068fe68
Triggered by Thread:  0
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libMobileGestalt.dylib          0x0000000195af7e84 0x195af4000 + 16004
1   libMobileGestalt.dylib          0x0000000195af82bc MGGetBoolAnswer + 32
2   AppSupport                      0x000000018b020594 __CPIsInternalDevice_block_invoke + 16
3   libdispatch.dylib               0x0000000196c99950 _dispatch_client_callout + 12
4   libdispatch.dylib               0x0000000196c9a828 dispatch_once_f + 92
5   AppSupport                      0x000000018b02057c CPIsInternalDevice + 60
6   UIKit                           0x0000000189b58750 ___UIApplicationUsesAlternateUI_block_invoke + 12
7   libdispatch.dylib               0x0000000196c99950 _dispatch_client_callout + 12
8   libdispatch.dylib               0x0000000196c9a828 dispatch_once_f + 92
9   UIKit                           0x0000000189923750 UIApplicationInitialize + 1872
10  UIKit                           0x0000000189922b1c UIApplicationMain + 320
MGCopyAnswer:
->  0x193a7fe84 <+0>:  .long  0x002d7c28                ; unknown opcode
    0x193a7fe88 <+4>:  .long  0x00000001                ; unknown opcode
    0x193a7fe8c <+8>:  stp    x20, x19, [sp, #32]
    0x193a7fe90 <+12>: stp    x29, x30, [sp, #48]
    0x193a7fe94 <+16>: add    x29, sp, #48
    0x193a7fe98 <+20>: sub    sp, sp, #48
    0x193a7fe9c <+24>: mov    x19, x1
    0x193a7fea0 <+28>: mov    x22, x0
    0x193a7fea4 <+32>: movz   w0, #0
    0x193a7fea8 <+36>: bl     0x193a7f564               ; ___lldb_unnamed_function54$$libMobileGestalt.dylib
    0x193a7feac <+40>: orr    w1, wzr, #0x1
    0x193a7feb0 <+44>: mov    x0, x22
    0x193a7feb4 <+48>: bl     0x193a7f5fc               ; ___lldb_unnamed_function56$$libMobileGestalt.dylib
    0x193a7feb8 <+52>: mov    x21, x0
    0x193a7febc <+56>: movz   w20, #0
    0x193a7fec0 <+60>: cbz    x21, 0x193a7fefc          ; <+120>
    0x193a7fec4 <+64>: ldr    w20, [x21, #148]
    0x193a7fec8 <+68>: mov    x0, x21
orig_MGCopyAnswer
    0x104234000: movz   x1, #0
    0x104234004: stp    x24, x23, [sp, #-64]!
    0x104234008: stp    x22, x21, [sp, #16]
    0x10423400c: ldr    x16, #8
    0x104234010: br     x16
    0x104234014: .long  0x93a7fe8c                
    0x104234018: .long  0x00000001                ; unknown opcode
What am I doing wrong?
You cannot hook MGCopyAnswer directly because it is too short.
When CydiaSubstrate hooks a C function, it sorts of overwrites an assembly version of goto your_function; at the beginning of the original function. This "goto" in ARM64 is 16 bytes in size, which means if the original function is too short (< 16 bytes), CydiaSubstrate can spill over and corrupt the neighboring functions.
This is exactly why the problem of MGCopyAnswer. The implementation of MGCopyAnswer is basically (on 9.3.2 arm64):
01 00 80 d2 movz x1, #0
01 00 00 14 b MGCopyAnswer_internal
which is just 8 bytes (< 16 bytes) in size. So CydiaSubstrate will corrupt the 8 bytes after the end of MGCopyAnswer.
Unfortunately, MGCopyAnswer_internal is right after MGCopyAnswer, and even worse this function and is called by MGGetBoolAnswer as well. Since MGCopyAnswer_internal is corrupt, you get an EXC_BAD_INSTRUCTION crash inside libMobileGestalt.
A good news for MGCopyAnswer is that, you could just hook MGCopyAnswer_internal! This has an additional benefit that many related functions like MGGetBoolAnswer, MGCopyAnswerWithError, MGCopyMultipleAnswers etc. can respond to your change as well. The bad thing is that MGCopyAnswer_internal is completely internal, and there is no symbols pointing to it. We could rely on the fact that MGCopyAnswer_internal is exactly 8 bytes after MGCopyAnswer on ARM64, and develop this ugly hack:
static CFPropertyListRef (*orig_MGCopyAnswer_internal)(CFStringRef prop, uint32_t* outTypeCode);
CFPropertyListRef new_MGCopyAnswer_internal(CFStringRef prop, uint32_t* outTypeCode) {
return orig_MGCopyAnswer_internal(prop, outTypeCode);
}
extern "C" MGCopyAnswer(CFStringRef prop);
static CFPropertyListRef (*orig_MGCopyAnswer)(CFStringRef prop);
CFPropertyListRef new_MGCopyAnswer(CFStringRef prop) {
return orig_MGCopyAnswer(prop);
}
%ctor {
uint8_t MGCopyAnswer_arm64_impl[8] = {0x01, 0x00, 0x80, 0xd2, 0x01, 0x00, 0x00, 0x14};
const uint8_t* MGCopyAnswer_ptr = (const uint8_t*) MGCopyAnswer;
if (memcmp(MGCopyAnswer_ptr, MGCopyAnswer_arm64_impl, 8) == 0) {
MSHookFunction(MGCopyAnswer_ptr + 8, (void*)new_MGCopyAnswer_internal, (void**)&orig_MGCopyAnswer_internal);
} else {
MSHookFunction(MGCopyAnswer_ptr, (void*)new_MGCopyAnswer, (void**)&orig_MGCopyAnswer);
}
}
(This only checks for arm64 on 9.3.2. Other platforms may crash in different ways, and have different assembly code, so you may need to add additional conditions into enter the hook-MGCopyAnswer_internal branch. YMMV!)
Try this code:
#import <substrate.h>
static CFTypeRef (*orig_MGCopyAnswer)(CFStringRef str);
CFTypeRef new_MGCopyAnswer(CFStringRef str)
{
return orig_MGCopyAnswer(str);
}
%ctor
{
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
if ( appID && [appID isEqualToString:#"com.test.test"]) {
void * MGCopyAnswerFn = MSFindSymbol(NULL, "_MGCopyAnswer");
MSHookFunction(MGCopyAnswerFn, (void *) new_MGCopyAnswer, (void **)& orig_MGCopyAnswer);
}
}

Resources