0x01 反调试绕过

在4399游戏页面按下F12无反应,原因可能是捕捉了该键位。不过可以在chrome的More Tools中正常打开控制台。

然而接下来Debugger会一直卡在一个匿名函数处;即使continue也会触发新的函数.

1
2
3
4
5
6
7
8
9
10
11
// VM198
(function anonymous(
) {
debugger
})
// VM224
(function anonymous(
) {
debugger
})
// ...

全局搜索关键词debugger, 找到了可疑代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// ......
// Antiindulgence.js?20268251
	if(window.location.host=="www.4399.com" || window.location.host=="h.4399.com"  || window.location.host=="zuopin.4399.com" || window.location.host=="h.api.4399.com" || window.location.host=="zxwyouxi.com"){
        // ----- 屏蔽快捷键基本没有影响 -----
		//屏蔽F12
		$(document).keydown(function(event){
			if(event.keyCode==123){
				if(event.preventDefault){
					event.preventDefault();
				}else{
					window.event.returnValue == false;
				}
			}
		}); 
		//屏蔽ctrl+shift+i
		$(document).keydown(function(event){
		  if(event.ctrlKey && event.shiftKey && event.keyCode==73){
				if(event.preventDefault){
					event.preventDefault();
				}else{
					window.event.returnValue == false;
				}
		  }
		});
        // ----- 防止我们设置never stop at ... ----
		// 为右键添加自定义事件,可以禁用
		$(document).ready(function(){
			$(document).bind("contextmenu",function(e){
				return false;
			});
		});


		//疯狂调试模式
		setInterval(function(){
			check();
		}, 2000);
		var check = function(){
			function doCheck(a){
                // 无条件触发(function() {}['constructor']('debugger')())
				if (('' + a / a)['length'] !== 1 || a % 20 === 0){
                    // (function() {}).constructor === Function
                    // 所以等价于 Function('debugger')(), 即执行动态创建出的函数,这个函数只包含一个'debugger'语句
					(function() {}['constructor']('debugger')());
				} else {
					(function() {}['constructor']('debugger')());
				}
				doCheck(++a);
			}
			try {
				doCheck(0);
			}catch(err){}       // 栈溢出会被忽略,持续递归
		};
		check();


		function consoleOpenCallback() {
			alert('关闭调试窗');
			window.location.reload();
		}

		var Anti_numtots = 0;
		(function () {
			window._windon_handler = setInterval( function() {
                // 通过时间检测调试器,如果时间距离远,说明存在debugger.
				var before = new Date();
				debugger;
				var after = new Date();
				if (after.getTime() - before.getTime() > 100) {
					if (after.getTime() - before.getTime() > 2000) {
						consoleOpenCallback();
						clearInterval(_windon_handler);
					}else{
						Anti_numtots++;
						if(Anti_numtots>=2){
							consoleOpenCallback();
							clearInterval(_windon_handler);
						}
					}
				}else{
					Anti_numtots = 0;
				}
			}, 1000)
		})();
	}

我们需要绕过后面的无限调试和反调试检查. 可疑发现它们都依赖于setInterval这个函数设置的定时任务.

在正常情况下,我们可以通过记录其返回值,然后clearInterval来让其失效:

1
2
interval_id = setInterval(...)
clearInterval(interval_id);

但是这里没有interval_id, 所以可以尝试在执行之前就将setInterval函数覆盖:

1
2
3
4
5
6
const oldSetInterval = window.setInterval;

window.setInterval = function(fn, delay, ...args) {
    console.log("blocked setInterval:", fn, delay);
    return -1;
};

方法1:断点 + 查看 + interval

可以解决. 但是network部分还是没有看到目标.swf文件.

方法2:

如果想要直接disable所有的setInterval函数,副作用是可能会误杀正常逻辑中的定时函数。但是可以先试试.

首先要注意时机,我们需要在执行到反调试函数之前就完成hook。如果我们在一个页面先在控制台输入自己的setInterval覆盖原始函数,刷新后/重新进入目标页面,并不会奏效。

方法3:proxy

另一条线:搜索.swf后, 拼出路径:https://s7.4399.com/4399swf/upload_swf/ftp/20100703wen/1.swf

接着在network中找,发现有一个包的请求url对上了. 关闭ruffle插件的自动执行swf文件功能后,下载.

但是执行的时候显示请在4399内执行. 应该是整个swf文件内部被魔改了.