Open burtonator opened 8 years ago
A change in the DOM.
The input where one writes their message used to be a <textarea>
and now it is an editable <div>
. Before I was able to take the data out of the <textarea>
and replace it with the encrypted version. Now doing that does not work. And I am having to automate the keypress of an editable <div>
.
Auto-Encrypt Before I was able to manipulate(encrypt) the message before it was sent to Facebook on the Enter keypress. Now I have to prevent the code being submitted and then encrypt it. But I still need to be able to submit the message automatically after encrypting it by simulating the enter press.
Is it possible to bring up a local textarea or use the extension to generate a text area, then edit it there, then write it to he div encrypted?
Interesting attempt at a block. Seems eventually possible to get past.
Yes, it is! Someone mentioned that using an <iframe>
to my server which has a <textarea>
might even be a good idea! But I can't do any of that until I am able to automate an Enter keydown! :-1:
Yes I think it will be but I need help!
Hi Max,
I think you might be over-complicating this a bit. I'm the author of a similar tool (exactly for facebook) and it has been working for the past 2 years (the main difference between our implementations is that my doesn't use a preset password).
I assume you made it into a Chrome extension because you couldn't add an event listener to the ENTER key before the event listeners that Facebook adds itself. Having said this, I don't see the reason why you are using this to prevent the default event bubbling. Why not just replace the inner plain text with the encrypted one like this and then let Facebook handle everything else?
@maxisme
Ah.. ok. Well I have a work around / potential solution for you. What you can do is generate a raw event and fire it which generates the keyboard event. I have some that does this for phantomjs and it works for DOM across the board. I can try to pull it out of mothballs if you like but it might take me a while.
Long story short, you re-create the actual DOM event and make a synthetic one and send it.. works fine.
Hello Pavel,
Thank you for getting in touch!
Yes I think I probably am... Haha. Oh awesome. Have you tested it in the last couple of days? From what I can see that seems to be the method I used to have before facebook changed their DOM.
I have tried your method by replacing this with $(node).val(encrypted);
(what I was doing before facebook changed everything) and removing the "preventing default" stuff. But no luck :-1:
Hi Max,
That's kinda weird. I haven't tested the last couple of days (I'm on holiday - yay!) but will do this weekend and let you know.
@burtonator Sounds brilliant. That would be so useful, if you don't mind! I am having a problem finding the actual DOM event though!?
@sadreck Oh nice! Hope you are having nice time. Yes please let me know. Hopefully will have a fix by then and I will let you know!!
@sadreck we both live in Brighton... Weird.
@maxisme it's where all great ideas are born! haha
@maxisme What you should do is to set a breakpoint in your code and then look at the event that was created. Then you just duplicate it.
Here's something similar. I think I'm doing this for mouse events but this should be a good starting point for you:
var event = document.createEvent('MouseEvent');
// NOTE: it turns out that we don't need to use bounding rect because we're
// calling element.dispatchEvent which places the event directly on the
// element we're interested in regardless of the position of the mouse
// pointer. I want this disabled because there are some race conditions
// and setting this to left=0, top=0 means that I don't need to worry about
// issues like the page elements being redrawn and shifted.
var useBoundingRect = false;
var rect = { left: 0, top: 0 };
if ( useBoundingRect ) {
rect = element.getBoundingClientRect();
}
// this method has a large number of parameters but for our usage
// its pretty simple. We only really care about the last param,
// the element
event.initMouseEvent(
eventName, //event type : click, mousedown, mouseup, mouseover, mousemove, mouseout.
true, //canBubble
false, //cancelable
window, //event's AbstractView : should be window
1, // detail : Event's mouse click count
rect.left, // screenX
rect.top, // screenY
rect.left, // clientX
rect.top, // clientY
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
0, // button : 0 = click, 1 = middle button, 2 = right button
element // relatedTarget : Only used with some event types (e.g. mouseover and mouseout). In other cases, pass null.
);
element.dispatchEvent( event );
@burtonator Thank you! Is setting a breakpoint in the code supposed to help me find out the function that facebook use?
@maxisme Sorry. I meant to say you can add an event listener for [enter] then set a break point, look at the event that is sent. Then you can create a synthetic one and use it with dispatchEvent.
@maxisme That should definitely work.. wonder if there is some issue with bubbling or something along those lines. My strength is Java backend and while I do know enough to get bloody in the Javascript/browser world it's not my forte... but I think this would be the right path to go down.
@burtonator When monitoring evt.keyCode
in my document.addEventListener('keydown', function(evt){
it returns 0
not 13 when running the code:
var keyboardEvent = document.createEvent("KeyboardEvent");
var initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
keyboardEvent[initMethod](
"keydown", // event type : keydown, keyup, keypress
true, // bubbles
false, // cancelable
window, // viewArg: should be window
false, // ctrlKeyArg
false, // altKeyArg
false, // shiftKeyArg
false, // metaKeyArg
13, // keyCodeArg : unsigned long the virtual key code, else 0
0 // charCodeArgs : unsigned long the Unicode character associated with the depressed key, else 0
);
document.dispatchEvent(keyboardEvent);
So obviously facebook are blocking the keycode 13... I did find:
__d('SyntheticKeyboardEvent', ['SyntheticUIEvent', 'getEventCharCode', 'getEventKey', 'getEventModifierState'], function a(b, c, d, e, f, g, h, i, j, k) {
'use strict';
if (c.__markCompiled)
c.__markCompiled();
var l = {
key: j,
location: null ,
ctrlKey: null ,
shiftKey: null ,
altKey: null ,
metaKey: null ,
repeat: null ,
locale: null ,
getModifierState: k,
charCode: function(event) {
if (event.type === 'keypress')
return i(event);
return 0;
},
keyCode: function(event) {
if (event.type === 'keydown' || event.type === 'keyup')
return event.keyCode;
return 0;
},
which: function(event) {
if (event.type === 'keypress')
return i(event);
if (event.type === 'keydown' || event.type === 'keyup')
return event.keyCode;
return 0;
}
};
function m(n, o, p, q) {
h.call(this, n, o, p, q);
}
h.augmentClass(m, l);
f.exports = m;
}, null );
here. But I am not sure whether it is at all linked...
Also the sendkeys function is not picked up by the keydown which is interesting...
I hate Facebook scripts...
I have also tried:
var $textBox = $(node);
var keycode = 13;
var press = jQuery.Event("keydown");
press.altGraphKey = false;
press.altKey = false;
press.bubbles = true;
press.cancelBubble = false;
press.cancelable = false;
press.charCode = keycode;
press.clipboardData = undefined;
press.ctrlKey = false;
press.currentTarget = $textBox[0];
press.defaultPrevented = true;
press.detail = 0;
press.eventPhase = 2;
press.keyCode = keycode;
press.keyIdentifier = "";
press.keyLocation = 0;
press.layerX = 0;
press.layerY = 0;
press.metaKey = false;
press.pageX = 0;
press.pageY = 0;
press.returnValue = true;
press.shiftKey = false;
press.srcElement = $textBox[0];
press.target = $textBox[0];
press.type = "keydown";
press.view = Window;
press.which = keycode;
$textBox.trigger(press);
I am starting to think that automating the enter key is not the way I should be going. Instead I should be trying to replace the inputted text before it is sent.
The issue is that the text used to submit to the chat on the keypress is not actually from here it is stored somewhere! (dodgy I know.)
Hey, I've been messing around with the changes Facebook made aswell, without any luck.. It looks like they're using their own kind of Event Subscriber and calling ".AddListener" which is a function they created. They then generate their own custom events "BaseEventEmitter". Facebook uses .keyCode and .which which are both read only properties of events, doesn't look like they do any checks to see if its an actual event to so I'm trying to just create an object with all the same properties and functions and use that. I tried renaming the addEventListener function on all types of elements hoping I could intercept the event on all listeners inside the page and maybe pass a custom Object through.
Run the following in the context of the page before any of the Facebook scripts begin executing. Disclaimer : I still haven't managed to fix this but maybe this will help
var _interfaces = [ HTMLDivElement, HTMLImageElement, HTMLUListElement, HTMLElement, HTMLDocument ];
for (var i = 0; i < _interfaces.length; i++) {
// pass in original addEventListener
(function(original) {
// rename EventListener
_interfaces[i].prototype.addEventListener = function(a,b,c){
// if its a key event change its values
if(a == 'keydown' || a == 'keypress' || a == 'keyup' || a == 'input'){
console.log(arguments);
var listenerElem = this;
var intercept = (function () {
var simEv = {};
for(var i in arguments[0]){
simEv[i] = arguments[0][i];
}
console.log('intercepted', arguments, simEv);
arguments[0].preventDefault();
arguments[0].stopPropagation();
b.apply(listenerElem, [simEv]);
});
original.apply(this, [a, intercept, c])
} else {
// if not key event pass normal arguments
original.apply(this, arguments)
}
}
})(_interfaces[i].prototype.addEventListener);
}
@Angus-McLean Awesome!! That looks sweet! Thank you so much. I will have a go too and will let you know if I find anything! I have been trying for so long though haa. Your method seems like the write direction for sure! I have been using chrome to stop before submitting the message and go through all the (illegible) events!
The only problem with this is that if they change it again... then you have to reverse engineer it again. So what is really needed is a generic event capture system that will work no matter what they do as the system evolves.
Not trying to be too pessimistic.. I realize that this was a lot of hard work.
I think what @maxisme is suggesting could be on the right lines. I overrode Facebook's event managing system (Arbiter) and it seemed to work quite well. You could also just monkey patch the XMLHttpRequest prototype maybe? https://github.com/TobyColeman/Whisper/tree/master/src/js/app/injected (shameless plug)
@TobyColeman @Angus-McLean @sadreck @burtonator This is driving me crazy. I am happy to put a £50 (student price) bounty on this haha!
Any news on this? I found the project today and am totally gutted that its down.
Not sure how much this helps, but the MessengerComposer react component used to send facebook chat messages calls a function named _handleMessageSend on its component instance, which then crafts a message and calls the _sendMessage function also on its own instance to send the message. Would it be possible to replace the message in this component before 'n' is constructed? Assuming the parameter k is the message.
_handleMessageSend: function(k, l) {
if (!this.props.canReply)
return false;
return this._sendMessage(function(m) {
var n = this._messageBuilder.constructUserGeneratedMessageObject(k, c('MercurySourceType').MESSENGER_WEB, m);
if (l)
if (l.shareable_attachment)
n.shareable_attachment = l.shareable_attachment;
return n;
}
.bind(this));
}
I was able to set the _handleMessageSent function to a global variable through chrome dev tools and call it with a string as the only argument. The result was the string successfully sent through the chat. If we can get the instance of this function for a chatbox, we could pass our encrypted text to it and send the message I believe.
Hello @Tasemu. Thank you for your interest! That looks perfect... Unfortunately Crypter is for facebook.com rather than messenger.com :-1: but I believe that there is something similar to what you are attempting that @Angus-McLean mentioned above! Let me know if you have any more ideas! And thanks again.
I found the facebook function where they send the message. It is on line 887
(formatted) in https://static.xx.fbcdn.net/rsrc.php/v2iDeF3/yr/l/en_US/9dfBYy78YgQ.js (pastebin) . There is a variable n
which holds the content of the message body:"test"
where test
is the message.
I have tried changing the body
to hello and then resuming the script but facebook still sends the first message test I don't know whether that is because you can't do that with chrome. Or whether the message is assigned somewhere else? But I think we are getting very close. This is all thanksed to you @Tasemu I started searching facebooks code for _sendMessage
thank you!
Is it possible to just alter the REST API call directly? I am wondering if you can monkey patch the XHR layer by copying the current functon to a new variable, then providing your own implementation.
This way when they call XHR you can see the data, and the API endpoint, and rewrite it.
I am sorry @burtonator but I do not understand a word of that! Haha. Pardon my ignorance… I will get researching.
Thank you!
Maximilian Mitchell maxis.me (http://maxis.me)
On Tuesday, 15 March 2016 at 19:20, Kevin Burton wrote:
Is it possible to just alter the REST API call directly? I am wondering if you can monkey patch the XHR layer by copying the current functon to a new variable, then providing your own implementation. This way when they call XHR you can see the data, and the API endpoint, and rewrite it.
— You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub (https://github.com/maxisme/crypter/issues/4#issuecomment-196982855)
Okay so I have found the function where if I manipulate the variable like above it will modify the message sent!! It is on line 8936 of https://static.xx.fbcdn.net/rsrc.php/v2i_Sq3/yp/l/en_US/qHGRnnkBbvr.js (http://pastebin.com/Z20ef3Nj)
Here is the latest version of Crypter for Chrome using iframes if you are interested. The only major thing left to do is regarding this topic. Currently with this extension you have to press enter twice; first time it creates a lock, second time to send the lock.
Oh wicked! i'm a little confused though, does this version work? Or is facebook still blocking it
Just gave it a shot then, seems to be working! oh man you legend!
Hey guys I have finally finished a new version of Crypter for Chrome but I believe it is quite buggy and would really appreciate you testing it if you have a second. Thanks again for all the help. Oh and the code is on the Crypter 2.0 branch.
How did the block technically work?
Did they block ALL encrypted messages or did they somehow just block the extension? How are they blocking the extension when it isn't included on their site?