依稀记得第一次接触Hook的概念是在周伟民先生的书中-><<多任务下的数据结构与算法>>,当时觉得Hook很奇妙,有机会要学习到,正好近段日子找来了MiniHook,就一起分享一下。

本篇文章是在x64下测试与分析jmp+offset类型的Hook,并且逆推测出热补丁的简单用法,MinHook它的中心就是覆盖重写并且可以复原。知道大概的思路后后让我们先来具体的实现MinHook再去做测试。

首先是堆的申请(申请PAGE_SIZE大小自动生长的堆),以下是实现与卸载

  1. 1 NTSTATUS WINAPI Initialize(VOID)
  2. 2 {
  3. 3 NTSTATUS Status = STATUS_SUCCESS;
  4. 4
  5. 5 EnterSpinLock();
  6. 6
  7. 7 if (__HeapHandle == NULL)
  8. 8 {
  9. 9 __HeapHandle = HeapCreate(0,//申请堆栈
  10. 10 0, //提交 PAGE_SIZE
  11. 11 0); //If dwMaximumSize is 0, the heap can grow in size.自动增长
  12. 12 if (__HeapHandle != NULL)
  13. 13 {
  14. 14 //没有实现
  15. 15 }
  16. 16 else
  17. 17 {
  18. 18 Status = STATUS_MEMORY_NOT_ALLOCATED;
  19. 19 }
  20. 20 }
  21. 21 else
  22. 22 {
  23. 23 Status = STATUS_ADDRESS_ALREADY_EXISTS;
  24. 24 }
  25. 25
  26. 26 LeaveSpinLock();
  27. 27
  28. 28 return Status;
  29. 29 }
  30. 30
  31. 31 NTSTATUS WINAPI Uninitialize(VOID)
  32. 32 {
  33. 33 NTSTATUS Status = STATUS_SUCCESS;
  34. 34
  35. 35 return Status;
  36. 36 }

第一幕CreateHook

CreateHook 第一步:判断内存是否申请好了,是否可执行,判断是否已经Hook过了,如果已经Hook过,当让他返回其所在位置,因为此时他的地址位置已经可以用来启动Hoook,如下代码详解

  1. 1 UINT FindHookEntry(LPVOID FunctionAddress)
  2. 2 {
  3. 3 UINT i;
  4. 4 for (i = 0; i < __Hooks.Length; ++i)
  5. 5 {
  6. 6 if ((ULONG_PTR)FunctionAddress == (ULONG_PTR)__Hooks.Items[i].TargetFunctionAddress)
  7. 7 return i;
  8. 8 }
  9. 9 return STATUS_NOT_FOUND;
  10. 10 }

CreateHook 第二步:进行Hook,在这里用到TRAMPOLINE结构体,我称之为跳板结构体,作为数据的中间传输过渡,TRAMPOLINE中几个注意的成员是1.Relay:在x64下Fake函数到原函数的中转站(x86用不到),2.OldIPs:原函数地址的偏移字节的保存3.NewIPs: 已经写入FakeFunctionAddress函数的字节数 4.MemorySlot:32字节原函数地址的前7个字节和跳转指令后的字节 5.PachAbove:热补丁

  1. 1 typedef struct _TRAMPOLINE
  2. 2 {
  3. 3 LPVOID TargetFunctionAddress; // [In] Address of the target function.
  4. 4 LPVOID FakeFunctionAddress; // [In] Address of the detour function.
  5. 5 LPVOID MemorySlot; // MemorySlot 32字节原函数地址的前五个字节和跳转指令后的字节
  6. 6
  7. 7 #if defined(_M_X64) || defined(__x86_64__)
  8. 8 LPVOID Relay; // [Out] Address of the relay function.
  9. 9 #endif
  10. 10 BOOL PatchAbove; // [Out] Should use the hot patch area? //Patch --->热补丁哦 //0xA 0xB
  11. 11 UINT IP; // [Out] Number of the instruction boundaries.
  12. 12 UINT8 OldIPs[8]; // [Out] Instruction boundaries of the target function.
  13. 13 UINT8 NewIPs[8]; // [Out] Instruction boundaries of the trampoline function.
  14. 14 } TRAMPOLINE, *PTRAMPOLINE;

CreateHook 第三步: 分配一块内存用来保存Trampoline里的MemorySlot数据 ,以下是MemorySlot结构体定义(MemorySlot内存构建放到最后的代码链接中):

  1. 1 #define MEMORY_BLOCK_SIZE 0x1000
  2. 2 #if defined(_M_X64) || defined(__x86_64__)
  3. 3 #define MEMORY_SLOT_SIZE 64
  4. 4 #else
  5. 5 #define MEMORY_SLOT_SIZE 32
  6. 6 #endif
  7. 7
  8. 8 // Max range for seeking a memory block. (= 1024MB)
  9. 9 #define MAX_MEMORY_RANGE 0x40000000
  10. 10
  11. 11 typedef struct _MEMORY_SLOT
  12. 12 {
  13. 13 union
  14. 14 {
  15. 15 struct _MEMORY_SLOT *Flink;//下一指针
  16. 16 UINT8 BufferData[MEMORY_SLOT_SIZE];
  17. 17 };
  18. 18 } MEMORY_SLOT, *PMEMORY_SLOT; //32字节
  19. 19
  20. 20 typedef struct _MEMORY_BLOCK
  21. 21 {
  22. 22 _MEMORY_BLOCK* Flink;
  23. 23 PMEMORY_SLOT FreeMeorySlotHead; // First element of the free slot list.空闲插槽列表的第一个元素。
  24. 24 UINT UsedCount;
  25. 25 } MEMORY_BLOCK, *PMEMORY_BLOCK; //12字节

CreateHook 第四步:CreateTrampoline

Hook的Target我们这里先使用MessageBoxW,作为一个详细的jmp跳转流程解释,然后我写了几个汇编程序去进行其他E8,Call等指令的跳转实现,不过它是怎么跳转的我会在下面跳转的时候贴出来,首先来玩X64下的MessageBoxW,

  1. 64 MessageBox
  2. 00007FF97B4485A0 48 83 EC 38 sub rsp,38h
  3. 00007FF97B4485A4 45 33 DB xor r11d,r11d
  4. 00007FF97B4485A7 44 39 1D 7A 33 03 00 cmp dword ptr [gfEMIEnable (07FF97B47B928h)],r11d
  5. 00007FF97B4485AE 74 2E je MessageBoxW+3Eh (07FF97B4485DEh)
  6. 00007FF97B4485B0 65 48 8B 04 25 30 00 00 00 mov rax,qword ptr gs:[30h]
  7. 00007FF97B4485B9 4C 8B 50 48 mov r10,qword ptr [rax+48h]
  8. 00007FF97B4485BD 33 C0 xor eax,eax
  9. 00007FF97B4485BF F0 4C 0F B1 15 98 44 03 00 lock cmpxchg qword ptr [gdwEMIThreadID (07FF97B47CA60h)],r10
  10. 00007FF97B4485C8 4C 8B 15 99 44 03 00 mov r10,qword ptr [gpReturnAddr (07FF97B47CA68h)]
  11. 00007FF97B4485CF 41 8D 43 01 lea eax,[r11+1]
  12. 00007FF97B4485D3 4C 0F 44 D0 cmove r10,rax
  13. 00007FF97B4485D7 4C 89 15 8A 44 03 00 mov qword ptr [gpReturnAddr (07FF97B47CA68h)],r10
  14. 00007FF97B4485DE 83 4C 24 28 FF or dword ptr [rsp+28h],0FFFFFFFFh
  15. 00007FF97B4485E3 66 44 89 5C 24 20 mov word ptr [rsp+20h],r11w
  16. 00007FF97B4485E9 E8 A2 FE FF FF call MessageBoxTimeoutW (07FF97B448490h)
  17. 00007FF97B4485EE 48 83 C4 38 add rsp,38h

前面讲过我们是通过跳转加指令形式跳转到我们需要到的地址处,上面代码注释中我们了解到OldPos与NewPos是在MemorySlot创建过程对原函数地址的偏移字节的保存和已经写入FakeFunctionAddress函数的字节数,如下

  1. 1 ULONG_PTR OldInstance = (ULONG_PTR)Trampoline->TargetFunctionAddress + OldPos;
  2. 2 ULONG_PTR NewInstance = (ULONG_PTR)Trampoline->MemorySlot + NewPos;
  3. 3 //数据
  4. 4 //OldPos是指的指令的偏移字节 即5个字节中的第2345位.OldInstance地址
  5. 5 //指令长度

了解到一些后,我们就应该去真正的对MemorySlot去构建,他的构建用了一个超级大的do-While()循坏(因为实践了好几种跳转指令,心累),x86下的MessageBoxW跳转在7字节处,所以为了之后的恢复,我们需要把7字节的内容做一个保存,这就是所谓的OriginalDataBackup数组的作用->用来恢复也就是解除Hook,后面会逐步解析他的作用和位置,我们这里先记住即可

MemorySlot开始申请32字节的长度,,我们利用反汇编引擎HDE计算出MessageBoxW函数基地址,从上面给出的MessageBoxW的地址内容中,我们可以看到到达5字节的加法是先加4个字节到下一地址,然后加3到跳转位置,记录在OldPos,NewPos中

  1. CopyCodeLength = HDE_DISASM((LPVOID)OldInstance, &hde);
  2. if (hde.flags & F_ERROR)
  3. {
  4. return FALSE;
  5. }
  6.  
  7. CopyCodeData = (LPVOID)OldInstance;
  8. .....
  9.  
  10. Trampoline->OldIPs[Trampoline->IP] = OldPos;
  11. Trampoline->NewIPs[Trampoline->IP] = NewPos;
  12. Trampoline->IP++;

到达7字节了,我们就可以去做跳回MessageBoxW基地址加5字节偏移跳转指令了

  1. 1if (OldPos >= sizeof(JMP_REL))
  2. {
  3. // The trampoline function is long enough.
  4.  
  5. #if defined(_M_X64) || defined(__x86_64__)
  6.  
  7. //OldInstance = 00007FF97B4485A7;
  8. jmp.Address = OldInstance;
  9. #else
  10. //OldInstance = 74CA8B85
  11.  
  12. //目标 = 源 + Offset + 5
  13. //Offset = 目标 - (源 + 5)
  14. jmp.Operand = (UINT32)(OldInstance - (NewInstance + sizeof(jmp))); //计算跳转到目标的偏移
  15.  
  16. #endif
  17. CopyData = &jmp;
  18. CopyDataLength = sizeof(jmp);
  19.  
  20. IsLoop = TRUE;
  21. }
  1. 1 //这里是热补丁的判断 是否有足够的位置长跳转
  2. 2 if (OldPos < sizeof(JMP_REL)
  3. 3 && !IsCodePadding((LPBYTE)Trampoline->TargetFunctionAddress + OldPos, sizeof(JMP_REL) - OldPos))
  4. 4 {
  5. 5
  6. 6 // Is there enough place for a short jump?
  7. 7 //没有有足够的位置长跳转,那是否有足够的位置短跳转?
  8. 8 if (OldPos < sizeof(JMP_REL_SHORT)
  9. 9 && !IsCodePadding((LPBYTE)Trampoline->TargetFunctionAddress + OldPos, sizeof(JMP_REL_SHORT) - OldPos))
  10. 10 {
  11. 11 return FALSE;
  12. 12 }
  13. 13 //只能写短跳转,使用热补丁
  14. 14 // Can we place the long jump above the function?
  15. 15 //热补丁:目标地址之前地址是否可执行?
  16. 16 if (!SeIsExecutableAddress((LPBYTE)Trampoline->TargetFunctionAddress - sizeof(JMP_REL)))
  17. 17 return FALSE;
  18. 18 //目标地址之前是否是可被覆盖的空白
  19. 19 if (!IsCodePadding((LPBYTE)Trampoline->TargetFunctionAddress - sizeof(JMP_REL), sizeof(JMP_REL)))
  20. 20 return FALSE;
  21. 21 //标志可以热补丁
  22. 22 Trampoline->PatchAbove = TRUE;

做了这么多工作,无非是为了MemorySlot里有数据前7个字节和跳转回MessageBoxW基地址+5字节的的偏移,构造好后,我们的TRAPOLINE结构也就完成

CreateHook第五步:添加Hook信息了(TRAMPLIONE结构体过渡),我们需要再去创建一个HookEntry的结构体去完成接收信息

  1. 1 // Hook information.
  2. 2 typedef struct _HOOK_ENTRY
  3. 3 {
  4. 4 LPVOID TargetFunctionAddress; //目标地址
  5. 5 LPVOID FakeFunctionAddress; //Fake地址即覆盖地址
  6. 6 LPVOID TrampolineMemorySlot; // Address of the trampoline function.
  7. 7 UINT8 OriginalDataBackup[8]; // Original prologue of the target function.目标功能的原始序幕- //恢复Hook使用的存放原先数据
  8. 8
  9. 9 UINT8 PatchAbove : 1; // Uses the hot patch area. 备份原函数的5字节,重要!!!
  10. 10 UINT8 IsEnabled : 1; // Enabled.启用或者关闭
  11. 11 UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled.
  12. 12
  13. 13 UINT IP : 4; // Count of the instruction boundaries.索引 想到汇编的IP就很明白了
  14. 14 UINT8 OldIPs[8]; // Instruction boundaries of the target function.原地址的字节变化就靠它了
  15. 15 UINT8 NewIPs[8]; // Instruction boundaries of the trampoline function 用在后续解释的MemorySlot中
  16. 16 } HOOK_ENTRY, *PHOOK_ENTRY; //44字节
  17. 17
  18. 18
  19. 19 typedef struct _HOOK_INFORMATION_
  20. 20 {
  21. 21 PHOOK_ENTRY Items; // Data heap
  22. 22 UINT MaximumLength; // Size of allocated data heap, items
  23. 23 UINT Length; // Actual number of data items
  24. 24 }HOOK_INFORMATION,*PHOOK_INFORMATION;

当有了这个结构体后就可以去CreateHook了,下面是构建过程:

  1. 1 if (CreateTrampoline(&Tl))
  2. 2 {
  3. 3 PHOOK_ENTRY HookEntry = AddHookEntry(); //填充一个HookInfo信息
  4. 4 if (HookEntry != NULL)
  5. 5 {
  6. 6 HookEntry->TargetFunctionAddress = Tl.TargetFunctionAddress;
  7. 7 #if defined(_M_X64) || defined(__x86_64__)
  8. 8 HookEntry->FakeFunctionAddress = Tl.pRelay;//跳转在trampoline
  9. 9 #else
  10. 10 HookEntry->FakeFunctionAddress = Tl.FakeFunctionAddress;
  11. 11 #endif
  12. 12 HookEntry->TrampolineMemorySlot = Tl.MemorySlot;
  13. 13 HookEntry->PatchAbove = Tl.PatchAbove
  14. 14 HookEntry->IsEnabled = FALSE;
  15. 15 //HookEntry->QueueEnable = FALSE;
  16. 16 HookEntry->IP = Tl.IP;
  17. 17
  18. 18 memcpy(HookEntry->OldIPs, Tl.OldIPs, ARRAYSIZE(Tl.OldIPs));
  19. 19 memcpy(HookEntry->NewIPs, Tl.NewIPs, ARRAYSIZE(Tl.NewIPs));
  20. 20
  21. 21 // Back up the target function.
  22. 22
  23. 23 if (Tl.PatchAbove)//这就是热补丁
  24. 24 {
  25. 25 memcpy(
  26. 26 HookEntry->OriginalDataBackup,
  27. 27 (LPBYTE)TargetFunctionAddress - sizeof(JMP_REL),
  28. 28 sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
  29. 29 }
  30. 30 else
  31. 31 { //存储源函数的数据内容
  32. 32 memcpy(HookEntry->OriginalDataBackup, TargetFunctionAddress, sizeof(JMP_REL));
  33. 33 }
  34. 34 if (OriginalWhitelist != NULL)//白名单,用来恢复
  35. 35 {
  36. 36 *OriginalWhitelist = HookEntry->TrampolineMemorySlot;
  37. 37 }

到这里为止终于是创建了Hook

第二幕  EnableHook

顾名思义就是启动Hook,显而易见得知它的作用无非就是覆盖原函数我们记录的那7字节,如下:

  1. 1 //SHELLCODE
  2. 2 PJMP_REL jmp = (PJMP_REL)PatchData;
  3. 3 jmp->Opcode = 0xE9;//跳转
  4. 4 jmp->Operand = (UINT32)((LPBYTE)HookEntry->FakeFunctionAddress - (PatchData + sizeof(JMP_REL)));
  5. 5

当需要解除Hook时候我们就可以用到在前面说过的OriginalDataBackup去恢复原函数,或者直接调用MemorySlot中记录下的原始序幕

  1. 1 else
  2. 2 {
  3. 3 memcpy(PatchData, HookEntry->OriginalDataBackup, sizeof(JMP_REL));
  4. 4 }

第三幕 MessageBoxW测试

  1. 1 if (CreateHook(&MessageBoxW, &FakeMessageBox,
  2. 2 reinterpret_cast<LPVOID*>(&__OriginalMessageBoxW)) != STATUS_SUCCESS)//告知要hook成什么样子
  3. 3 {
  4. 4 return;
  5. 5 }
  6. 6
  7. 7 MessageBoxW(0, L"MessageBoxW", L"MessageBoxW", 0);//没有Hook还是原先,不要也行
  8. 8 if (EnableHook(MessageBoxW) != STATUS_SUCCESS)
  9. 9 {
  10. 10 printf("EnableHook is wrong\r\n");
  11. 11 return;
  12. 12 }
  13. 13 MessageBoxW(NULL, L"CreateHook()", L"CreateHook()", 0);//启动Hook后,现在是FakeHOOK
  14. 14
  15. 15 printf("Input AnyKey To Exit\r\n");
  16. 16 getchar();
  17. 17
  18. 18 Uninitialize();//返回释放
  19. 19 }
  20. 20
  21. 21 int WINAPI FakeMessageBox(
  22. 22 _In_opt_ HWND DialogHwnd,
  23. 23 _In_opt_ WCHAR* DialogText,
  24. 24 _In_opt_ WCHAR* DialogCaption,
  25. 25 _In_ UINT Type
  26. 26 )
  27. 27 {
  28. 28 __OriginalMessageBoxW(DialogHwnd, L"FakeMessageBox", L"FakeMessageBox", Type);
  29. 29 return 0;
  30. 30 }

编译运行后出结果啦,先是原先的MessageBoxW:

                      

这是成功Hook后的:

                      

一切顺利,没有白费功夫,下面是我对EB,call,热补丁的汇编源码,我们仿照MessageBoxW的形式在test.cpp中定义函数指针,与Fake函数的输出形式。

在这里花费了功夫探索出了热补丁的简单定义是申请5字节空的内存然后 mov edi,edi,能应用正确,汇编代码如下

  1. .DATA
  2. MessageBoxW dq 0
  3. .CODE
  4.  
  5. Asm_OnInitMember PROC
  6.  
  7. mov qword ptr[rsp+8h],rcx
  8. push rbp
  9. push rdi
  10. sub rsp,28h
  11. mov rax,qword ptr[rsp+28h+8h+8h+8h]
  12. mov MessageBoxW,rax
  13. add rsp,28h
  14. pop rdi
  15. pop rbp
  16.  
  17. ret
  18. Asm_OnInitMember ENDP
  19.  
  20. Asm_1 PROC
  21.  
  22. mov qword ptr[rsp+8h],rcx
  23. push rbp
  24. push rdi
  25. sub rsp,28h
  26. xor rbx,rbx
  27. ;00007FF77A8012BC E9 7A 0B 00 00 jmp Asm_4 (07FF77A801E3Bh)
  28. mov rax,qword ptr[rsp+28h+8h+8h+8h]
  29. mov ebx,dword ptr[rax+1]
  30. add rax,rbx
  31. add rax,5
  32. add rsp,28h
  33. pop rdi
  34. pop rbp
  35. ret
  36. Asm_1 ENDP
  37.  
  38. Asm_3 PROC
  39. jmp Label1
  40. Label1:
  41. jmp Label2
  42. Label2:
  43. mov eax,-3
  44. ret
  45. Asm_3 ENDP
  46.  
  47. Asm_4 PROC
  48. call Label0
  49. jmp Exit;
  50. Label0:
  51. mov rcx,0;
  52. call Label1; //Call
  53. db 'H'
  54. db 0
  55. db 'e'
  56. db 0
  57. db 'l'
  58. db 0
  59. db 'l'
  60. db 0
  61. db 'o'
  62. db 0
  63. db 'S'
  64. db 0
  65. db 'u'
  66. db 0
  67. db 'b'
  68. db 0
  69. db '_'
  70. db 0
  71. db '4'
  72. db 0
  73. db 0
  74. db 0
  75. Label1:
  76. pop rdx
  77. call Label2;
  78. db 'H'
  79. db 0
  80. db 'e'
  81. db 0
  82. db 'l'
  83. db 0
  84. db 'l'
  85. db 0
  86. db 'o'
  87. db 0
  88. db 'S'
  89. db 0
  90. db 'u'
  91. db 0
  92. db 'b'
  93. db 0
  94. db '_'
  95. db 0
  96. db '4'
  97. db 0
  98. db 0
  99. db 0
  100. Label2:
  101. pop r8
  102. mov r9,0
  103. call MessageBoxW
  104. ret
  105. Exit:
  106. ret
  107. Asm_4 ENDP
  108.  
  109. Asm_10 PROC
  110. db 0CCh
  111. db 0CCh
  112. db 0CCh
  113. db 0CCh
  114. db 0CCh
  115.  
  116. mov edi,edi
  117. ret
  118. Asm_10 ENDP
  119. END
  1. 1 //热补丁测试
  2. typedef void(*LPFN_SUB_10)();
  3. void FakeSub_10(); //热补丁
  4. LPFN_SUB_10 __OriginalSub_10 = NULL;
  5. 2  
  6. PVOID v10 = Asm_1(Asm_10);
  7. 3
  8. 4 if (SeCreateHook((PVOID)((ULONG_PTR)v10 + 5), &FakeSub_10,
  9. 5 reinterpret_cast<LPVOID*>(&__OriginalSub_10)) != STATUS_SUCCESS)
  10. 6 {
  11. 7 return;
  12. 8 }
  13. 9 //对于热补丁函数调用
  14. 10 ((LPFN_SUB_10)(((ULONG_PTR)v10 + 5)))();
  15. 11 if (SeEnableHook(ALL_HOOKS) != STATUS_SUCCESS)
  16. 12 {
  17. 13 printf("SeEnableHook() Error\r\n");
  18. 14 return;
  19. 15 }
  20. 16 ((LPFN_SUB_10)(((ULONG_PTR)v10 + 5)))();

E9的测试只需要自写一个函数调用测试调用即可,如下面这样就行了然后在仿照上面自行测试即可

  1. 1 1 //E9指令,这样就行了
  2. 2 2
  3. 3 3 void Sub_2()
  4. 4 4 {
  5. 5 5 printf("Sub_2\n\r");
  6. 6 6 }

下面是所有的正确输出结果:

好了,x86下的MiniHook终于是测试完了,写了一遍后又是更懂了,如果有什么差错,望大家纠正

[原创]MinHook测试与分析(x64下 E9,EB,CALL指令测试,且逆推测试微软热补丁)的更多相关文章

  1. Jmeter测试结果分析(下)

    Jmeter测试结果分析(下) 前文再续,续接上一回.上一篇讲了如何利用Assertion将测试结果进行初步的筛选.那么,当我们拿到了测试结果之后,我们应该如何去看待它们呢?它们又是怎么来的呢? 一. ...

  2. MinHook测试与分析(x86下 E8,E9,EB,CALL指令测试,且逆推测试微软热补丁)

    依稀记得第一次接触Hook的概念是在周伟民先生的书中-><<多任务下的数据结构与算法>>,当时觉得Hook的本质就是拦截,就算到现在也是如此认为. 本篇文章是在x86下测 ...

  3. [转载] 关于Win7 x64下过TP保护的一些思路,内核层过保护,驱动过保护

    首先特别感谢梦老大,本人一直没搞懂异常处理机制,看了他的教程之后终于明白了.在他的教程里我学到了不少东西.第一次在论坛发帖,就说说Win7 x64位下怎么过TP保护.如果有讲错的地方,还望指出.说不定 ...

  4. 原创 C++应用程序在Windows下的编译、链接:第三部分 静态链接(二)

    3.5.2动态链接库的创建 3.5.2.1动态链接库的创建流程 动态链接库的创建流程如下图所示: 在系统设计阶段,主要的设计内容包括:类结构的设计以及功能类之间的关系,动态链接库的接口.在动态链接库中 ...

  5. LoadRunner测试结果分析03 转载至zhangzhe的新浪博客

    LoadRunner测试结果分析之我见 前面分析的Web Resource(网络资源)的测试情况,其主要关注的是服务器性能,而系统本身和环境都有可能存在问题,页面诊断(Web Page Diagnos ...

  6. LoadRunner测试结果分析01 转载至zhangzhe的新浪博客

    LoadRunner测试结果分析之我见 LoadRunner生成测试结果并不代表着这次测试结果的结束,相反,这次测试结果的重头戏才刚刚开始.如何对测试结果进行分析,关系着这次测试的成功与否.网上关于L ...

  7. Monkey测试3——Monkey测试结果分析

    Monkey测试结果分析 一. 初步分析方法: Monkey测试出现错误后,一般的差错步骤为以下几步: 1. 找到是monkey里面的哪个地方出错 2. 查看Monkey里面出错前的一些事件动作,并手 ...

  8. X64下MmIsAddressValid的逆向及内存寻址解析

    标 题: [原创]X64下MmIsAddressValid的逆向及内存寻址解析 作 者: 普通朋友 时 间: 2015-10-21,20:03:52 链 接: http://bbs.pediy.com ...

  9. 8051、ARM和DSP指令周期的测试与分析

    在实时嵌入式控制系统中,指令周期对系统的性能有至关重要的影响.介绍几种最常用的微控制器的工作机制,采用一段循环语句对这几种微控制器的指令周期进行测试,并进行分析比较.分析结论对系统控制器的选择有一定的 ...

随机推荐

  1. jumpserver V0.4.0 在CentOs7上的安装

    基于 CentOS 7 一步一步安装 Jumpserver 0.4.0 环境 系统: CentOS 7 IP: 192.168.226.128 一. 准备Python3和Python虚拟环境 1.1 ...

  2. 一语惊醒梦中人-《Before I Fall》

    I still remembered  I turned my attention to the title when I browsed in news by cellphone.I saw the ...

  3. java HashSet改用

    写的一个Student类如下: 上面是直接使用的HashSet集合,系统会把new Student()  当做地址不用来出来,所以结果如下: 然后我在Student类中重写了hashCode()和eq ...

  4. selenium,html高宽设置成了0,会影响元素可见性,怎么手动修改某个元素的高宽?

     问题:要js的话,需要用webelment,此时元素已经是不可见了   ((JavascriptExecutor) this.driver).executeScript("argument ...

  5. jdk不同版本对String拼接的优化分析

    1. 测试demo代码 测试循环中字符串拼接优化 public class ForTest { public static void main(String[] args) { String a = ...

  6. chrome开发工具指南(七)

    检查动画 使用 Chrome DevTools 动画检查器检查和修改动画. 通过打开动画检查器捕捉动画.检查器会自动检测动画并将它们分类为多个组. 通过慢速播放.重播或查看动画源代码来检查动画. 通过 ...

  7. Java基础学习 —— 线程

    线程: 多线程的好处:解决了在一个进程中同时执行多个任务代码的问题. 自定义线程的创建方式: 1.自定一个类继承thread类,重写thread的run方法 吧自定义线程的任务代码写在run方法内,创 ...

  8. 那些年,我们不懂的却又不得不提的 JAVA异常和异常处理!

    ---恢复内容开始--- 首先,我是个小小的菜鸟,最近突然突发奇想,想研究一下java的异常和异常的处理,稍有些理解,老鸟们莫要嘲笑... 既然要讲异常和异常的处理,我们就要先了解异常,那么,什么是异 ...

  9. SNS团队第五次站立会议(2017.04.26)

    一.当天站立式会议照片 本次会议主要内容:汇报工作进度,根据完成情况调整进度 二.每个人的工作 成员 今天已完成的工作 明天计划完成的工作 罗于婕 完善数据库文件 根据需求完善数据库文件 龚晓婷 编写 ...

  10. 201521123096《Java程序设计》第六周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 2. 书面作业 (1)clone方法 1.1 Object ...