how to fix Error: java.lang.ClassNotFoundException on frida - javascript

I'm trying to bypass a root detection mechanism on an android app using Frida, I've tried so many different scripts (frida code share) and different approaches (like hiding root) with no luck!
So I tried to locate the class and method responsible for checking if the device is rooted or not and changing it's return value.
This is my script :
setTimeout(function() { // avoid java.lang.ClassNotFoundException
Java.perform(function() {
var hook = Java.use("app.name.RootUtils");
console.log("info: hooking target class");
hook.isRooted.overload().implementation = function() {
console.log("info: entered target method");
return Java.use("java.lang.Boolean").$new(false);
}
});
},0);
If I inject this code normally it won't work because it looks like the isRooted method will get called before it
If I use spawn to run the app and change this method return value it fails with error :
frida.core.RPCException: Error: java.lang.ClassNotFoundException: Didn't find class ...
I've also tried spawning the app and then using objection to run "android root disable" but it will return this error :
frida.core.RPCException: TypeError: cannot read property 'getApplicationContext' of null
at getApplicationContext (src/android/lib/libjava.ts:21)
I'm not sure if this is a problem with Frida or my system or ...
I think if I was able to make my main code runs at exactly after the class gets loaded (like using a loop to check or using a hook) the problem would be fixed but I don't know how to write that kind of code in js for frida.
I'm on macOS 11.5.1 using python 3.9 and installed latest version of frida and objection
I've tested on one rooted phone with android 10 and an emulator with android 6

I was able to solve this issue with a simple yet not very technical solution.
I used a setInteval to run my hooking over and over until it gets to work, (as #Robert mentioned, I also needed to wrap hooking inside a try catch to prevent the code from stoping after first try)
This may not work for everyone but since it worked for me I will post the final code, may it helps someone else in the future :)
Java.perform(function() {
var it = setInterval(function(){
try{
var hook = Java.use("app.name.RootUtils");
console.log("info: hooking target class");
hook.isRooted.overload().implementation = function() {
console.log("info: entered target method");
clearInterval(it);
return Java.use("java.lang.Boolean").$new(false);
}
} catch(e) {
console.log("failed!");
}
},200); // runs every 200milisecods
});
PS : you may need to change interval time to match your app needs, it worked for me with 200 miliseconds.

Sometimes app would do encryptions in its class loader, you may need to replace Java.classFactory.loader with the app's customized class loader in order to make Java.use function properly.
Here's how it's done:
Java.perform(function() {
//get real classloader
//from http://www.lixiaopeng.top/article/63.html
var application = Java.use("android.app.Application");
var classloader;
application.attach.overload('android.content.Context')
.implementation = function(context) {
var result = this.attach(context); // run attach as it is
classloader = context.getClassLoader(); // get real classloader
Java.classFactory.loader = classloader;
return result;
}
//interesting classes
const interestingClassPath = "com.myApp.SometingInteresting";
interestingClass = Java.use(interestingClassPath);
//do whatever you like here
})

Class not found
How do you know the class is app.name.RootUtils have you decompiled to app using Jadx or apktool? How about the method where RootUtils.isRooted() is called? Is there any special code that loads the RootUtils class e.g. from a non-standard dex file included in the app? If the class is loaded from a special dex file you could hook this dex loading mechanism and first execute it and then install your hook for RootUtils.isRooted().
Alternatively assuming RootUtils.isRooted() is called only from one other method and does not use special code for loading the RootUtils class you could hook that method and use the this hook to install install your RootUtils.isRooted() hook.
Error handling
The correct way to handle errors in JavaScript is using try catch block, not the setTimeout function:
Java.perform(() => {
try {
var hook = Java.use("app.name.RootUtils");
...
} catch (e) {
console.log("Failed to hook root detection" + e);
}
}
Regarding your problems hooking the class

Related

How can I detect when an AngularJS app is finished loading a page using JavaScript?

I'm trying to write Robot Framework tests to cover certain use cases for a third party AngularJS app.
I have the requirement that I need to use Python 3.5+ and SeleniumLibrary (rather than the old Selenium2Library).
I've attempted to adapt the wait_until_angular_ready keyword from https://github.com/rickypc/robotframework-extendedselenium2library to work outside of the context of this library, updating it to work with Python 3.5+ and SeleniumLibrary.
The keyword executes the following JavaScript to check when Angular is Ready, but it seems to always return true immediately
var cb = arguments[arguments.length-1];
if(window.angular){
var $inj;
try {
$inj = angular.element(document.querySelector('[data-ng-app],[ng-app],.ng-scope')
||document).injector()
||angular.injector(['ng'])
} catch(ex) {
$inj = angular.injector(['ng'])
};
$inj.get = $inj.get||$inj;
$inj.get('$browser').notifyWhenNoOutstandingRequests(function() {
cb(true) // it's always returning here
})
} else {
cb(true)
}
This is called from within my version of the wait_until_angular_ready keyword which is located in a class which subclasses the SeleniumLibrary WaitingKeywords class.
The code which executes the Js looks like
WebDriverWait(self.driver, 100, 0.1). \
until(lambda driver: driver.execute_async_script(script), error)
Have I made a mistake here or is this not the correct way to check when angular has finished rendering the page? I will admit that I am not very familiar with AngularJS
Probably you resolved this issue, but if anyone in the future have the same inconvenience can use this library for Angular in Robot framework: https://github.com/Selenium2Library/robotframework-angularjs
you can use document.readyState , it will return "complete" as a response if the page is rendered completly.

Send event to js from swift or objective-c

I have created the following class (condensed version), heres a reference to the full file
https://github.com/cotyembry/CastRemoteNative/blob/7e74dbc56f037cc61241f6ece24a94d8c52abb32/root/ios/CastRemoteNative/NativeMethods.swift
#objc(NativeMethods)
class NativeMethods: RCTEventEmitter {
#objc(sendEventToJSFromJS)
func sendEventToJSFromJS {
self.emitEvent(eventName: "test", body: "bodyTestString")
}
func emitEvent(eventName: String: body: Any) {
self.sendEvent(withName: eventName, body: body)
}
}
This works perfectly and fires my callback listener that is in my javascript code when I call the emitEvent method like the following, its an altered snippet from
https://github.com/cotyembry/CastRemoteNative/blob/7e74dbc56f037cc61241f6ece24a94d8c52abb32/root/js/Components/ChromecastDevicesModal.js
From the javascript side
import {
NativeModules,
NativeEventEmitter
} from 'react-native'
//here I bring in the swift class to use inside javascript
var NativeMethods = NativeModules.NativeMethods;
//create an event emitter to use to listen for the native events when they occur
this.eventEmitter = new NativeEventEmitter(NativeMethods);
//listen for the event once it sends
this.subscription = this.eventEmitter.addListener('test', (body) => { console.log('in test event listener callback', body)});
NativeMethods.sendEventToJSFromJS() //call the native method written in swift
I simply have the sendEventToJSFromJS method invoked on a button press in javascript
Again, this works and the console.log('in test event listener callback', body) code works and runs on the javascript side
My Issue where this does NOT work:
If I was to do the following inside the swift file after defining the class, this would not work:
var nativeMethodsInstance = nativeMethods()
nativeMethodsInstance.sendEventToJSFromSwift()
Why? Because the following error is thrown:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'bridge is not set. This is probably because you've explicitly synthesized the bridge in NativeMethods, even though it's inherited from RCTEventEmitter.'
So, when creating an instance of NativeMethods, versus not... what is the difference?
For additional information:
Objective-C gets the same bridge not set issue when I write these same snippets of code in .h and .m files instead of in .swift files
I found where the error message is getting printed in the native code, but it just has the variable
_bridge
and is checking to see if it is nil
The files are this error comes from is:
RCTEventEmitter.h
RCTEventEmitter.c
here is the full snippet of RCTEventEmitter.c
- (void)sendEventWithName:(NSString *)eventName body:(id)body
{
RCTAssert(_bridge != nil, #"bridge is not set. This is probably because you've "
"explicitly synthesized the bridge in %#, even though it's inherited "
"from RCTEventEmitter.", [self class]);
if (RCT_DEBUG && ![[self supportedEvents] containsObject:eventName]) {
RCTLogError(#"`%#` is not a supported event type for %#. Supported events are: `%#`",
eventName, [self class], [[self supportedEvents] componentsJoinedByString:#"`, `"]);
}
if (_listenerCount > 0) {
[_bridge enqueueJSCall:#"RCTDeviceEventEmitter"
method:#"emit"
args:body ? #[eventName, body] : #[eventName]
completion:NULL];
} else {
RCTLogWarn(#"Sending `%#` with no listeners registered.", eventName);
}
}
Where does this _bridge value get set and how does it get set so I can know, in the cases where it is failing how to set it
I found the following also in RCTEventEmitter.h
#property (nonatomic, weak) RCTBridge *bridge;
In the error that is given it mentions the bridge is inherited in the RCTEventEmitter, so is this maybe an issue with the weak part to the bridge property?
Or do I need to change my strategy in how I'm doing this all together?
I know it probably has to be something to do with me not fully understanding the
#synthesize bridge = _bridge;
part of the code and all the languages being mixed in doesnt help much lol...
This is really hard, so any help would be much appreciated!
Thanks so much for your time
here is a link to the full project when the project history code represented the code from my question above (since I have since made changes to the project):
https://github.com/cotyembry/CastRemoteNative/tree/7e74dbc56f037cc61241f6ece24a94d8c52abb32
I figured it out
Warning: this solution uses a deprecated method react native method - I could not figure out how to "properly" inherit from the RCTEventEmitter and send an event... every time I tried to the _bridge would end up being nil
Make sure Swift is bridged to Objective C (if you're using swift to send the event to javascript)
Do Not create instances of the exported Native modules (whether they be written in Swift or Objective C)
Let React Native's underlying implementation do this and for each and every class that needs to send an event, export that particular Native Class Objective C Implementation code or Swift code (the Native Module) to React-Native. This allows the javascript to be able to listen to the event
var publicBridgeHelperInstance = PublicBridgeHelper() //instantiate the the objective c class from inside the .swift file to use later when needing to get a reference to the bridge to send an event to javascript written in react native
#objc(DeviceManager) //export swift module to objective c
class DeviceManager: NSObject {
#objc(deviceDidComeOnline:) //expose the function to objective c
public func deviceDidComeOnline(_ device: GCKDevice) {
//imagine this deviceDidComeOnline function gets called from something from the Native code (totally independent of javascript) - honestly this could be called from a native button click as well just to test it works...
//emit an event to a javascript function that is a in react native Component listening for the event like so:
//1. get a reference to the bridge to send an event through from Native to Javascript in React Native (here is where my custom code comes in to get this to actually work)
let rnBridge = publicBridgeHelperInstance.getBridge() //this gets the bridge that is stored in the AppDelegate.m file that was set from the `rootView.bridge` variable (more on this later)
//(if you want to print the bridge here to make sure it is not `nil` go ahead:
print("rnBridge = \(rnBridge)")
//2. actually send the event through the eventDispatcher
rnBridge?.eventDispatcher().sendAppEvent(withName: "test", body: "testBody data!!!")
}
}
in AppDelegate.h put (additionally to the code that was already in the file)
#import "YourProjectsBridgingHeaderToMakeThisCodeAvailableInSwift.h" //replace this with your actual header you created when creating a swift file (google it if you dont know how to bridge swift to objective c)
#interface PublicBridgeHelper: NSObject
-(RCTBridge*)getBridge;
#end
in AppDelegate.m put (in addition to the code that was already in the file)
#import <React/RCTRootView.h>
RCTBridge *rnBridgeFromRootView;
#implementation PublicBridgeHelper //this is created to SIMPLY return rnBridgeFromRootView defined above over to my Swift class when actually sending the event to javascript that defines a react native Component
-(RCTBridge*)getBridge {
NSLog(#"rnBridgeFromRootView = #%#", rnBridgeFromRootView);
return rnBridgeFromRootView;
}
important - also make sure to add the following line of code to the Objective C .h's bridging header to make this PublicBridgeHelper definition available to be used in the .swift code
#import "AppDelegate.h"
finally,
now to show you how to set the rnBridgeFromRootView variable used in AppDelegate.m (that gets returned and used in the .swift code right before sending the event to javascript)
open AppDelegate.m and in the method body of
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... }
include the following after the line of code that instantiates the rootView variable
i.e. after the line that probably looks like
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:#"YourProjecNameProbably" initialProperties:nil launchOptions:launchOptions];
add:
rnBridgeFromRootView = rootView.bridge //set the bridge to be exposed and returned later and used by the swift class
Now to explain the publicBridgeHelperInstance.getBridge() part that is in the .swift file
publicBridgeHelper is an instance of an objective c class which allows the swift class ability to get a reference to the react native bridge
If you are still having problems understanding my answer after reading this I made a video over it and you can watch it here:
https://www.youtube.com/watch?v=GZj-Vm9cQIg&t=9s

How can I access the DOM of a <webview> in Electron?

I'm just getting started with Electron, with prior experience with node-webkit (nw.js).
In nw.js, I was able to create iframes and then access the DOM of said iframe in order to grab things like the title, favicon, &c. When I picked up Electron a few days ago to port my nw.js app to it, I saw advice to use webviews instead of iframes, simply because they were better. Now, the functionality I mentioned above was relatively easy to do in nw.js, but I don't know how to do it in Electron (and examples are slim to none). Can anyone help?
Also, I have back/forward buttons for my webview (and I intend on having more than one). I saw in the documentation that I could call functions for doing so on a webview, but nothing I have tried worked either (and, I haven't found examples of them being used in the wild).
I dunno who voted to close my question, but I'm glad it didn't go through. Other people have this question elsewhere online too. I also explained what I wanted to achieve, but w/e.
I ended up using ipc-message. The documentation could use more examples/explanations for the layperson, but hey, I figured it out. My code is here and here, but I will also post examples below should my code disappear for whatever reason.
This code is in aries.js, and this file is included in the main renderer page, which is index.html.
var ipc = require("ipc");
var webview = document.getElementsByClassName("tabs-pane active")[0];
webview.addEventListener("ipc-message", function (e) {
if (e.channel === "window-data") {
// console.log(e.args[0]);
$(".tab.active .tab-favicon").attr("src", e.args[0].favicon);
$(".tab.active .tab-title").html(e.args[0].title);
$("#url-bar").val(e.args[0].url);
$("#aries-titlebar h1").html("Aries | " + e.args[0].title);
}
// TODO
// Make this better...cancel out setTimeout?
var timer;
if (e.channel === "mouseover-href") {
// console.log(e.args[0]);
$(".linker").html(e.args[0]).stop().addClass("active");
clearTimeout(timer);
timer = setTimeout(function () {
$(".linker").stop().removeClass("active");
}, 1500);
}
});
This next bit of code is in browser.js, and this file gets injected into my <webview>.
var ipc = require("ipc");
document.addEventListener("mouseover", function (e) {
var hoveredEl = e.target;
if (hoveredEl.tagName !== "A") {
return;
}
ipc.sendToHost("mouseover-href", hoveredEl.href);
});
document.addEventListener("DOMContentLoaded", function () {
var data = {
"title": document.title,
"url": window.location.href,
// need to make my own version, can't rely on Google forever
// maybe have this URL fetcher hosted on hikar.io?
"favicon": "https://www.google.com/s2/favicons?domain=" + window.location.href
};
ipc.sendToHost("window-data", data);
});
I haven't found a reliable way to inject jQuery into <webview>s, and I probably shouldn't because the page I would be injecting might already have it (in case you're wondering why my main code is jQuery, but there's also regular JavaScript).
Besides guest to host IPC calls as NetOperatorWibby, it is also very useful to go from host to guest. The only way to do this at present is to use the <webview>.executeJavaScript(code, userGesture). This api is a bit crude but it works.
If you are working with a remote guest, like "extending" a third party web page, you can also utilize webview preload attribute which executes your custom script before any other scripts are run on the page. Just note that the preload api, for security reasons, will nuke any functions that are created in the root namespace of your custom JS file when your custom script finishes, however this custodial process will not nuke any objects you declare in the root. So if you want your custom functions to persist, bundle them into a singleton object and your custom APIs will persist after the page fully loads.
[update] Here is a simple example that I just finished writing: Electron-Webview-Host-to-Guest-RPC-Sample
This relates to previous answer (I am not allowed to comment): Important info regarding ipc module for users of Electron 1.x:
The ipc module was split into two separate modules:
ipcMain for the main process
ipcRenderer for the renderer process
So, the above examples need to be corrected, instead of
// Outdated - doesn't work in 1.x
var ipc = require("ipc");
use:
// In main process.
var ipcMain = require('electron').ipcMain
And:
// In renderer process.
var ipcRenderer = require('electron').ipcRenderer
See: http://electron.atom.io/blog/2015/11/17/electron-api-changes section on 'Splitting the ipc module'

notification.confirm callback not firing phonegap iOS

I'm using Phonegap (Cordova) v1.9, and this app is solely for iPhone4+ use.
I've tried the two methods below, on their own and simultaneously.
Phonegap's deviceready event has been fired previous to this code.
I'm using the iPhone configuration utility to watch the console, but I'm not getting anything from the code below.
Also, I'm using Sencha Touch (v1.1) and jQuery. No conflicts noticed throughout the app.
Please help?
function onConfirmCallback(button){
console.log("onConfirmCallback");
console.log(button);
}
var toConfirm = navigator.notification.confirm("You don't have a pincode set.\nWould you like to set one now?", onConfirmCallback, "Pincode", "Yes,No");
toConfirm.onAlertDismissed = function(index, label){
console.log("onAlertDismissed");
console.log(index);
}
Have you tried looking for exceptions to see what might be happening?
try
{
var toConfirm = navigator.notification.confirm("You don't have a pincode set.\nWould you like to set one now?", onConfirmCallback, "Pincode", "Yes,No");
toConfirm.onAlertDismissed = function(index, label) {
console.log("onAlertDismissed");
console.log(index);
}
}
catch (ex)
{
console.log(ex.message);
}
Edit:
The example mentioned in the comment below (from https://groups.google.com/group/phonegap/browse_thread/thread/126f56f4e296e555) is for PhoneGap 0.9.2. I have not seen any examples for v1.3 where the confirm function returns an object as your using with 'toConfirm'. Maybe that is causing your problem. Have you tried the following (removing all other code to do with toConfirm to simplify the problem)?
navigator.notification.confirm("You don't have a pincode set.\nWould you like to set one now?", onConfirmCallback, "Pincode", "Yes,No");
http://docs.phonegap.com/en/1.3.0/phonegap_notification_notification.md.html#notification.confirm
Also, is there any reason your not using a newer version of PhoneGap (Cordova)?

Functions registered with ExternalInterface.addCallback not available in Javascript

I'm working on a Flash game that needs to call some Javascript on the page and get data back from it. Calling Javascript from Flash works. Calling the Flash functions from Javascript (often) doesn't.
I'm using the Gaia framework.
What happens:
The swf is loaded in with SWFObject
There's a button in the Flash file. On click, it uses ExternalInterface.call() to call a Javascript function. This works.
The Javascript function calls a Flash function that was exposed with ExternalInterface.addCallback().
Sometimes, the Javascript produces the following error: TypeError: myFlash.testCallback is not a function.
When the error happens, it affects all functions registered with addCallback(). Gaia and some of its included libraries use addCallback(), and calling those functions from Javascript also produces the TypeError.
Waiting a long time before pressing the button in Flash doesn't solve the error.
Having Flash re-try addCallback() periodically doesn't solve the error
When the error occurs, ExternalInterface.available = true and ExternalInterface.objectID contains the correct name for the Flash embed object.
When the error occurs, document.getElementById('myflashcontent') correctly returns the Flash embed object.
Edited to add:
This issue shows up in Firefox 3.6, but not Chrome or IE8. I haven't tried older browsers.
I'm running the Debug version of the Flash player.
My calls to ExternalInterface.addCallback() are wrapped in a try...catch block. When the JS error occurs, the catch block is not triggered. It's a silent failure.
The error occurs when testing on a webhost, with the swf loaded from the same server as the page it's on.
I set allowScriptAccess = always.
Setting flash.system.Security.allowDomain("mydomain") doesn't fix the error.
From my Page class:
public class MyPage extends AbstractPage
{
// declarations of stage instances and class variables
// other functions
override public function transitionIn():void
{
send_button.addEventListener(MouseEvent.MOUSE_UP, callJS);
exposeCallbacks();
super.transitionIn();
}
private function exposeCallbacks():void
{
trace("exposeCallbacks()");
if (ExternalInterface.available) {
trace("ExternalInterface.objectID: " + ExternalInterface.objectID);
try {
ExternalInterface.addCallback("testCallback", simpleTestCallback);
trace("called ExternalInterface.addCallback");
}
catch (error:SecurityError) {
trace("A SecurityError occurred: " + error.message + "\n");
}
catch (error:Error) {
trace("An Error occurred: " + error.message + "\n");
}
}
else {
trace("exposeCallbacks() - ExternalInterface not available");
}
}
private function simpleTestCallback(str:String):void
{
trace("simpleTestCallback(str=\"" + str + "\")");
}
private function callJS(e:Event):void
{
if (ExternalInterface.available) {
ExternalInterface.call("sendTest", "name", "url");
}
else {
trace("callJS() - ExternalInterface not available");
}
}
}
My Javascript:
function sendTest(text, url) {
var myFlash = document.getElementById("myflashcontent");
var callbackStatus = "";
callbackStatus += '\nmyFlash[testCallback]: ' + myFlash['testCallback'];
//console.log(callbackStatus);
var errors = false;
try {
myFlash.testCallback("test string");
}
catch (err) {
alert("Error: " + err.toString());
error = true;
}
if (!error) {
alert("Success");
}
}
var params = {
quality: "high",
scale: "noscale",
wmode: "transparent",
allowscriptaccess: "always",
bgcolor: "#000000"
};
var flashVars = {
siteXML: "xml/site.xml"
};
var attributes = {
id: "myflashcontent",
name: "myflashcontent"
};
// load the flash movie.
swfobject.embedSWF("http://myurl.com/main.swf?v2", "myflashcontent",
"728", "676", "10.0.0", serverRoot + "expressInstall.swf",
flashVars, params, attributes, function(returnObj) {
console.log('Returned ' + returnObj.success);
if (returnObj.success) { returnObj.ref.focus(); }
});
Calls made to JS via ExternalInterface are wrapped within a try { } block and that causes subsequent JS errors to get suppressed.
A workaround for the same is to cause a function closure in JavaScript and execute the actual code after a timeout.
Example:
function myFnCalledByEI (arg1, arg2) {
setTimeout(myActualFunction () {
// You can use arg1 and arg2 here as well!
// Errors raised within this function will not be
// suppressed.
}, 0);
};
Here was our scenario once we narrowed down all the conditions:
Only on FireFox/Windows
Only when wmode=transparent
Only when using the js alert() function
In this specific scenario, ExternalInterface.call() would not fire right away. It only worked after creating a tiny delay with Timer class.
If we made wmode=window, or removed the alert() - everything worked. Try using console.log() to display debug text in firebug.
The other gotcha? Whether your js function returns an array or object vs a string. Surprisingly returning the native js array was interpreted by an array in Flash. Try outputting info about your return data like this:
var myRetVal = flash.external.ExternalInterface.call("my_js_func");
debug_txt.text = flash.utils.describeType(myRetVal).toString();
This might be similar to the issue you are experiencing.
http://www.google.com/support/forum/p/translator-toolkit-api/thread?tid=58cda1b34ae1e944&hl=en
You can see that type of error message if something wrong happened in Flash while making the call. Something like an uncaught exception.
Are you running the debug version of the player? That might give you more information about what's going on.
Also, is this consistent across browsers? I've seen old versions of IE having trouble accepting several consecutive Flash <-> JS calls.
J
I tryied your code, and It worked ok if I place an alert before everything, so, I thinkg it is something related to some kind of time you have to wait.
Have you tried this in JavaScript?
if (myFlash)
{
if (!myFlash.testCallback)
{
if (__flash__addCallback)
{
__flash__addCallback( myFlash, "testCallback" );
}
else
{
console.log("Error: Flash External Interface injected JavaScript function not found. The external interface function won't work.");
}
}
}
myFlash.testCallback("test string");
I had used this in many cases.
Again at certain places I had to redefine __flash_addCallback and _flash_removeCallback functions to minimize the errors.
Presently I do not remember what I did for __flash_addCallback, but this is what I did for the latter:
if (__flash__removeCallback)
{
__flash__removeCallback = function (instance, name) {if(instance && instance[name]) instance[name] = null;
}
We've run into the same problem, and only in Firefox.
Following the advice given by THM we were able to find a solution.
In our case, the swf being inserted was inside a div being animated into view with jQuery.slideDown(). This apparently causes it to sometimes reboot while being started. In some cases this led to the callback functions not being available.
Fixed by calling swfobject.embedSWF only after the slideDown effect had finished.
I was getting this error because I had the same Flash file on the page multiple times. Even though each one of them had a different id/name, they were all using the same callback.

Categories

Resources