windows脱壳复习
UPX壳
使用upX压缩之后对比入下
现在使用OD脱压缩壳
注意配置OD的UDD路径,还有异常选项别忘设置
然后用OD在入我们前面加壳的程序。断在0x01014240
然而原来的入口
0100739D 0000 add byte ptr ds:[eax],al
0100739F 0000 add byte ptr ds:[eax],al
010073A1 0000 add byte ptr ds:[eax],al
010073A3 0000 add byte ptr ds:[eax],al
010073A5 0000 add byte ptr ds:[eax],al
010073A7 0000 add byte ptr ds:[eax],al
010073A9 0000 add byte ptr ds:[eax],al
010073AB 0000 add byte ptr ds:[eax],al
010073AD 0000 add byte ptr ds:[eax],al
010073AF 0000 add byte ptr ds:[eax],al
010073B1 0000 add byte ptr ds:[eax],al
010073B3 0000 add byte ptr ds:[eax],al
010073B5 0000 add byte ptr ds:[eax],al
010073B7 0000 add byte ptr ds:[eax],al
被改成了00,说明被移走了。下面就是脱壳修复。就是在适当时机,当原始信息还原的时候,把他dump出来,然后在修复一些东西,让其变成脱壳了并且正常运行的程序。总体来说流程是
1. F9运行程序, F12暂停
2. 根据堆栈调用链回溯main函数,main函数的上面就是程序的入口点.
alt+k查看堆栈调用, 选择最后一个show call, 往上翻,找vc7.0的入口汇编push 70
在push 70处下一个硬件执行断点. 为什么要下硬件执行断点?
3. 重载程序(ctrl+f2), F9运行程序 此时中断到oep处. debug---> hardware breakpoints 删除
4. loadpe dump程序
5. importrec修复导入表
我们可以根据esp定律,使用插件,IDAFicator,在执行完pushad操作之后,对esp下硬件断点,全速运行当再断下来时候就是popad操作,即将到达原始程序oep。,此时
010143BD 61 popad
010143BE 8D4424 80 lea eax,dword ptr ss:[esp-80]
010143C2 6A 00 push 0
010143C4 39C4 cmp esp,eax
010143C6 ^ 75 FA jnz short notepad.010143C2
010143C8 83EC 80 sub esp,-80
010143CB - E9 CD2FFFFF jmp notepad.0100739D
此时sub esp,-80
然后观察0100739D跳过去代码特征。发现这里739D是入口点。
0100739D 6A 70 push 70
0100739F 68 98180001 push notepad.01001898
010073A4 E8 BF010000 call notepad.01007568
010073A9 33DB xor ebx,ebx
010073AB 53 push ebx
010073AC 8B3D CC100001 mov edi,dword ptr ds:[10010CC] ; kernel32.GetModuleHandleA
010073B2 FFD7 call edi
010073B4 66:8138 4D5A cmp word ptr ds:[eax],5A4D
010073B9 75 1F jnz short notepad.010073DA
010073BB 8B48 3C mov ecx,dword ptr ds:[eax+3C]
010073BE 03C8 add ecx,eax
看到GetModuleHandleA,然后的步骤就是从内存dump出来。
然后就是修复oep,现在程序入口点739D,用loadPE修复,然后修复程序的导入表比如ThunkValue是个虚拟内存要修改成原来的。使用ImportREC,进入ImprotREC选择我们脱壳的程序。
先填写正确的OEP然后点自动搜索IAT,提示导入表RVA是1000,我们去OD看看是不是
01001000 77DA6FEF advapi32.RegQueryValueExW
01001004 77DA6C17 advapi32.RegCloseKey
01001008 77DCBA25 advapi32.RegCreateKeyW
0100100C 77DCBD05 advapi32.IsTextUnicode
01001010 77DA7AAB advapi32.RegQueryValueExA
01001014 77DA7842 advapi32.RegOpenKeyExA
01001018 77DAD757 advapi32.RegSetValueExW
0100101C 00000000
01001020 7718D270 comctl32.CreateStatusWindowW
01001024 00000000
01001028 77F0DC19 gdi32.EndPage
0100102C 77F24A05 gdi32.AbortDoc
01001030 77F0DEA9 gdi32.EndDoc
01001034 77EF6E5F gdi32.DeleteDC
01001038 77F0F456 gdi32.StartPage
0100103C 77EF7F9D gdi32.GetTextExtentPoint32W
01001040 77EFBE28 gdi32.CreateDCW
01001044 77F24B25 gdi32.SetAbortProc
01001048 77EFA5BB gdi32.GetTextFaceW
0100104C 77EF7EAC gdi32.TextOutW
01001050 77F25695 gdi32.StartDocW
01001054 77F1FB22 gdi32.EnumFontsW
01001058 77EF61C1 gdi32.GetStockObject
发现就是导入表。大小是348,然后修改ImportREC里导入表rva和大小。点击获取导入表。然后点击修复转储到我们刚才脱壳dump出的文件里。这时候就能正常运行脱壳的程序了。
前面是一种方法,是我们知道原来OEP位置,但是如果我们不确定加壳程序有什么信息,采用下面这种方法。就是我们想让程序跑起来然后回溯,比如在运行时点击暂停,然后观察调用栈情况,
找到名字是我们程序的调用最后最下面一层,右键点击showcal。这里就是notepadXXXX,倒数第二个
在往上翻,就找到的OEP了
之所以是这,因为他符合vc7的OEP特征,每种编译器编译的二进制文件都有自己的特征。如果不熟悉oep特征,可以先确定程序是什么编译的。可以通过如file format identififier等工具先查下,然后去搜索这类程序的OEP特征。参考资料
https://blog.csdn.net/youyou519/article/details/82418055
https://blog.csdn.net/cdsntxz158/article/details/7998778
然后我们在oep处下一个硬件执行断点。然后重新运行,全速运行,就断到了OEP了。后面步骤就跟第一个方法一样了,通过lordpe等工具把程序从内存dump出来,修复OEP,重建导入表。
另一个demo
下面看一个稍微复杂点的带反调试的UPX壳。
上图是用这种加壳软件加壳前和加壳后的对比。
并且运行起来加壳程序可以在任务管理器看到这个程序是双进程(猜测是双进程守护类型)。下面分析一下壳的流程。
先单步走到
0101D10C E8 00000000 call notepad_.0101D111
0101D111 5D pop ebp
0101D112 81ED 8E290200 sub ebp,2298E
0101D118 B9 02120000 mov ecx,1202
0101D11D 8DBD D6290200 lea edi,dword ptr ss:[ebp+229D6]
0101D123 8BF7 mov esi,edi
0101D125 AC lods byte ptr ds:[esi]
0101D126 F9 stc
0101D127 EB 01 jmp short notepad_.0101D12A
这里发现走过nop到达上面0101D10C这里,执行了一条call下一条指令的地址,下一条pop ebp,因为call等于push eip,jmpxxx,他这里紧接着pop ebp,相当于mov ebp eip,然后ebp减去2298E,然后后续一些了操作在操作eax直到
0101D156 AA stos byte ptr es:[edi]
0101D157 ^ E2 CC loopd short notepad_.0101D125
算出的值又给回了edi地址里。然后下面进入一个循环,fellow进去看一下
0101D125 AC lods byte ptr ds:[esi]
0101D126 F9 stc
0101D127 EB 01 jmp short notepad_.0101D12A
0101D129 E8 34B9FEC8 call CA008A62
0101D12E C0C0 8D rol al,8D
0101D131 FEC8 dec al
0101D133 EB 01 jmp short notepad_.0101D136
发现这循环又回到了之前取esi地址的地方。 在观察此时ecx为1202即循环次数。我们让他跳过循环直接运行到循环结束的地方也就是loopd的下一条指令,此时就已经循环解码完成
如图,od显示这里是moduleEntryPoint,接下来指令,把ebp+2289DD地址的值给eax,ecx赋值573然后call一个地方,进去看
0101DC4A 8BF8 mov edi,eax
0101DC4C 33C0 xor eax,eax
0101DC4E 33DB xor ebx,ebx
0101DC50 33D2 xor edx,edx
0101DC52 8A07 mov al,byte ptr ds:[edi]
0101DC54 F7E2 mul edx
0101DC56 03D8 add ebx,eax
0101DC58 42 inc edx
0101DC59 47 inc edi
0101DC5A ^ E2 F6 loopd short notepad_.0101DC52
0101DC5C 93 xchg eax,ebx
0101DC5D C3 retn
在把eax给了edi之后,然后对eax做了一些了移动相乘之类的操作,把ebx给了eax,所以我们f4直接跑到这里循环结束010dc5c处。然后f7,执行完这个call,回到调用模块,
此时eax为0002079D,
将其放入这里的[0101dc6e]放入dump
再往下走
0101D16F 8B85 DB340200 mov eax,dword ptr ss:[ebp+234DB]
0101D175 0340 3C add eax,dword ptr ds:[eax+3C]
这些执行完0101D16F后eax为010000000,在模块里发现这就是base基址即Dos头,所以下面取eax+3C地址里的值,这些就知道在操作pe结构的东西了。[eax+3C],如果不熟悉PE结构,可以通过od自带功能,在模块列表,找到你要查询的基址那里点右键选dump,即可看到这个结构体。如下图
发现这里就是PE头的偏移。里面是E0,然后eax+e0,发现e0偏移即是PE头了。然后下面一条汇编指令是再加80,继续观察
发现80即使导入表的rva了。 然后单步走发现把其家属基址给ecx即导入表真正地址。然后单步去除导入表0x10里的值,10D035
然后加基址
0101D190 8B18 mov ebx,dword ptr ds:[eax] ; kernel32.LoadLibraryA
发现是找
LoadLibraryA,估计是要导入自己的dll。继续往下
0101D198 F785 E3340200 01000000 test dword ptr ss:[ebp+234E3],1
0101D1A2 74 09 je short notepad_.0101D1AD
0101D1A4 803B CC cmp byte ptr ds:[ebx],0CC
0101D1A7 0F84 F4090000 je notepad_.0101DBA1
我们看到判断刚才那你是不是1,是的话跳转到一个地址,不是道haul继续往下判断,ebx地址的值是不是CC,即看看是不是被下了软件断点。所以我们推出je跳过去的代码就是反调试用的。
0101DBA1 6A 00 push 0
0101DBA3 6A FF push -1
0101DBA5 FF95 393A0200 call dword ptr ss:[ebp+23A39]
再看下ebp+23A39
0101E1BC 0000 add byte ptr ds:[eax],al
0101E1BE 0000 add byte ptr ds:[eax],al
发现过去opcode是0000,过去就蹦了。所以不再去关调试这条线路了,回到代码,
0101D1AD 83C0 04 add eax,4
0101D1B0 8B18 mov ebx,dword ptr ds:[eax] ; kernel32.GetProcAddress
0101D1B2 899D B7360200 mov dword ptr ss:[ebp+236B7],ebx
0101D1B8 F785 E3340200 01000000 test dword ptr ss:[ebp+234E3],1
0101D1C2 74 09 je short notepad_.0101D1CD
0101D1C4 803B CC cmp byte ptr ds:[ebx],0CC
0101D1C7 0F84 D4090000 je notepad_.0101DBA1
发现有时再操作另一个函数GetProcAddress,继续
0101D1CD 8D85 1C3A0200 lea eax,dword ptr ss:[ebp+23A1C]
0101D1D3 50 push eax
0101D1D4 FF95 B3360200 call dword ptr ss:[ebp+236B3]
0101D1DA 8BF0 mov esi,eax ; ntdll.7C920000
0101D1DC 8D85 263A0200 lea eax,dword ptr ss:[ebp+23A26]
发现push的是ntdll.dll,,call的是LoadlibraryA。再把加载这个dll的地址给esi,然后单步发现把诸如NtTerminateProcess这类字符给eax,给eax,然后还是用了参数,getProcessAddress,然后call一个函数,很明显是过度函数地址的作用。进去给他右键个label,GetAPI方便观察。,然后后面一系列的初始化函数,单步走到这段结束。此时壳的函数初始化完了。
0101D37B 8985 CE3B0200 mov dword ptr ss:[ebp+23BCE],eax ; kernel32.GetCommandLineA
0101D381 8BFD mov edi,ebp
0101D383 8D85 622F0200 lea eax,dword ptr ss:[ebp+22F62]
0101D389 50 push eax
0101D38A 64:FF35 00000000 push dword ptr fs:[0]
0101D391 64:8925 00000000 mov dword ptr fs:[0],esp
0101D398 8BBD DB340200 mov edi,dword ptr ss:[ebp+234DB]
0101D39E 037F 3C add edi,dword ptr ds:[edi+3C]
单步下去发现获得控制台参数,然后把一个貌似是oep地址的值给eax,
0101D38A 64:FF35 00000000 push dword ptr fs:[0]
0101D391 64:8925 00000000 mov dword ptr fs:[0],esp
0101D398 8BBD DB340200 mov edi,dword ptr ss:[ebp+234DB]
0101D39E 037F 3C add edi,dword ptr ds:[edi+3C]
0101D3A1 8BB5 DB340200 mov esi,dword ptr ss:[ebp+234DB]
0101D3A7 8B4F 54 mov ecx,dword ptr ds:[edi+54]
0101D3AA 8D85 F43B0200 lea eax,dword ptr ss:[ebp+23BF4]
0101D3B0 50 push eax
0101D3B1 6A 04 push 4
0101D3B3 51 push ecx
0101D3B4 FFB5 DB340200 push dword ptr ss:[ebp+234DB]
0101D3BA 6A FF push -1
0101D3BC FF95 C63A0200 call dword ptr ss:[ebp+23AC6]
发现还是在遍历PE结构,去sizeofHeader,然后继续单步发现把sizeofHeader当中菜蔬,又放进去基址之类的call一个函数
0101D3A7 8B4F 54 mov ecx,dword ptr ds:[edi+54] ; sizeofHeader
0101D3AA 8D85 F43B0200 lea eax,dword ptr ss:[ebp+23BF4]
0101D3B0 50 push eax
0101D3B1 6A 04 push 4
0101D3B3 51 push ecx
0101D3B4 FFB5 DB340200 push dword ptr ss:[ebp+234DB] ; base
0101D3BA 6A FF push -1
0101D3BC FF95 C63A0200 call dword ptr ss:[ebp+23AC6] ; kernel32.VirtualProtectEx
VirtualProtectEx函数可以改变在特定进程中内存区域的保护属性。
0006FFA4 0101D3C2 /CALL to VirtualProtectEx from notepad_.0101D3BC
0006FFA8 FFFFFFFF |hProcess = FFFFFFFF
0006FFAC 01000000 |Address = notepad_.01000000
0006FFB0 00001000 |Size = 1000 (4096.)
0006FFB4 00000004 |NewProtect = PAGE_READWRITE
0006FFB8 0101E377 \pOldProtect = notepad_.0101E377
0006FFBC 0006FFE0 Pointer to next SEH record
0006FFC0 0101D6E5 SE handler
0006FFC4 7C817067 RETURN to kernel32.7C817067
发现这个函数参数要改成可读可写,然后接着单步进去看下一个Call,发现进去首先是GetModuleFileNameA,获取模块名。
0101D726 DBAD C3370200 fld tbyte ptr ss:[ebp+237C3]
0101D72C 0F31 rdtsc
发现获取的就是我们调试的进程。然后单步几部发现如上,浮点操作取值。将这个地址的浮点数压栈,接着rdtsc指令是得到CPU自启动以后的运行周期,然后单步比较ah是否为0,等于0跳一个地方,先不管,紧接着单步发现call ZwSetInformaticaThread,进去看一下参数
ZwSetInformationThread
ZwSetInformationThread拥有两个参数,第一个参数用来接收当前线程的句柄,第二个参数表示线程信息类型,若其值设置为ThreadHideFromDebugger(0x11),使用语句ZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);调用该函数后,调试进程就会被分离出来。该函数不会对正常运行的程序产生任何影响,但若运行的是调试器程序,因为该函数隐藏了当前线程,调试器无法再收到该线程的调试事件,最终停止调试。还有一个函数DebugActiveProcessStop用来分离调试器和被调试进程,从而停止调试。两个API容易混淆,需要牢记它们的区别。
所以这里是调用这个函数是用来进行反调试的。所以需要STRONGOD把KernMode模块打开,防止此类反调试。不然就会被停止调试。
0101D73F FF95 763A0200 call dword ptr ss:[ebp+23A76] ; SetInformationThread
0101D745 FF95 8A3A0200 call dword ptr ss:[ebp+23A8A] ; ntdll.CsrGetProcessId一般程序正常启动时是不具备调试权限(SedebugPrivilege)的,除非自己有提权的需要主动开启,但是调试器启动程序的时候,由于调试器本身会开启调试权限,所以被调试进程会继承调试权限,因此我们可以通过检查进程是否具有调试权限来进行反调试。
检查进程是否具有调试权限的方式很简单,系统启动的时候会启动一个核心进程csrss.exe,我们可以通过判断能否使用OpenProcess打开该进程来检查当前进程是否具有调试权限,因为只有拥有管理员权限+调试权限的进程才能打开csrss.exe的句柄。严格来说这种检查方法是不太严格的,因为当进程有调试权限无管理员权限的时候也不能打开csrss.exe的句柄,幸运的是,大多数调试器都会要求提供管理员权限,所以被调试程序也会同时拥有管理员权限+调试权限。
通过CsrGetProcessId函数可以获取csrss.exe的PID,然后通过OpenProcess尝试打开csrss.exe的句柄:
紧接着获取当前进程PID,单步几部然后发现打开这个进程。这个进程是csrss,如果debug权限是可以打开的,打开成功返回PID是不等于0,所以这里不等于0,就会跳转到停止的地方去了,这里相当的原因是StrongOD已经帮我们操作了。
继续往下跟,发现有关于比如填充800001这种异常的数据。接着往下又是VirtualAlloc申请内存。申请了内存,又修改了内存属性,调用esi
0101D7B4 50 push eax
0101D7B5 6A FF push -1
0101D7B7 FF95 C63A0200 call dword ptr ss:[ebp+23AC6]
0101D7BD FFD6 call esi
这种情况正常状态,会触发异常,调试状态不会触发。如果跑过了,已经触发异常,就单步走走到调用异常的,慢慢比如在每层导数第一个函数下断然后一步一步跑发现走到
7C9232A6 FFD1 call ecx ; notepad_.0101D679
7C9232A8 64:8B25 00000000 mov esp,dword ptr fs:[0]
如上面,call,ecx,ecx是源程序模块的地址不再是ntdll的了,所以这里就是一个异常的出口。
0101D679 55 push ebp
0101D67A 8BEC mov ebp,esp
0101D67C 57 push edi
0101D67D 8B45 10 mov eax,dword ptr ss:[ebp+10]
0101D680 8B5D 08 mov ebx,dword ptr ss:[ebp+8]
0101D683 8B1B mov ebx,dword ptr ds:[ebx]
0101D685 8BB8 9C000000 mov edi,dword ptr ds:[eax+9C]
0101D68B 3B9F 5B360200 cmp ebx,dword ptr ds:[edi+2365B]
0101D691 74 0B je short notepad_.0101D69E
0101D693 8BA8 9C000000 mov ebp,dword ptr ds:[eax+9C]
0101D699 E9 AE040000 jmp notepad_.0101DB4C
0101D69E F787 E3340200 04000000 test dword ptr ds:[edi+234E3],4
0101D6A8 74 17 je short notepad_.0101D6C1
0101D6AA 8B58 04 mov ebx,dword ptr ds:[eax+4]
0101D6AD 0358 08 add ebx,dword ptr ds:[eax+8]
0101D6B0 0358 0C add ebx,dword ptr ds:[eax+C]
0101D6B3 0358 10 add ebx,dword ptr ds:[eax+10]
0101D6B6 0358 14 add ebx,dword ptr ds:[eax+14]
0101D6B9 0358 18 add ebx,dword ptr ds:[eax+18]
0101D6BC 83FB 00 cmp ebx,0
0101D6BF ^ 75 D2 jnz short notepad_.0101D693
0101D6C1 FFB7 57360200 push dword ptr ds:[edi+23657]
0101D6C7 8F80 B8000000 pop dword ptr ds:[eax+B8]
0101D6CD 89B8 B4000000 mov dword ptr ds:[eax+B4],edi
0101D6D3 C780 B0000000 04000000 mov dword ptr ds:[eax+B0],4
0101D6DD B8 00000000 mov eax,0
0101D6E2 5F pop edi
0101D6E3 C9 leave
0101D6E4 C3 retn
0101D6E5 55 push ebp
0101D6E6 8BEC mov ebp,esp
0101D6E8 8B45 10 mov eax,dword ptr ss:[ebp+10]
0101D6EB 8BA8 9C000000 mov ebp,dword ptr ds:[eax+9C]
0101D6F1 E9 4E040000 jmp notepad_.0101DB44
0101D6F6 E8 00000000 call notepad_.0101D6FB
0101D6FB 5D pop ebp
0101D6FC 81ED 782F0200 sub ebp,22F78
0101D702 68 04010000 push 104
0101D707 8D85 BF360200 lea eax,dword ptr ss:[ebp+236BF]
0101D70D 50 push eax
0101D70E 6A 00 push 0
0101D710 FF95 DD3A0200 call dword ptr ss:[ebp+23ADD]
0101D716 F785 E3340200 01000000 test dword ptr ss:[ebp+234E3],1
0101D720 0F84 66010000 je notepad_.0101D88C
0101D726 DBAD C3370200 fld tbyte ptr ss:[ebp+237C3]
0101D72C 0F31 rdtsc
0101D72E 80FC 00 cmp ah,0
0101D731 0F84 15040000 je notepad_.0101DB4C
0101D737 6A 00 push 0
0101D739 6A 00 push 0
0101D73B 6A 11 push 11
0101D73D 6A FE push -2
0101D73F FF95 763A0200 call dword ptr ss:[ebp+23A76]
0101D745 FF95 8A3A0200 call dword ptr ss:[ebp+23A8A]
回来之后这里就是异常handle做的操作了。 发现就是在填充异常结构体。
然后这里如果是0x80000001就继续不是的话。就退出了。
0101D6AA 8B58 04 mov ebx,dword ptr ds:[eax+4] ; eax context
0101D6AD 0358 08 add ebx,dword ptr ds:[eax+8]
0101D6B0 0358 0C add ebx,dword ptr ds:[eax+C]
0101D6B3 0358 10 add ebx,dword ptr ds:[eax+10]
0101D6B6 0358 14 add ebx,dword ptr ds:[eax+14]
0101D6B9 0358 18 add ebx,dword ptr ds:[eax+18]
0101D6BC 83FB 00 cmp ebx,0
这里的eax就是下面结构体
typedef struct _CONTEXT
{+000 DWORD ContextFlags;+004 DWORD Dr0;+008 DWORD Dr1;+00C DWORD Dr2;+010 DWORD Dr3;+014 DWORD Dr6;+018 DWORD Dr7;+01C FLOATING_SAVE_AREA FloatSave;+08C DWORD SegGs;+090 DWORD SegFs;+094 DWORD SegEs;+098 DWORD SegDs;+09C DWORD Edi;+0A0 DWORD Esi;+0A4 DWORD Ebx;+0A8 DWORD Edx;+0AC DWORD Ecx;+0B0 DWORD Eax;+0B4 DWORD Ebp;+0B8 DWORD Eip;+0BC DWORD SegCs;+0C0 DWORD EFlags;+0C4 DWORD Esp;+0C8 DWORD SegSs;+0CC BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
所以他这里把几个硬件断点的值加起来跟0比较其实就是检查有无硬件断点,反硬件断点。如果发现不是领就又调到之前推出的地方去了。然后单步接着看。
0101D6BF ^\75 D2 jnz short notepad_.0101D693
0101D6C1 FFB7 57360200 push dword ptr ds:[edi+23657] ; exception exit
0101D6C7 8F80 B8000000 pop dword ptr ds:[eax+B8] ; eip
0101D6CD 89B8 B4000000 mov dword ptr ds:[eax+B4],edi
0101D6D3 C780 B0000000 04000000 mov dword ptr ds:[eax+B0],4
0101D6DD B8 00000000 mov eax,0
0101D6E2 5F pop edi
0101D6E3 C9 leave
0101D6E4 C3 retn
发现push,pop就是把异常出口给eip(0x0101D7C4),下面就是还原EBP。继续单步跟发现调用了ZwContinue然后kifastSystemCall,发现最后就走出了异常,来到了下面,0101D7E8这个就是异常handler。在哪里下个断。
0101D7C4 5E pop esi
0101D7C5 8DB5 57360200 lea esi,dword ptr ss:[ebp+23657]
0101D7CB 8D85 65300200 lea eax,dword ptr ss:[ebp+23065]
0101D7D1 8906 mov dword ptr ds:[esi],eax
0101D7D3 C746 04 04000080 mov dword ptr ds:[esi+4],80000004
0101D7DA 9C pushfd
0101D7DB 810C24 00010000 or dword ptr ss:[esp],100
后面发现,记录了0x80000004,这个可以通过DebugOption,exceptionRand看出这里是个单步异常。然后检测单步异常所以我们要全速运行跳过这段,不然会调试失败。
接下来
0101D7F4 /0F85 52030000 jnz notepad_.0101DB4C
0101D7FA |8D85 5F360200 lea eax,dword ptr ss:[ebp+2365F]
0101D800 |50 push eax
0101D801 |FF95 903B0200 call dword ptr ss:[ebp+23B90] ; GetStartupInfor anti debugger
0101D807 |8B85 6F360200 mov eax,dword ptr ss:[ebp+2366F]
0101D80D |0385 73360200 add eax,dword ptr ss:[ebp+23673]
0101D813 |0385 7F360200 add eax,dword ptr ss:[ebp+2367F]
0101D819 |0385 83360200 add eax,dword ptr ss:[ebp+23683]
0101D81F |0385 87360200 add eax,dword ptr ss:[ebp+23687]
0101D825 |0385 77360200 add eax,dword ptr ss:[ebp+23677]
0101D82B |0385 7B360200 add eax,dword ptr ss:[ebp+2367B]
0101D831 |83F8 00 cmp eax,0
0101D834 |0F85 12030000 jnz notepad_.0101DB4C
这里又是反调试,所以我们手动改变Z位让他相等不调到退出处。
接着
0101D834 /0F85 12030000 jnz notepad_.0101DB4C
0101D83A |16 push ss
0101D83B |17 pop ss
0101D83C |9C pushfd
0101D83D |58 pop eax
0101D83E |25 00010000 and eax,100
0101D843 |3D 00010000 cmp eax,100
0101D848 |0F84 FE020000 je notepad_.0101DB4C
0101D84E |8D85 A3360200 lea eax,dword ptr ss:[ebp+236A3]
又是个单步反调试,所以我们继续把判断相等的位置改成不跳转。然后继续向下分析。
0101D871 FF95 BA3B0200 call dword ptr ss:[ebp+23BBA] ; kernel32.CreateProcessA
这里就是创建双进程守护了。
0006FF8C 0101D877 /CALL to CreateProcessA from notepad_.0101D871
0006FF90 0101DE42 |ModuleFileName = "C:\Documents and Settings\Administrator\桌面\notepad.exe.ISP.EXE"
0006FF94 00000000 |CommandLine = NULL
0006FF98 00000000 |pProcessSecurity = NULL
0006FF9C 00000000 |pThreadSecurity = NULL
0006FFA0 00000000 |InheritHandles = FALSE
0006FFA4 00000004 |CreationFlags = CREATE_SUSPENDED
0006FFA8 00000000 |pEnvironment = NULL
0006FFAC 00000000 |CurrentDir = NULL
0006FFB0 0101DDE2 |pStartupInfo = notepad_.0101DDE2
0006FFB4 0101DE26 \pProcessInfo = notepad_.0101DE26
这是创建的参数。继续
0101D87D FF95 A73B0200 call dword ptr ss:[ebp+23BA7] ; kernel32.DebugActiveProcess
调试进程。继续单步几步发现
0101D8DB FF95 ED3A0200 call dword ptr ss:[ebp+23AED] ; kernel32.CreateFileA
查看参数就是打开自己。
0101D8E6 FF95 293B0200 call dword ptr ss:[ebp+23B29] ; kernel32.GetFileSize
然后获得文件大小,
0101D8F3 FF95 FD3A0200 call dword ptr ss:[ebp+23AFD] ; kernel32.GlobalAlloc
申请一块内存。然后继续单步发现读文件,
0101D912 E8 33030000 call notepad_.0101DC4A
然后发现call一个地址,进去看下, 发现是循环在做一些操作文件,然后比对相等不相等。如果不相等就跳到退出那里。
继续单步往下,发现是在判断一些标志位。
0101D9E9 E8 0D020000 call notepad_.0101DBFB
然后 进去看下
发现是在反一些调试窗口
继续往下发现都是在找一些monitor windows,然后继续往下
0101DA26 64:FF35 30000000 push dword ptr fs:[30]
0101DA2D 58 pop eax
0101DA2E 8B40 0C mov eax,dword ptr ds:[eax+C]
0101DA31 8B40 0C mov eax,dword ptr ds:[eax+C]
0101DA34 C740 20 00000000 mov dword ptr ds:[eax+20],0
0101DA3B F785 E3340200 08000000 test dword ptr ss:[ebp+234E3],8
发现就是在操作PEB,0xC处即InLoadOrderModuleList,然后进行一些校验,继续往下
0101DA52 E8 F3010000 call notepad_.0101DC4A
发现一个call,参数是壳入口,发现又是在自校验壳,进去看下,自校验,这里不能在结尾下断点因为这样会改变比较程序的值,造成不匹配退出所有只能跟。
然后单步跟发现校验结束回到调用点
0101D3BC FF95 C63A0200 call dword ptr ss:[ebp+23AC6] ; VitualProtect
0101D3C2 E8 2F030000 call notepad_.0101D6F6 ; anti_bugger && self check
0101D3C7 8B85 DB340200 mov eax,dword ptr ss:[ebp+234DB]
0101D3CD BB 01000000 mov ebx,1
0101D3D2 E8 AB060000 call notepad_.0101DA82
发现传进去PE文件的base和1,然后call,进去看在操作section,循环比较看是不是rsrc,.rsr,就是在判断区段是否被修改。
然后后面一些指令也是在操作判断pe结构,继续往下跟,发现申请一块内存。然后继续往下跟发现又是个不知道的循环赋值。但是发现edi里被存上了kernel32.dll,并且后面继续跟发现有loadlibrary,继续跟
0101D4D6 038D DB340200 add ecx,dword ptr ss:[ebp+234DB] ; notepad_.01000000
0101D4DC 8B56 04 mov edx,dword ptr ds:[esi+4]
0101D4DF 0395 DB340200 add edx,dword ptr ss:[ebp+234DB]
0101D4E5 E9 AE000000 jmp notepad_.0101D598
发现在操作PE结构,寻找一些东西。继续,发现
0101BF30 4B 45 52 4E 45 4C 33 32 ERNEL32
0101BF40 2E 44 4C 4C 00 41 44 56 41 50 49 33 32 2E 64 6C .DLL.ADVAPI32.dl
0101BF50 6C 00 43 4F 4D 43 54 4C 33 32 2E 64 6C 6C 00 63 l.COMCTL32.dll.c
0101BF60 6F 6D 64 6C 67 33 32 2E 64 6C 6C 00 47 44 49 33 omdlg32.dll.GDI3
0101BF70 32 2E 64 6C 6C 00 6D 73 76 63 72 74 2E 64 6C 6C 2.dll.msvcrt.dll
0101BF80 00 53 48 45 4C 4C 33 32 2E 64 6C 6C 00 55 53 45 .SHELL32.dll.USE
0101BF90 52 33 32 2E 64 6C 6C 00 57 49 4E 53 50 4F 4F 4C R32.dll.WINSPOOL
0101BFA0 2E 44 52 56 00 00 4C 6F 61 64 4C 69 62 72 61 72 .DRV..LoadLibrar
0101BFB0 79 41 00 00 47 65 74 50 72 6F 63 41 64 64 72 65 yA..GetProcAddre
0101BFC0 73 73 00 00 56 69 72 74 75 61 6C 50 72 6F 74 65 ss..VirtualProte
0101BFD0 63 74 00 00 56 69 72 74 75 61 6C 41 6C 6C 6F 63 ct..VirtualAlloc
0101BFE0 00 00 56 69 72 74 75 61 6C 46 72 65 65 00 00 00 ..VirtualFree...
0101BFF0 45 78 69 74 50 72 6F 63 65 73 73 00 00 00 52 65 ExitProcess...Re
0101C000 67 43 6C 6F 73 65 4B 65 79 00 00 00 43 72 65 gCloseKey...Cre
发现是在解密一些用到的字符串。继续往下跟,然后又发现在解密一段东西,跟着循环发现是解密代码。如下
0101D5D3 64:A1 18000000 mov eax,dword ptr fs:[18]
0101D5D9 8B40 24 mov eax,dword ptr ds:[eax+24]
0101D5DC 50 push eax
0101D5DD 6A FF push -1
0101D5DF 6A 01 push 1
0101D5E1 FF95 6C3B0200 call dword ptr ss:[ebp+23B6C]
0101D5E7 83F8 00 cmp eax,0
0101D5EA 0F84 54050000 je notepad_.0101DB44
0101D5F0 8985 BB360200 mov dword ptr ss:[ebp+236BB],eax
0101D5F6 6A 00 push 0
0101D5F8 6A 00 push 0
0101D5FA 6A 00 push 0
0101D5FC 8D85 E1320200 lea eax,dword ptr ss:[ebp+232E1]
0101D602 50 push eax
0101D603 6A 00 push 0
0101D605 6A 00 push 0
0101D607 6A FF push -1
0101D609 FF95 533B0200 call dword ptr ss:[ebp+23B53]
0101D60F F785 E3340200 00100000 test dword ptr ss:[ebp+234E3],1000
0101D619 74 1C je short notepad_.0101D637
0101D61B 6A 00 push 0
0101D61D 6A 00 push 0
0101D61F 6A 03 push 3
0101D621 6A 00 push 0
0101D623 6A 00 push 0
0101D625 68 00000080 push 80000000
0101D62A 8D85 BF360200 lea eax,dword ptr ss:[ebp+236BF]
0101D630 50 push eax
0101D631 FF95 ED3A0200 call dword ptr ss:[ebp+23AED]
0101D637 61 popad
0101D638 50 push eax
0101D639 64:FF35 00000000 push dword ptr fs:[0]
0101D640 64:8925 00000000 mov dword ptr fs:[0],esp
发现这里在读TEB,读取一些信息,然后openthread,然后
ss:[0101E2D6]=7C8104BC (kernel32.CreateRemoteThread)
创建远程线程。查看一下参数eax也就是线程是什么,发现是前面做的反调试相关的东西。所以我们不能让他创建,所以把栈上回调改成0,不创建。
然后继续单步经过一些列单步到了
0101425F 90 nop
01014260 60 pushad
01014261 BE 00000101 mov esi,notepad_.01010000
01014266 8DBE 0010FFFF lea edi,dword ptr ds:[esi+FFFF1000]
0101426C 57 push edi
0101426D 83CD FF or ebp,FFFFFFFF
01014270 EB 10 jmp short notepad_.01014282
01014272 90 nop
01014273 90 nop
01014274 90 nop
01014275 90 nop
01014276 90 nop
01014277 90 nop
01014278 8A06 mov al,byte ptr ds:[esi]
0101427A 46 inc esi
0101427B 8807 mov byte ptr ds:[edi],al
0101427D 47 inc edi
0101427E 01DB add ebx,ebx
01014280 75 07 jnz short notepad_.01014289
01014282 8B1E mov ebx,dword ptr ds:[esi]
01014284 83EE FC sub esi,-4
01014287 11DB adc ebx,ebx
01014289 ^ 72 ED jb short notepad_.01014278
0101428B B8 01000000 mov eax,1
01014290 01DB add ebx,ebx
01014292 75 07 jnz short notepad_.0101429B
01014294 8B1E mov ebx,dword ptr ds:[esi]
01014296 83EE FC sub esi,-4
发现就是又加了层UPX壳,然后这个下面的位置下个断点。
然后全速运行就脱壳完了,然后dump发现不能dump,
然后里面有反dump,
antidump
push fs:[30h]
pop eax
MOV EAX,[EAX+0Ch]
MOV EAX,[EAX+0Ch]
MOV DWORD PTR [EAX+20h],0;修改内存镜像大小为0
我们就要把镜像大小修复了,lordPE可以修复。然后dump就成功了。然后修复impactREC就好了。
ASP壳
我们可以观察下加壳之后的程序OEP也变了,并且节也多了两个。脱壳方式跟upx一样。
加密壳
前面步骤跟上面一样,在重建导入表时候发现
发现导入表值是无效的,然后随便打开个看到地址1D0C8过去看看。
这里本来应该是系统函数地址,这里显然不是,被加密了,过去看下填充的地址是什么
这里发现从eax取出真正的函数地址, 然后做了加密跳转过去。这时候需要写个脚本自动把这里的函数地址替换回去。
注意观察加密片段如下
0038001F 8105 3A003800 B9C1F260 add dword ptr ds:[38003A],60F2C1B9
00380029 A1 3A003800 mov eax,dword ptr ds:[38003A]
0038002E 812D 3A003800 B9C1F260 sub dword ptr ds:[38003A],60F2C1B9
00380038 FFE0 jmp eax
我们就是要取得eax里面是真正函数地址,所以在这里处理时候可以用sti,sti,两个跳过,sti就是F7的意思,单步两部此时eax就是函数地址,此外根据ImportREC或者OD里观察,iAT起始地址是0x0041D0C4结束地址是0x0041D13C,所以修复的就是这两块直间的值,此外这个程序oep是0x00401000
根据程序修复iat脚本如下
//1.找到oep
//2.找到api数组首地址和end地址
//3.还原地址var oep
var esp_addr
var iat_start
var iat_end
var tmp1
var tmp2
//到达OEPmov iat_start,0041D0C4
mov iat_end,0041D13C
mov oep,00401000bphws oep,"x"
//清理断点
esto
bc
bpmc
bphwc
mov oep,eip
mov esp_addr,espmov tmp2,iat_start//下面是修复代码,就是一个一个取导入表里面的值然后修复
label_loop:cmp tmp2,iat_end
je label_exit
add tmp2,4
mov tmp1,[tmp2]cmp tmp1,0
je label_loopmov eip,tmp1
sti
sti
mov [tmp2],eax
jmp label_loop
label_exit:
mov eip,oep
mov esp,esp_addr
dpe "Unpack.exe",eip
ret
然后run script,就修改好了,
然后用ImportREC修复iat,这时程序就脱壳成功,并且跑起来了,另外脚本编写编码注意gbk,不过我没遇到这种问题。
或者还可以如下,写静态脚本
修复IAT的几种方式:
1.在到达oep之前寻找IAT加密地址跳过,或者找Majec jmp
2.到达oep之后写汇编代码进行修复或者写脚本进行修复
一:在OEP之前进行patch
GetModuleHanldleA+GetProcAddress
1.
shl byte ptr ds:[ecx],cl //这两句是加密函数名
xor byte ptr ds:[ecx],al
----------------------------------------
2.
mov eax,dword ptr ss:[ebp+4071A6] //这里获取加密地址
这里进行patch的话有两个思路:
1.把获取加密的地址nop掉
2.之后肯定会把eax写入一个地址表里,在写入之发再把正确的值进行写入。
F7往下走就可以看到了mov dword ptr ds:[edi],eax 这里可以进行patch,
看栈翻一翻会有惊喜!
mov eax,dword ptr ss:[esp-B8]
mov dword ptr ds:[edi],eax
jmp 写入的地址
二:到OEP之后进行Patch
003F0000 B8 C8D04100 mov eax,41D0C8//iat开始地址
003F0005 8B10 mov edx,dword ptr ds:[eax]
003F0007 83FA 00 cmp edx,0
003F000A 74 14 je 003F0020//判断是否为0
003F000C 8B4A 02 mov ecx,dword ptr ds:[edx+2] key指针
003F000F 8B09 mov ecx,dword ptr ds:[ecx] 取出key1
003F0011 8B52 06 mov edx,dword ptr ds:[edx+6] 取出key2
003F0014 81FA 00000040 cmp edx,40000000
003F001A 77 13 ja 003F002F 加密方式判断
003F001C 33CA xor ecx,edx xor加密
003F001E 8908 mov dword ptr ds:[eax],ecx 保存地址
003F0020 83C0 04 add eax,4 下一个地址
003F0023 3D 3CD14100 cmp eax,41D13C 比较是否是结尾
003F0028 ^ 72 DB jb 003F0005
003F002A - E9 D10FAAFF jmp 00401000 跳到OEP
003F002F 03CA add ecx,edx AND加密
003F0031 ^ EB EB jmp 003F001E
B8C8D041008B1083FA0074148B4A028B098B520681FA00000040771333CA890883C0043D3CD1410072DBE9D10F020003CAEBEB
也可以读取加密地址的代码是xor(8135)还是and(8105)然后进行分支解密
把上面这段脚本代码粘贴到程序空闲的地方,然后把eip拉到这里,相关跳转地址改成自己机器相应的地址即可。
另外还可以在iat表下写断点,看谁给他写的,加密也就在那个附近了。然后把加密nop调,然后写进去真实地址,就达到修复iat的作用了。
再观察一个加密壳样本,发现有pushad,试下esp定律下硬件断点。
0044E214 55 push ebp ; oep
0044E215 8BEC mov ebp,esp
0044E217 83C4 F0 add esp,-10
0044E21A B8 8CE04400 mov eax,Project1.0044E08C
0044E21F E8 587EFBFF call Project1.0040607C
0044E224 A1 4CFF4400 mov eax,dword ptr ds:[44FF4C]
0044E229 8B00 mov eax,dword ptr ds:[eax]
0044E22B E8 08E6FFFF call Project1.0044C838
0044E230 8B0D 28004500 mov ecx,dword ptr ds:[450028] ; Project1.00451B64
0044E236 A1 4CFF4400 mov eax,dword ptr ds:[44FF4C]
0044E23B 8B00 mov eax,dword ptr ds:[eax]
0044E23D 8B15 ACDE4400 mov edx,dword ptr ds:[44DEAC] ; Project1.0044DEF8
0044E243 E8 08E6FFFF call Project1.0044C850
0044E248 A1 4CFF4400 mov eax,dword ptr ds:[44FF4C]
0044E24D 8B00 mov eax,dword ptr ds:[eax]
0044E24F E8 7CE6FFFF call Project1.0044C8D0
0044E254 E8 8F5EFBFF call Project1.004040E8
很快找到了oep但是观察下面函数调用,没有显示名称,八成iat是加密的而且,到了oep都没显示,猜测是动态解密。然后先按照流程操作,dump,修复导入表发现导入表又是一堆无效的NO,过去看一下发现如下
00452118 00461624 Project1.00461624
0045211C 004611EC Project1.004611EC
00452120 0046199C Project1.0046199C
00452124 00461198 Project1.00461198
00452128 00461354 Project1.00461354
0045212C 00461210 Project1.00461210
00452130 00461B1C Project1.00461B1C
00452134 00462068 Project1.00462068
00452138 00461B58 Project1.00461B58
0045213C 0046190C Project1.0046190C
00452140 00461378 Project1.00461378
00452144 0046181C Project1.0046181C
00452148 00461660 Project1.00461660
0045214C 004618AC Project1.004618AC
00452150 004613E4 Project1.004613E4
00452154 004618C4 Project1.004618C4
00452158 00461B04 Project1.00461B04
0045215C 00461A08 Project1.00461A08
然后我们去第一个iat位置去看下,把eip拉过去。
然后不停的单步观察,注意看什么时候寄存器或者调用的参数出现真实函数地址,发现如下。
此时去eax找edi数值偏移4的地方取到函数地址,猜测eax处就是真正导入表,所以在dump查看eax,如下,即导入表。
继续单步
009E2846 61 popad
此时eax里面是函数真正地址。所以下面操作就是写个脚本根据这些地址和流程修复iat。
根据od和importREC已经刚在跟踪OD的信息知道oep,iat表起始和结尾地址,替换拿到真正iat放在eax时的地址。
脚本跟前面类似,就是就是细节比如,不是单步两次了,是在把真正函数地址eax使用时候下断,也就是在009E2846断下,然后把此时eax(里面存的真正函数地址)写到它对应的iat地址里。注意如果脚本反复跑有异常,那就分段泡脚本,然后把每次考的二进制数据复制到导入表位置,然后dump,修复iat,运行成功。
脚本代码参考。
//1.找到oep
//2.找到api数组首地址和end地址
//3.还原地址var oep
var esp_addr
var iat_start
var iat_end
var tmp1
var tmp2
//下面这个变量记录真正iat的地址
var tmp_bp
//到达OEPmov iat_start,00452118
mov iat_end,000526D8
mov oep,0044E214
mov tmp_bp,009E2846
bphws oep,"x"
//清理断点
esto
bc
bpmc
bphwc
mov oep,eip
mov esp_addr,espmov tmp2,iat_start//下面是修复代码,就是一个一个取导入表里面的值然后修复
label_loop:cmp tmp2,iat_end
je label_exit
add tmp2,4
mov tmp1,[tmp2]cmp tmp1,0
je label_loopmov eip,tmp1bp tmp_bp
esto
bc tmp_bpmov [tmp2],eax
jmp label_loop
label_exit:
mov eip,oep
mov esp,esp_addr
ret
然后程序脱壳就成功了。
VMP壳
这是现在的主流和脱壳难度最大的壳。
首先一般此类壳都有虚拟机检测,需要过虚拟机检测。
虚拟机检测资料
https://blog.csdn.net/youyou519/article/details/94181836
所以in,str等这些指令加壳程序无法模拟。但是push ebp,mov ebp,esp是可以使用自己编造的指令集替换的。
然后我们看下2.x版本的vmp壳。
od增加插件zeus可以分析得到表单
Load PCode:
01142762 01142762 mov al,byte ptr ds:[esi-1]
---------------------------------------------
Pcode Decode :
0114276C add al,bl
01142772 neg al
01142778 ror al,1
01142799 add al,0be
011427A5 add bl,al
---------------------------------------------
Dispatch Table :
011427BB 011427BB mov ecx,dword ptr ds:[eax*4+1142a05]
Dispatch Base : 01142A05
Dispatch Reg : ecx
---------------------------------------------
Handler Decode :
0114324A dec ecx
01143255 add ecx,0
---------------------------------------------
Handler Entry :
01144C82 01144C82 retn 44
---------------------------------------------
VM Initial Info:
[+00] <- 00000000 RELOC
[+01] <- 00000000 ANTIDUMP
[+02] <- 0006FFB0 ecx
[+03] <- 7FFDE000 ebx
[+04] <- 7C930208 edi
[+05] <- 7FFDE000 ebx
[+06] <- 00000000 eax
[+07] <- 00000246 EFL
[+08] <- 7C92E4F4 edx
[+09] <- FFFFFFFF esi
[+0A] <- 0A6462B9 RELOC
[+0B] <- 8749A2CD RETADDR
[+0C] <- C58DE0B9 INITDATA
---------------------------------------------
VMInitKey : C58DE0B9
VMDecodeKey : 010AB3D9
VMOpcodeStart : 010AB3D9
010AB3D8 VMEipStart : 010AB3D8
VMOpcode Direction : ↓
---------------------------------------------
011421C0 00: 011421C1 ---> 011421C0[VM_Cpuid ]
011434E2 01: 011434E3 ---> 011434E2[VM_GetR32 ]
0114461F 02: 01144620 ---> 0114461F[VM_WmDs32 ]
01143288 03: 01143289 ---> 01143288[VM_Shl8 ]
比如就是把一条指令cpuid膨胀变成了一个函数。如果像IO指令没法模拟的,就退出虚拟机指令环境,执行原有指令。
然后想过有检测虚拟机的指令,就把他patch掉。这只是思路,具体根据虚拟机不同版本方法有变化。
windows脱壳复习相关推荐
- linux 下 upx 脱壳笔记
linux很少有需要crack的软件,所以最近总是自娱自乐.自己写的软件自己破着玩但是由于都是知道自己的手段,没有什么意思.真的希望有高手们写些crackme for linux . 最近看了看win ...
- 索(shen)引(keng)大全
索(shen)引(keng)大全 (博主已获得称号:挖坑达人lv.5 (QvQ)) 下列各个索(shen)引(keng)不按时间顺序. 学习笔记: 计算机网络_学习笔记 索引 「索引」<Pyth ...
- 初入职场必备丨二进制面试问题汇总
汇编:沫沫 编者按:本贴面试题目汇总来源于线下和网络,仅供求职者面试前参考使用,侵删. 01安全研究岗位 一面 1. 不用+ - * /,输入两位数,打印出结果 示例:输入2 3,输出5 2. 不 ...
- 职称计算机隐藏桌面图标,2016职称计算机Windows复习巩固题
职称计算机Windows考试多做复习巩固题,对考试很有帮助.以下是百分网小编精心为大家整理的2016职称计算机Windows复习巩固题,希望对大家有所帮助!想了解更多相关信息请持续关注我们应届毕业生考 ...
- 专转本计算机word知识点,江苏专转本计算机windows和word复习资料(含答案).doc
Windows 操作系统部分知识点分析 知识点1:windows是一个多任务图形化的操作系统,DOS是一个单任务字符界面的操作系统. [往年考题] 13.下列__D__不属于多用户多任务操作系统的软件 ...
- Windows PowerShell 实战指南-附录(复习实验)-实验回顾1
任务1: 运行一个命令,从而显示应用程序事件日志中最新的100个条目,不要使用Get-WinEvent. 解答: get-eventlog -computername localhost -logna ...
- 「信息安全技术」期末复习宝典 【整理完毕】
文章目录 选择 密码与隐藏技术 数字签名与认证 身份与访问安全 计算机病毒与黑客 网络攻击与防范 网络安全与编程 设备与环境安全 软件保护技术1 软件保护技术2 社会工程学 区块链基础 问答题 1.简 ...
- Windows Phone开发(39):漫谈关键帧动画上篇 转:http://blog.csdn.net/tcjiaan/article/details/7550506...
尽管前面介绍的几种动画会让觉得很好玩了,但是,不知道你是否发现,在前面说到的一系列XXXAnimation中,都有一个共同点,那就是仅仅针对两个值的目标值之间产生动画,如果使用By,将在原值和加上By ...
- 计算机应用 含升学方向,对口升学《计算机应用基础》复习资料总汇(含答案))讲述.doc...
对口升学<计算机应用基础>复习资料总汇 第一部分?? 一.单项选择题 1.世界上第一台电子数字计算机取名为(????). A.UNIVAC????B.EDSAC????C.ENIAC??? ...
- Windows Server 2016 笔记
从业界普遍实践结果来看,Windows Server在服务器领域真是不太好用.但是,有些时候由于种种原因不得不用,所以还是有必要了解一下的.今天参加了一个Windows Server的培训,主要面对W ...
最新文章
- Java基础(七)--Exception异常处理
- iOS更改AppIcon
- 被放弃的概率权,机器下围棋不理会沉没成本
- P2339 提交作业usaco
- 如何使用Webpack
- 140.String Compression
- KindEditor在eclipse里的配置方法
- html5 加上魔法,简单易懂的React魔法(28):是时候添加一些CSS样式了
- C语言九九乘法表的代码(含注释)
- 妙算2的串口用自己的接线(杜邦线)连接无人机210或者stm32
- 数据科学必备用Python进行描述性统计数据分析详解
- Java-Tcp/Ip-CS控制台聊天应用Demo
- Ubuntu 20.04/21.04 不能检测到外部HDMI显示器
- 人类一败涂地做图教程_人类一败涂地皮肤怎么弄 人类一败涂地皮肤制作教程...
- Redis可视化管理工具:Another Redis DeskTop Manager
- latex从入门到精通
- 文件夹选择框 文件选择框
- 【English】新征程,我们在路上
- 日语 | 日本50音
- 电影-满城尽带黄金甲