Metatrader compiler denies to compile decompiled code

Metaquotes┬┤s response to decompilers

Since Metatrader Build 500 has been released, all updates are mandatory. This also means that compilers will be mandatory and this will again means that >=500 compiled code may stop working on older builds of Metatrader.

Since Build 500 Metaquotes detect decompiled projects and denies to compile them. Unfortunetly this is very weak protection because decompiler community reacted to Metaquote┬┤s action right away with a patch.

To demonstrate this we let decompile someone in community ‘Moving Average.mq4’ Expert Advisor to compare action and re-action:

Previous decompiled version


extern double Lots = 0.1;
extern double MaximumRisk = 0.02;
extern double DecreaseFactor = 3.0;
extern double MovingPeriod = 12.0;
extern double MovingShift = 6.0;

int f0_0(string as_unused_0) {
   int count_8 = 0;
   int count_12 = 0;
   for (int pos_16 = 0; pos_16 < OrdersTotal(); pos_16++) {
      if (OrderSelect(pos_16, SELECT_BY_POS, MODE_TRADES) == FALSE) break;
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == 20050610) {
         if (OrderType() == OP_BUY) count_8++;
         if (OrderType() == OP_SELL) count_12++;
      }
   }
   if (count_8 > 0) return (count_8);
   return (-count_12);
}
	 		   	 	 			   		  			  	 	  			   	  				  	 				 	 		 				        				     	  	     		  			     	 		    	  	    		   			 		 	    	 		 	      
double f0_3() {
   double lots_0 = Lots;
   int hist_total_8 = OrdersHistoryTotal();
   int count_12 = 0;
   lots_0 = NormalizeDouble(AccountFreeMargin() * MaximumRisk / 1000.0, 1);
   if (DecreaseFactor > 0.0) {
      for (int pos_16 = hist_total_8 - 1; pos_16 >= 0; pos_16--) {
         if (OrderSelect(pos_16, SELECT_BY_POS, MODE_HISTORY) == FALSE) {
            Print("Error in history!");
            break;
         }
         if (OrderSymbol() != Symbol() || OrderType() > OP_SELL) continue;
         if (OrderProfit() > 0.0) break;
         if (OrderProfit() < 0.0) count_12++;
      }
      if (count_12 > 1) lots_0 = NormalizeDouble(lots_0 - lots_0 * count_12 / DecreaseFactor, 1);
   }
   if (lots_0 < 0.1) lots_0 = 0.1;
   return (lots_0);
}
	 	 	 	 		   									  	 		  	  	 					 		 	  	 		 			   	  				  		    	 		 	 	 			 		 	 	 	 		  	    	  	  	 	  	 						 		 	   				   			 			
void f0_2() {
   double ima_0;
   int ticket_8;
   if (Volume[0] <= 1.0) {
      ima_0 = iMA(NULL, 0, MovingPeriod, MovingShift, MODE_SMA, PRICE_CLOSE, 0);
      if (Open[1] > ima_0 && Close[1] < ima_0) ticket_8 = OrderSend(Symbol(), OP_SELL, f0_3(), Bid, 3, 0, 0, "", 20050610, 0, Red);
      else
         if (Open[1] < ima_0 && Close[1] > ima_0) ticket_8 = OrderSend(Symbol(), OP_BUY, f0_3(), Ask, 3, 0, 0, "", 20050610, 0, Blue);
   }
}
	 	 				 	    	  				  	  		 					 		 	 			 		  			 	 			 	   	    			 			 					  					 		 	       	 	 	   	 			   	  	  		 	   	  		 			 					  
void f0_1() {
   double ima_0;
   if (Volume[0] <= 1.0) {
      ima_0 = iMA(NULL, 0, MovingPeriod, MovingShift, MODE_SMA, PRICE_CLOSE, 0);
      for (int pos_8 = 0; pos_8 < OrdersTotal(); pos_8++) {
         if (OrderSelect(pos_8, SELECT_BY_POS, MODE_TRADES) == FALSE) break;
         if (OrderMagicNumber() != 20050610 || OrderSymbol() != Symbol()) continue;
         if (OrderType() == OP_BUY) {
            if (!(Open[1] > ima_0 && Close[1] < ima_0)) break;
            OrderClose(OrderTicket(), OrderLots(), Bid, 3, White);
            return;
         }
         if (OrderType() == OP_SELL) {
            if (!(Open[1] < ima_0 && Close[1] > ima_0)) break;
            OrderClose(OrderTicket(), OrderLots(), Ask, 3, White);
            return;
         }
      }
   }
}
		   	    	 	  	   	  	  	 			  	 		   		    				       		  	  	 			 		 	 		 	   	 	 	 		 			 		 						  					   				  	      						    		 	 	 	 
void start() {
   if (Bars < 100 || IsTradeAllowed() == FALSE) return;
   if (f0_0(Symbol()) == 0) {
      f0_2();
      return;
   }
   f0_1();
}

Latest decompiled version

extern double Lots = 0.1;
extern double MaximumRisk = 0.02;
extern double DecreaseFactor = 3.0;
extern double MovingPeriod = 12.0;
extern double MovingShift = 6.0;

// 3B4FF059E3EA91105A3BBDBF7628BC6B
int f0_0(string Grps_unused_0) {
   int count_8 = 0;
   int count_12 = 0;
   for (int pos_16 = 0; pos_16 < OrdersTotal(); pos_16++) {
      if (OrderSelect(pos_16, SELECT_BY_POS, MODE_TRADES) == FALSE) break;
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == 20050610) {
         if (OrderType() == OP_BUY) count_8++;
         if (OrderType() == OP_SELL) count_12++;
      }
   }
   if (count_8 > 0) return (count_8);
   return (-count_12);
}
	  	  		 						  	   	 	    	 					  		 		 	    		 	 				  				   	    				   		      	 			 		    	 	  	  	 	 		  	 			  	 	 	  	 	  				     	  
// EBFE91FAEB07FF5788FD1001AD46AE29
double f0_3() {
   double lots_0 = Lots;
   int hist_total_8 = OrdersHistoryTotal();
   int count_12 = 0;
   lots_0 = NormalizeDouble(AccountFreeMargin() * MaximumRisk / 1000.0, 1);
   if (DecreaseFactor > 0.0) {
      for (int pos_16 = hist_total_8 - 1; pos_16 >= 0; pos_16--) {
         if (OrderSelect(pos_16, SELECT_BY_POS, MODE_HISTORY) == FALSE) {
            Print("Error in history!");
            break;
         }
         if (OrderSymbol() != Symbol() || OrderType() > OP_SELL) continue;
         if (OrderProfit() > 0.0) break;
         if (OrderProfit() < 0.0) count_12++;
      }
      if (count_12 > 1) lots_0 = NormalizeDouble(lots_0 - lots_0 * count_12 / DecreaseFactor, 1);
   }
   if (lots_0 < 0.1) lots_0 = 0.1;
   return (lots_0);
}
	   			  		   		 	 		      	 		 					 				  		 			  	 	 	     		  				  							    									   	  		 	    		 		   		  		 	  	  		 			 	 	  					 
// CB17DEBBAA4E3FCCC1627B748C11F14C
void f0_2() {
   double ima_0;
   int ticket_8;
   if (Volume[0] <= 1.0) {
      ima_0 = iMA(NULL, 0, MovingPeriod, MovingShift, MODE_SMA, PRICE_CLOSE, 0);
      if (Open[1] > ima_0 && Close[1] < ima_0) ticket_8 = OrderSend(Symbol(), OP_SELL, f0_3(), Bid, 3, 0, 0, "", 20050610, 0, Red);
      else
         if (Open[1] < ima_0 && Close[1] > ima_0) ticket_8 = OrderSend(Symbol(), OP_BUY, f0_3(), Ask, 3, 0, 0, "", 20050610, 0, Blue);
   }
}
	 		 	   	 		  	 		   	   	 		  		     					 							    	 			  	     		 		   	    	  	 			  	 		    			     		      	  	 			  			       	 	  	 	 
// 792CA38593D2232BE04E6146E4A427A1
void f0_1() {
   double ima_0;
   if (Volume[0] <= 1.0) {
      ima_0 = iMA(NULL, 0, MovingPeriod, MovingShift, MODE_SMA, PRICE_CLOSE, 0);
      for (int pos_8 = 0; pos_8 < OrdersTotal(); pos_8++) {
         if (OrderSelect(pos_8, SELECT_BY_POS, MODE_TRADES) == FALSE) break;
         if (OrderMagicNumber() != 20050610 || OrderSymbol() != Symbol()) continue;
         if (OrderType() == OP_BUY) {
            if (!(Open[1] > ima_0 && Close[1] < ima_0)) break;
            OrderClose(OrderTicket(), OrderLots(), Bid, 3, White);
            return;
         }
         if (OrderType() == OP_SELL) {
            if (!(Open[1] < ima_0 && Close[1] > ima_0)) break;
            OrderClose(OrderTicket(), OrderLots(), Ask, 3, White);
            return;
         }
      }
   }
}
									  	  	 	 	 	  				  			    	 	   				    			 		 			  	 		  		 	    							 			         		   	 			   					    	 	 			    	  	 		 		 			 	
// EA2B2676C28C0DB26D39331A336C6B92
void start() {
   if (Bars < 100 || IsTradeAllowed() == FALSE) return;
   if (f0_0(Symbol()) == 0) {
      f0_2();
      return;
   }
   f0_1();
}

Previous version does not work but latest version of decompilation compiles with latest Metatrader compiler well. Obvisouly decompiler community has found a way to skip the basic try from Metaquotes. We think this try to solve this decompilation problem from Metaquotes as good start to go towards right direction. Metaquotes needs to be more creative and decompilation may be more hard then now. The good thing is; there are thousands of decompiled code on many computers, they will stop getting compiled with 500 Build.

For detailed report what has been changed you may check this link

It looks like it was not easy to skip new anti-decompiler measurements from Metaquote for decompiler community if you see the report. Obvisouly they have added some random comments to source code and prefixed first functions parameter part with random chars. We expect response from Metaquotes to this re-action in next builds. We will be watching this competition.

Date of report: Jun 13, 2013

Update July 2013

Since version 509 MT4 throws an Error Message about detected decompiled source code “using of decompiled source code is prohibited”:

It seems that the fight continious but we believe that its not easy for MQ to win this game without changing the essential .ex4 format. Changing EX4 format while there are hundrets of thousands running Ex4 versions is not easy. Obviously they try to fight the new compilations first rather then fighting EX4 formats. Actually after compilation it will probably not possible to detect that the source was decompiled or original. This means that if they change the EX4 format (which we dont believe they will do) then all “legal” compiled software will also stop working. This would make the community really upset.

We know that decompiler community already have new versions out to market which would obfuscate the naming to avoid detecting. MQ can basically try to detect the decompiled version based on naming but its also possible to obfuscate this from decompiler perspective.

Wrong Detections

We are hosting thousands of EAs from clients, with their allowness we sometimes debug some issues and have seen that latest measurements from MetaQuotes does not detect decompiled ones correctly. We have seen several cases where detection was wrong. This is interesting fact to know.

Posted in Metatrader4