"Audio Recorder" is indeed a very simple tweak. The author tried to obfuscate important parts of his tweak (which function is being hooked), but here is what I found.
Tweak basically hooks just one function - AudioConverterConvertComplexBuffer
from AudioToolbox.framework
. Tweak is loaded in mediaserverd
daemon at startup.
First, we need to find out when we should start recording because AudioConverterConvertComplexBuffer
is called even when you just playing regular audio files. To achieve that tweak is listening to kCTCallStatusChangeNotification
notification from CTTelephonyCenter
.
Second, AudioConverterConvertComplexBuffer
implementation. I didn't finished it yet so I will post what I have so far. Here is somewhat working example that will get you started.
Helper class to keep track of AudioConverterRef - ExtAudioFileRef pairs
@interface ConverterFile : NSObject @property (nonatomic, assign) AudioConverterRef converter; @property (nonatomic, assign) ExtAudioFileRef file; @property (nonatomic, assign) BOOL failedToOpenFile; @end @implementation ConverterFile @end
ConverterFile objects container
NSMutableArray* callConvertersFiles = [[NSMutableArray alloc] init];
AudioConverterConvertComplexBuffer original implementation
OSStatus(*AudioConverterConvertComplexBuffer_orig)(AudioConverterRef, UInt32, const AudioBufferList*, AudioBufferList*);
AudioConverterConvertComplexBuffer hook declaration
OSStatus AudioConverterConvertComplexBuffer_hook(AudioConverterRef inAudioConverter, UInt32 inNumberPCMFrames, const AudioBufferList *inInputData, AudioBufferList *outOutputData);
Hooking
MSHookFunction(AudioConverterConvertComplexBuffer, AudioConverterConvertComplexBuffer_hook, &AudioConverterConvertComplexBuffer_orig);
AudioConverterConvertComplexBuffer hook definition
OSStatus AudioConverterConvertComplexBuffer_hook(AudioConverterRef inAudioConverter, UInt32 inNumberPCMFrames, const AudioBufferList *inInputData, AudioBufferList *outOutputData) { //Searching for existing AudioConverterRef-ExtAudioFileRef pair __block ConverterFile* cf = nil; [callConvertersFiles enumerateObjectsUsingBlock:^(ConverterFile* obj, NSUInteger idx, BOOL *stop){ if (obj.converter == inAudioConverter) { cf = obj; *stop = YES; } }]; //Inserting new AudioConverterRef if (!cf) { cf = [[[ConverterFile alloc] init] autorelease]; cf.converter = inAudioConverter; [callConvertersFiles addObject:cf]; } //Opening new audio file if (!cf.file && !cf.failedToOpenFile) { //Obtaining input audio format AudioStreamBasicDescription desc; UInt32 descSize = sizeof(desc); AudioConverterGetProperty(cf.converter, kAudioConverterCurrentInputStreamDescription, &descSize, &desc); //Opening audio file CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[NSString stringWithFormat:@"/var/mobile/Media/DCIM/Call%u.caf", [callConvertersFiles indexOfObject:cf]], kCFURLPOSIXPathStyle, false); ExtAudioFileRef audioFile = NULL; OSStatus result = ExtAudioFileCreateWithURL(url, kAudioFileCAFType, &desc, NULL, kAudioFileFlags_EraseFile, &audioFile); if (result != 0) { cf.failedToOpenFile =