首页
社区
课程
招聘
[原创]代码混淆之我见(三)
发表于: 2019-2-5 19:22 25399

[原创]代码混淆之我见(三)

2019-2-5 19:22
25399

前言:

本次我们以VMProtect V2.12.3中对HandleDispatch的混淆为例,进行一次非常简单解混淆操作
注意是非常简单,所以牛人请飞过……
本次我们以VMProtect V2.12.3中对HandleDispatch的混淆为例,进行一次非常简单解混淆操作
注意是非常简单,所以牛人请飞过……
传送门:
代码混淆之我见(一)
  • 准备工作
用VMProtect对一段非常简单的汇编代码进行操作,如下。
include		windows.inc
include		kernel32.inc
include		user32.inc
includelib	kernel32.lib
includelib	user32.lib

.data
szText		db	'VMProtect V2.12.3',0
szTitle		db	'三十二变'

.code

@Main	proc
	mov	eax,2019h
	invoke	MessageBox,NULL,offset szText,offset szTitle,MB_OK
	ret

@Main endp

_Start:
	invoke	@Main
	invoke	ExitProcess,0
end	_Start
在VMProtect中,配置对@Main过程进行虚拟化处理。
include		windows.inc
include		kernel32.inc
include		user32.inc
includelib	kernel32.lib
includelib	user32.lib

.data
szText		db	'VMProtect V2.12.3',0
szTitle		db	'三十二变'

.code

@Main	proc
	mov	eax,2019h
	invoke	MessageBox,NULL,offset szText,offset szTitle,MB_OK
	ret

@Main endp

_Start:
	invoke	@Main
	invoke	ExitProcess,0
end	_Start
在VMProtect中,配置对@Main过程进行虚拟化处理。
通过查看反汇编代码可知,@Main过程已经被替换为一条JMP指令,以遇到流程转移指令就跟进的原则,发现最多能定位到此处。

retn 0x10指令执行之后,程序的状态是怎样的我们并不清楚。
经不完全统计,该段代码中共运用了以下几种混淆。
1.栈混淆
2.插入死代码
3.打乱代码空间局部性
与(一)中所描述的“插入死代码”稍有不同,本文介绍的“插入死代码”有新的含义。即即使不执行该段代码,亦不会对程序之后的执行状态造成影响
以一段具体的代码为例,如下。
int x; //1
x = 5; //2
x = 10; //3
print("%d", x); //4
其中语句2即是死代码,因为该语句仅对x赋值,而变量x在语句2上不是活跃的,即x在被使用前就被语句3重新定值。
int x; //1
x = 5; //2
x = 10; //3
print("%d", x); //4
其中语句2即是死代码,因为该语句仅对x赋值,而变量x在语句2上不是活跃的,即x在被使用前就被语句3重新定值。
打乱代码空间局部性即打乱分析者对某段代码的感知。如果将一段逻辑上是连续的代码拆分成无数段,并用流程转移指令随机连接,最终,被拆分的代码段在物理上变得毫无关系,这将严重影响我们对代码的分析。
接下来,我们通过编写解混淆脚本,尝试辅助对虚拟机的分析。
注意,所需的库为Capstone、PythonWin。
另外,Python我只学了半个小时的语法,所以我写出了很多我自己都觉得非常玄幻的代码。如有问题,望跟帖反馈……汗
以下部分内容仅为本人一得之见,如有错误,望跟帖反馈。
  • Dispatch
首先,我们需要读取完全读取该段代码,直到遇到ret指令终止。
为了方便分析堆栈中的数据情况,在将代码拼接回来时,对于CALL指令可以直接将其转化为push imm32/jmp addr的结构,跳转指令则直接舍去。
#获取一段汇编代码,以RET X指令作为终止信号
#CALL指令转换为push ret/jmp addr
#条件跳转默认成立
def GetAsm(StartAddr):
	LstAsm = []
	ObDis = Cs(CS_ARCH_X86, CS_MODE_32)
	ObDis.detail = True
	k = b'\x6A\x00'
	asmm = ObDis.disasm(k, 0x401000)
	for insn in asmm:
		vcall = insn
	state = True
	while state:
		code = ReadMemory(StartAddr, 30)
		asmm = ObDis.disasm(code, StartAddr)
		x = 0
		for insn in asmm:
			if insn.mnemonic == 'ret':
				LstAsm.append(insn)
				state = False
			if x == 0:
				if IsBranch(insn.mnemonic):
					for i in insn.operands:
						StartAddr = i.imm
					break
				elif insn.mnemonic == 'call':
					for i in insn.operands:
						LstAsm.append(vcall)
						StartAddr = i.imm
					break
				else:
					LstAsm.append(insn)
			elif x == 1:
				StartAddr = insn.address
				break
			x += 1
	return LstAsm 
因为要分析ret 0x10这条指令最终走向何处,所以我们要对程序运行时的栈进行模拟。
#获取一段汇编代码,以RET X指令作为终止信号
#CALL指令转换为push ret/jmp addr
#条件跳转默认成立
def GetAsm(StartAddr):
	LstAsm = []
	ObDis = Cs(CS_ARCH_X86, CS_MODE_32)
	ObDis.detail = True
	k = b'\x6A\x00'
	asmm = ObDis.disasm(k, 0x401000)
	for insn in asmm:
		vcall = insn
	state = True
	while state:
		code = ReadMemory(StartAddr, 30)
		asmm = ObDis.disasm(code, StartAddr)
		x = 0
		for insn in asmm:
			if insn.mnemonic == 'ret':
				LstAsm.append(insn)
				state = False
			if x == 0:
				if IsBranch(insn.mnemonic):
					for i in insn.operands:
						StartAddr = i.imm
					break
				elif insn.mnemonic == 'call':
					for i in insn.operands:
						LstAsm.append(vcall)
						StartAddr = i.imm
					break
				else:
					LstAsm.append(insn)
			elif x == 1:
				StartAddr = insn.address
				break
			x += 1
	return LstAsm 
因为要分析ret 0x10这条指令最终走向何处,所以我们要对程序运行时的栈进行模拟。
建立一个字典,作为程序运行时的栈空间。通过分析代码,处理sub、sbb、add、adc、lea等指令对esp的影响,更新栈指针,同时注意对于栈空间的访问。但当遇到如sub esp,reg32或者mov reg32,[esp + reg32 - imm8]等指令,我们还是认为其对堆栈的影响是不可预料的。我们将栈里的内存以DWORD为单位开始编号。但我模拟的栈指针是从低地址向高地址扩展的,所以对于程序中对于esp的调整,都要进行与之相反的工作来调整模拟的栈指针。并为每条指令附加read,write字典,以表示其访问的栈变量。
注意,我忽略了将esp赋值给其它寄存器,程序通过其它寄存器来访问栈空间的情况。
def AnalyzeStackObfs(LstAsm):
	ptr = -1 #模拟栈指针,但是从低地址向高地址扩展的,以DWORD为最小单位,即执行一次push dword栈指针递增1
	LstStackInfo = []
	stackele = {}
	stackele['read'] = []
	stackele['write'] = []
	for insn in LstAsm:
		stackele = {}
		stackele['read'] = []
		stackele['write'] = []		
		if insn.mnemonic == 'push': #入栈
			for x in insn.operands:
				if x.type == X86_OP_MEM and insn.reg_name(x.mem.base) == 'esp' and x.mem.index == 0:
					if x.mem.disp % 4 != 0:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
					if x.access == CS_AC_READ:
						stackele['read'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_WRITE:
						stackele['write'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_READ | CS_AC_WRITE:
						stackele['read'].append(ptr - x.mem.disp // 4)
						stackele['write'].append(ptr - x.mem.disp // 4)		
			ptr += 1
			stackele['write'].append(ptr)
		elif insn.mnemonic in ['pushad', 'pushal']: #入栈
			ptr += 8
			for x in range(ptr, ptr - 8, -1):
				stackele['write'].append(x)
		elif insn.mnemonic == 'pushfd': #入栈
			ptr += 1
			stackele['write'].append(ptr)
		elif insn.mnemonic == 'pop': #出栈
			for x in insn.operands:
				if x.type == X86_OP_MEM and insn.reg_name(x.mem.base) == 'esp' and x.mem.index == 0:
					if x.mem.disp % 4 != 0:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
					if x.access == CS_AC_READ:
						stackele['read'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_WRITE:
						stackele['write'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_READ | CS_AC_WRITE:
						stackele['read'].append(ptr - x.mem.disp // 4)
						stackele['write'].append(ptr - x.mem.disp // 4)
			stackele['read'].append(ptr)
			ptr -= 1
		elif insn.mnemonic == 'popfd': #出栈
			stackele['read'].append(ptr)
			ptr -= 1
		elif insn.mnemonic in ['popad', 'popal']: #出栈
			for x in range(ptr, ptr - 8, -1):
				stackele['read'].append(x)
			ptr -= 8
		elif insn.mnemonic in ['sub', 'sbb'] and ModifyReg('esp', insn): #调整栈指针
			for x, k in zip(insn.operands, range(1, 3)):
				if k == 2:
					if x.type == X86_OP_IMM:
						if x.imm % 4 == 0:
							ptr += (x.imm // 4)
						else:
							print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
					else:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
		elif insn.mnemonic in ['add', 'adc'] and ModifyReg('esp', insn): #调整栈指针
			for x, k in zip(insn.operands, range(1, 3)):
				if k == 2:
					if x.type == X86_OP_IMM:
						if x.imm % 4 == 0:
							ptr -= (x.imm // 4)
						else:
							print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
					else:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))			
		elif insn.mnemonic == 'lea': #调整栈指针
			if ModifyReg('esp', insn):
				for x, k in zip(insn.operands, range(1, 3)):
					if k == 2:
						if x.type == X86_OP_MEM:
							if x.mem.disp % 4 == 0:
								ptr -= (x.mem.disp // 4)
							else:
								print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
						else:
							print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
		elif insn.mnemonic == 'ret':
			stackele['read'].append(ptr)
			for x in insn.operands:
				ptr -= (x.imm // 4 + 1)
		elif ModifyReg('esp', insn): #如果还有其它调整栈指针的指令没有被考虑,打印出详细信息
			print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
		else: #处理 xxx [esp + xx],xx格式指令
			for x in insn.operands:
				if x.type == X86_OP_MEM and insn.reg_name(x.mem.base) == 'esp' and x.mem.index == 0:
					if x.mem.disp % 4 != 0:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))

					if x.access == CS_AC_READ:
						stackele['read'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_WRITE:
						stackele['write'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_READ | CS_AC_WRITE:
						stackele['read'].append(ptr - x.mem.disp // 4)
						stackele['write'].append(ptr - x.mem.disp // 4)
		LstStackInfo.append(stackele)

	for x, k in zip(LstAsm, LstStackInfo):
		print(x.mnemonic + '	' + x.op_str, k)

	return
最终我们得到了非常详细的输出。
def AnalyzeStackObfs(LstAsm):
	ptr = -1 #模拟栈指针,但是从低地址向高地址扩展的,以DWORD为最小单位,即执行一次push dword栈指针递增1
	LstStackInfo = []
	stackele = {}
	stackele['read'] = []
	stackele['write'] = []
	for insn in LstAsm:
		stackele = {}
		stackele['read'] = []
		stackele['write'] = []		
		if insn.mnemonic == 'push': #入栈
			for x in insn.operands:
				if x.type == X86_OP_MEM and insn.reg_name(x.mem.base) == 'esp' and x.mem.index == 0:
					if x.mem.disp % 4 != 0:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
					if x.access == CS_AC_READ:
						stackele['read'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_WRITE:
						stackele['write'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_READ | CS_AC_WRITE:
						stackele['read'].append(ptr - x.mem.disp // 4)
						stackele['write'].append(ptr - x.mem.disp // 4)		
			ptr += 1
			stackele['write'].append(ptr)
		elif insn.mnemonic in ['pushad', 'pushal']: #入栈
			ptr += 8
			for x in range(ptr, ptr - 8, -1):
				stackele['write'].append(x)
		elif insn.mnemonic == 'pushfd': #入栈
			ptr += 1
			stackele['write'].append(ptr)
		elif insn.mnemonic == 'pop': #出栈
			for x in insn.operands:
				if x.type == X86_OP_MEM and insn.reg_name(x.mem.base) == 'esp' and x.mem.index == 0:
					if x.mem.disp % 4 != 0:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
					if x.access == CS_AC_READ:
						stackele['read'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_WRITE:
						stackele['write'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_READ | CS_AC_WRITE:
						stackele['read'].append(ptr - x.mem.disp // 4)
						stackele['write'].append(ptr - x.mem.disp // 4)
			stackele['read'].append(ptr)
			ptr -= 1
		elif insn.mnemonic == 'popfd': #出栈
			stackele['read'].append(ptr)
			ptr -= 1
		elif insn.mnemonic in ['popad', 'popal']: #出栈
			for x in range(ptr, ptr - 8, -1):
				stackele['read'].append(x)
			ptr -= 8
		elif insn.mnemonic in ['sub', 'sbb'] and ModifyReg('esp', insn): #调整栈指针
			for x, k in zip(insn.operands, range(1, 3)):
				if k == 2:
					if x.type == X86_OP_IMM:
						if x.imm % 4 == 0:
							ptr += (x.imm // 4)
						else:
							print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
					else:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
		elif insn.mnemonic in ['add', 'adc'] and ModifyReg('esp', insn): #调整栈指针
			for x, k in zip(insn.operands, range(1, 3)):
				if k == 2:
					if x.type == X86_OP_IMM:
						if x.imm % 4 == 0:
							ptr -= (x.imm // 4)
						else:
							print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
					else:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))			
		elif insn.mnemonic == 'lea': #调整栈指针
			if ModifyReg('esp', insn):
				for x, k in zip(insn.operands, range(1, 3)):
					if k == 2:
						if x.type == X86_OP_MEM:
							if x.mem.disp % 4 == 0:
								ptr -= (x.mem.disp // 4)
							else:
								print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
						else:
							print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
		elif insn.mnemonic == 'ret':
			stackele['read'].append(ptr)
			for x in insn.operands:
				ptr -= (x.imm // 4 + 1)
		elif ModifyReg('esp', insn): #如果还有其它调整栈指针的指令没有被考虑,打印出详细信息
			print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))
		else: #处理 xxx [esp + xx],xx格式指令
			for x in insn.operands:
				if x.type == X86_OP_MEM and insn.reg_name(x.mem.base) == 'esp' and x.mem.index == 0:
					if x.mem.disp % 4 != 0:
						print('栈混淆清理可能有误,因为有未知的调整栈指针指令,该指令位于%X' % (insn.address))

					if x.access == CS_AC_READ:
						stackele['read'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_WRITE:
						stackele['write'].append(ptr - x.mem.disp // 4)
					elif x.access == CS_AC_READ | CS_AC_WRITE:
						stackele['read'].append(ptr - x.mem.disp // 4)
						stackele['write'].append(ptr - x.mem.disp // 4)
		LstStackInfo.append(stackele)

	for x, k in zip(LstAsm, LstStackInfo):
		print(x.mnemonic + '	' + x.op_str, k)

	return
最终我们得到了非常详细的输出。

ret 0x10指令所访问的栈变量如下。

可以观察到,ret 0x10指令读取了第65号栈变量,而65号栈变量在push dword ptr [esp + 0xc]中被定值,而定值又来源于第61号栈变量,61号栈变量最近一次定值在mov dword ptr [esp + 8],ecx中。
我们可以进一步观察ecx的定值情况。

ecx来源于[eax * 4 + 0x405821],接着又进行了循环右移操作,即解密工作。
现在我们可以来观察一下哪些代码可以被归入死代码一类。

首先不得不说,图中圈出的pushad指令非常诱人,但其定值了第67与66号栈变量,且依据现有信息,我们无法判断它们是否活跃。为了保证解混淆后的代码语义不发生变化,我们应该尽可能地保守分析。
在对栈变量进行活跃分析时,需要遵循以下原则。
1.活跃分析应从底部向顶部分析
2.出口代码处的所有栈变量都是活跃的
3.栈变量的活跃性持续向上传递,但遇到对该栈变量的读写操作时,会变更活跃性
4.当某一栈变量向上传递活跃性遇到写操作时,则活跃性变为死状态,若遇到读操作时,则活跃性变为活状态
5.当出现对同一栈变量进行读写操作时,我们默认读操作先于写操作
其中原则2是出于保守分析的前提提出的,而原则5则是经验之谈,如果你找到了反例,望跟帖反馈。以上原则均是基于同一基本块内栈变量活跃性流动情况提出的。
栈变量的活跃性分析代码如下。
#StackVar指定欲分析的栈变量
def AnalyzeStackVar(LstStackInfo, StackVar):
	k = True
	LstStackInfo.reverse()
	for info in LstStackInfo:
		info[StackVar] = k
		if StackVar in info['write']:
			k = False
		if StackVar in info['read']:
			k = True
	LstStackInfo.reverse()
	return
有了栈变量的在各指令上的活跃情况,我们就可以删除一些对死状态栈变量赋值的语句,具体代码如下。
#StackVar指定欲分析的栈变量
def AnalyzeStackVar(LstStackInfo, StackVar):
	k = True
	LstStackInfo.reverse()
	for info in LstStackInfo:
		info[StackVar] = k
		if StackVar in info['write']:
			k = False
		if StackVar in info['read']:
			k = True
	LstStackInfo.reverse()
	return
有了栈变量的在各指令上的活跃情况,我们就可以删除一些对死状态栈变量赋值的语句,具体代码如下。
def GetUnVar(info):
	z = []
	for x in range(0, MaxPtr + 1):
		if not info[x]:
			z.append(x)

	return z
#默认在尾部时,所有栈变量都是活跃的
for i in range(0, MaxPtr + 1):
	AnalyzeStackVar(LstStackInfo, i)

LstNewAsm = LstAsm[:]
LstNewStackInfo = LstStackInfo[:]
LstAsmInfo = []
for x, k in zip(LstNewAsm, LstNewStackInfo):
	y = {}
	y['Asm'] = x
	y['Stack'] = k
	LstAsmInfo.append(y)
LstNewAsmInfo = LstAsmInfo[:]
for asmi in LstAsmInfo:
	#不涉及到栈指针调整的指令可以直接删除
	if (len(asmi['Stack']['write']) != 0) and (IsListIn(asmi['Stack']['write'], GetUnVar(asmi['Stack']))) and (asmi['Asm'].mnemonic not in ['push', 'pushal', 'pushfd', 'pushad', 'pop', 'popfd', 'popal', 'popad']):
			print('位于%X处的指令被清除了' % asmi['Asm'].address)
			LstNewAsmInfo.remove(asmi)
输出情况并不是非常理想,仅删除了3条语句。
def GetUnVar(info):
	z = []
	for x in range(0, MaxPtr + 1):
		if not info[x]:
			z.append(x)

	return z
#默认在尾部时,所有栈变量都是活跃的
for i in range(0, MaxPtr + 1):
	AnalyzeStackVar(LstStackInfo, i)

LstNewAsm = LstAsm[:]
LstNewStackInfo = LstStackInfo[:]
LstAsmInfo = []
for x, k in zip(LstNewAsm, LstNewStackInfo):
	y = {}
	y['Asm'] = x
	y['Stack'] = k
	LstAsmInfo.append(y)
LstNewAsmInfo = LstAsmInfo[:]
for asmi in LstAsmInfo:
	#不涉及到栈指针调整的指令可以直接删除
	if (len(asmi['Stack']['write']) != 0) and (IsListIn(asmi['Stack']['write'], GetUnVar(asmi['Stack']))) and (asmi['Asm'].mnemonic not in ['push', 'pushal', 'pushfd', 'pushad', 'pop', 'popfd', 'popal', 'popad']):
			print('位于%X处的指令被清除了' % asmi['Asm'].address)
			LstNewAsmInfo.remove(asmi)
输出情况并不是非常理想,仅删除了3条语句。

这3条指令如下。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-6-10 23:24 被天水姜伯约编辑 ,原因:
上传的附件:
收藏
免费 13
支持
分享
打赏 + 12.00雪花
打赏次数 5 雪花 + 12.00
 
赞赏  CCkicker   +5.00 2019/02/18 精品文章~
赞赏  一位没有留下痕迹的看雪读者   +2.00 2019/02/10 精品文章~
赞赏  demoscene   +1.00 2019/02/10 中学生牛逼,期待 4、5、6、7、8、N
赞赏  orz1ruo   +2.00 2019/02/06 感谢分享~
赞赏  junkboy   +2.00 2019/02/05 精品文章~
最新回复 (15)
雪    币: 9934
活跃值: (2554)
能力值: ( LV6,RANK:87 )
在线值:
发帖
回帖
粉丝
2
很强
2019-2-5 19:27
0
雪    币: 180
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
很棒
2019-2-5 19:43
0
雪    币: 11716
活跃值: (133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
支持
2019-2-5 23:04
0
雪    币: 6172
活跃值: (2168)
能力值: ( LV4,RANK:156 )
在线值:
发帖
回帖
粉丝
5
厉害了
2019-2-6 07:50
0
雪    币: 4709
活跃值: (1685)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
6
收藏
2019-2-7 00:09
0
雪    币: 26136
活跃值: (1409)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
很强!  全是精品!
2019-2-7 17:35
1
雪    币: 2503
活跃值: (6531)
能力值: ( LV9,RANK:235 )
在线值:
发帖
回帖
粉丝
8
挽梦雪舞 很强![em_63] 全是精品![em_88]
汗,真不是精品……我一个辣鸡高中生的知识体系能让我写出啥精品?我做的就是把牛人们的思路退化一遍,然后造造轮子……
2019-2-8 08:45
0
雪    币: 26136
活跃值: (1409)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
三十二变 [em_2]汗,真不是精品……我一个辣鸡高中生的知识体系能让我写出啥精品?我做的就是把牛人们的思路退化一遍,然后造造轮子……
谦虚了
2019-2-8 13:29
0
雪    币: 9792
活跃值: (1725)
能力值: ( LV12,RANK:261 )
在线值:
发帖
回帖
粉丝
10
学习很多,感谢大佬
2019-2-8 23:20
0
雪    币: 196
活跃值: (144)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
大佬可否指教下VM怎么学?
2019-2-10 09:50
0
雪    币: 196
活跃值: (144)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
大神第二篇呢
最后于 2019-2-10 09:57 被大C滑稽编辑 ,原因:
2019-2-10 09:53
0
雪    币: 351
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
大神厉害
2019-2-11 16:13
0
雪    币: 2503
活跃值: (6531)
能力值: ( LV9,RANK:235 )
在线值:
发帖
回帖
粉丝
14
大C滑稽 大佬可否指教下VM怎么学?[em_13][em_13]
1.去混淆
2.学编译原理
3.学习一款逆向框架
4.弃坑跑路
以上为笑谈……
xiaohang前辈不是已经总结了吗?
https://bbs.pediy.com/thread-224537.htm
2019-2-12 08:49
0
雪    币: 12277
活跃值: (1990)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
15
不错,好啊
2019-4-20 18:50
0
雪    币: 199
活跃值: (183)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
支持下
2022-3-17 23:13
0
游客
登录 | 注册 方可回帖
返回