"Practical Reverse Engineering" 中的勘误表?
Errata in "Practical Reverse Engineering"?
我刚刚开始阅读 Bruce Dang 等人撰写的 实用逆向工程 一书,对第一章末尾 "walk-through" 的一部分感到困惑.这是代码的相关部分:
65: ...
66: loc_10001d16:
67: mov eax, [ebp-118h]
68: mov ecx, [ebp-128h]
69: jmp short loc_10001d2a (line 73)
70: loc_10001d24:
71: mov eax, [ebp+0ch]
72: mov ecx, [ebp+0ch]
73: loc_10001d2a:
74: cmp eax, ecx
75: pop esi
76: jnz short loc_10001D38 (line 82)
77: xor eax, eax
78: pop edi
79: mov esp, ebp
80: pop ebp
81: retn 0ch
82: ...
以及作者的评论:
"After the loop exits, execution resumes at line 66. Lines 67–68 save the matching PROCESSENTRY32
’s th32ParentProcessID/th32ProcessID
in EAX/ECX
and
continue execution at 73. Notice that Line 66 is also a jump target in line 43.
Lines 70–74 read the fdwReason
parameter of DllMain
(EBP+C
) and check
whether it is 0 (DLL_PROCESS_DETACH
). If it is, the return value is set to 0 and
it returns; otherwise, it goes to line 82."
这不是我阅读代码时的解释方式;当然,任何跳转到 loc_10001d24
(第 70 行)都会导致函数以 return 值 0 无条件地 终止,而不仅仅是 ebp+0x0c
处的值] 是0? (我假设 pop
ing 进入 esi
不会影响 eflags
寄存器,并且第 76 行中的跳转以第 74 行中 cmp eax, ecx
的结果为条件?)这个也与代码中的早期部分一致,如果各种调用函数 return 的值指示失败,则跳转到 loc_10001d24
。
此外,我认为从第 66 行开始的部分的要点也是 return 值为 0 if PROCESSENTRY32
(之前定义的结构,从位置 ebp-0x130
开始在内存中)具有相等的 th32ParentProcessID
(ebp-0x118
在内存中)和 th32ProcessID
(ebp-0x128
在内存中)条目;这个对吗?作者的评论似乎没有表明这一点。
作为一个更笼统的问题,即使只是本书的第 1 章,似乎也有相当多的错别字;有谁知道哪里有收集书中勘误表的网页吗?
是的,ECX 和 EAX 都是从同一个内存位置加载的,所以除非有其他东西指向它并且正在异步更改它,否则 cmp x,x
/ jne
将永远不会-采取。与浮点数不同,任何可能的整数都等于其自身。
你是对的,pop
不会更改 EFLAGS,根据英特尔的手册:https://www.felixcloutier.com/x86/pop。
要检查内存位置是否为零,您可以将其加载到 test eax,eax
/ jnz
的 reg 中
或 cmp dword ptr [ebp + 0xc], 0
/ jne
.
(JNE和JNZ是同一条指令,不同的助记符让你表达等号或直接为零的语义,基于ZF根据值本身设置。)
Lines 70–74 read the fdwReason parameter of DllMain (EBP+C) and check whether it is 0 (DLL_PROCESS_DETACH)
这是假的。如果这本书充满了这样的东西,那听起来就不是一本好书。
cmp eax,ecx
仅在从加载 2 个不同值的路径到达时才有意义。 (并且不能为此使用 test
,x & y != 0
不会告诉你它们是否相等。)这似乎不太可能是真正的编译器输出。
这是完整列表。它是在野发现的恶意软件的一部分:
01: ; BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
02: _DllMain@12 proc near
03: 55 push ebp
04: 8B EC mov ebp, esp
05: 81 EC 30 01 00+ sub esp, 130h
06: 57 push edi
07: 0F 01 4D F8 sidt fword ptr [ebp-8]
08: 8B 45 FA mov eax, [ebp-6]
09: 3D 00 F4 03 80 cmp eax, 8003F400h
10: 76 10 jbe short loc_10001C88 (line 18)
11: 3D 00 74 04 80 cmp eax, 80047400h
12: 73 09 jnb short loc_10001C88 (line 18)
13: 33 C0 xor eax, eax
14: 5F pop edi
15: 8B E5 mov esp, ebp
16: 5D pop ebp
17: C2 0C 00 retn 0Ch
18: loc_10001C88:
19: 33 C0 xor eax, eax
20: B9 49 00 00 00 mov ecx, 49h
21: 8D BD D4 FE FF+ lea edi, [ebp-12Ch]
22: C7 85 D0 FE FF+ mov dword ptr [ebp-130h], 0
23: 50 push eax
24: 6A 02 push 2
25: F3 AB rep stosd
26: E8 2D 2F 00 00 call CreateToolhelp32Snapshot
27: 8B F8 mov edi, eax
28: 83 FF FF cmp edi, 0FFFFFFFFh
29: 75 09 jnz short loc_10001CB9 (line 35)
30: 33 C0 xor eax, eax
31: 5F pop edi
32: 8B E5 mov esp, ebp
33: 5D pop ebp
34: C2 0C 00 retn 0Ch
35: loc_10001CB9:
36: 8D 85 D0 FE FF+ lea eax, [ebp-130h]
37: 56 push esi
38: 50 push eax
39: 57 push edi
40: C7 85 D0 FE FF+ mov dword ptr [ebp-130h], 128h
41: E8 FF 2E 00 00 call Process32First
42: 85 C0 test eax, eax
43: 74 4F jz short loc_10001D24 (line 70)
44: 8B 35 C0 50 00+ mov esi, ds:_stricmp
45: 8D 8D F4 FE FF+ lea ecx, [ebp-10Ch]
46: 68 50 7C 00 10 push 10007C50h
47: 51 push ecx
48: FF D6 call esi
49: 83 C4 08 add esp, 8
50: 85 C0 test eax, eax
51: 74 26 jz short loc_10001D16 (line 66)
52: loc_10001CF0:
53: 8D 95 D0 FE FF+ lea edx, [ebp-130h]
54: 52 push edx
55: 57 push edi
56: E8 CD 2E 00 00 call Process32Next
57: 85 C0 test eax, eax
58: 74 23 jz short loc_10001D24 (line 70)
59: 8D 85 F4 FE FF+ lea eax, [ebp-10Ch]
60: 68 50 7C 00 10 push 10007C50h
61: 50 push eax
62: FF D6 call esi
63: 83 C4 08 add esp, 8
64: 85 C0 test eax, eax
65: 75 DA jnz short loc_10001CF0 (line 52)
66: loc_10001D16:
67: 8B 85 E8 FE FF+ mov eax, [ebp-118h]
68: 8B 8D D8 FE FF+ mov ecx, [ebp-128h]
69: EB 06 jmp short loc_10001D2A (line 73)
70: loc_10001D24:
71: 8B 45 0C mov eax, [ebp+0Ch]
72: 8B 4D 0C mov ecx, [ebp+0Ch]
73: loc_10001D2A:
74: 3B C1 cmp eax, ecx
75: 5E pop esi
76: 75 09 jnz short loc_10001D38 (line 82)
77: 33 C0 xor eax, eax
78: 5F pop edi
79: 8B E5 mov esp, ebp
80: 5D pop ebp
81: C2 0C 00 retn 0Ch
82: loc_10001D38:
83: 8B 45 0C mov eax, [ebp+0Ch]
84: 48 dec eax
85: 75 15 jnz short loc_10001D53 (line 93)
86: 6A 00 push 0
87: 6A 00 push 0
88: 6A 00 push 0
89: 68 D0 32 00 10 push 100032D0h
90: 6A 00 push 0
91: 6A 00 push 0
92: FF 15 20 50 00+ call ds:CreateThread
93: loc_10001D53:
94: B8 01 00 00 00 mov eax, 1
95: 5F pop edi
96: 8B E5 mov esp, ebp
97: 5D pop ebp
98: C2 0C 00 retn 0Ch
99: _DllMain@12 endp
所以第 70-74 行本身没有任何意义,但确实服务于最初的目的 - 如果 Process32First()/Process32Next()
returns FALSE
那么代码跳转到这里并最终退出0。
如果找到所需的进程,则 eax/ecx 分别设置为 ParentProcessID/ProcessID,以便函数继续。
无论如何,书中还有第 83-85 行:
...lpStartAddress 为 0x100032D0。该块可以反编译如下:
if (fdwReason == DLL_PROCESS_DETACH) { return FALSE; }
if (fdwReason == DLL_THREAD_ATTACH || fdwReason == DLL_THREAD_DETACH) { return TRUE; }
CreateThread(0, 0, (LPTHREAD_START_ROUTINE) 0x100032D0, 0, 0, 0);
return TRUE;
第 83-85 行实际检查 fdwReason
是否等于 DLL_PROCESS_ATTACH
(如果不等于则绕过对 CreateThread
的调用,这很有意义),并且没有特殊情况DLL_PROCESS_DETACH
.
我会说这本书确实缺乏适当的结构,书中有些东西是理所当然的,而另一些可能是平凡的东西却被强调了。还是很好的资源。
哦,谁说这很容易。
我刚刚开始阅读 Bruce Dang 等人撰写的 实用逆向工程 一书,对第一章末尾 "walk-through" 的一部分感到困惑.这是代码的相关部分:
65: ...
66: loc_10001d16:
67: mov eax, [ebp-118h]
68: mov ecx, [ebp-128h]
69: jmp short loc_10001d2a (line 73)
70: loc_10001d24:
71: mov eax, [ebp+0ch]
72: mov ecx, [ebp+0ch]
73: loc_10001d2a:
74: cmp eax, ecx
75: pop esi
76: jnz short loc_10001D38 (line 82)
77: xor eax, eax
78: pop edi
79: mov esp, ebp
80: pop ebp
81: retn 0ch
82: ...
以及作者的评论:
"After the loop exits, execution resumes at line 66. Lines 67–68 save the matching
PROCESSENTRY32
’sth32ParentProcessID/th32ProcessID
inEAX/ECX
and continue execution at 73. Notice that Line 66 is also a jump target in line 43. Lines 70–74 read thefdwReason
parameter ofDllMain
(EBP+C
) and check whether it is 0 (DLL_PROCESS_DETACH
). If it is, the return value is set to 0 and it returns; otherwise, it goes to line 82."
这不是我阅读代码时的解释方式;当然,任何跳转到 loc_10001d24
(第 70 行)都会导致函数以 return 值 0 无条件地 终止,而不仅仅是 ebp+0x0c
处的值] 是0? (我假设 pop
ing 进入 esi
不会影响 eflags
寄存器,并且第 76 行中的跳转以第 74 行中 cmp eax, ecx
的结果为条件?)这个也与代码中的早期部分一致,如果各种调用函数 return 的值指示失败,则跳转到 loc_10001d24
。
此外,我认为从第 66 行开始的部分的要点也是 return 值为 0 if PROCESSENTRY32
(之前定义的结构,从位置 ebp-0x130
开始在内存中)具有相等的 th32ParentProcessID
(ebp-0x118
在内存中)和 th32ProcessID
(ebp-0x128
在内存中)条目;这个对吗?作者的评论似乎没有表明这一点。
作为一个更笼统的问题,即使只是本书的第 1 章,似乎也有相当多的错别字;有谁知道哪里有收集书中勘误表的网页吗?
是的,ECX 和 EAX 都是从同一个内存位置加载的,所以除非有其他东西指向它并且正在异步更改它,否则 cmp x,x
/ jne
将永远不会-采取。与浮点数不同,任何可能的整数都等于其自身。
你是对的,pop
不会更改 EFLAGS,根据英特尔的手册:https://www.felixcloutier.com/x86/pop。
要检查内存位置是否为零,您可以将其加载到 test eax,eax
/ jnz
的 reg 中
或 cmp dword ptr [ebp + 0xc], 0
/ jne
.
(JNE和JNZ是同一条指令,不同的助记符让你表达等号或直接为零的语义,基于ZF根据值本身设置。)
Lines 70–74 read the fdwReason parameter of DllMain (EBP+C) and check whether it is 0 (DLL_PROCESS_DETACH)
这是假的。如果这本书充满了这样的东西,那听起来就不是一本好书。
cmp eax,ecx
仅在从加载 2 个不同值的路径到达时才有意义。 (并且不能为此使用 test
,x & y != 0
不会告诉你它们是否相等。)这似乎不太可能是真正的编译器输出。
这是完整列表。它是在野发现的恶意软件的一部分:
01: ; BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
02: _DllMain@12 proc near
03: 55 push ebp
04: 8B EC mov ebp, esp
05: 81 EC 30 01 00+ sub esp, 130h
06: 57 push edi
07: 0F 01 4D F8 sidt fword ptr [ebp-8]
08: 8B 45 FA mov eax, [ebp-6]
09: 3D 00 F4 03 80 cmp eax, 8003F400h
10: 76 10 jbe short loc_10001C88 (line 18)
11: 3D 00 74 04 80 cmp eax, 80047400h
12: 73 09 jnb short loc_10001C88 (line 18)
13: 33 C0 xor eax, eax
14: 5F pop edi
15: 8B E5 mov esp, ebp
16: 5D pop ebp
17: C2 0C 00 retn 0Ch
18: loc_10001C88:
19: 33 C0 xor eax, eax
20: B9 49 00 00 00 mov ecx, 49h
21: 8D BD D4 FE FF+ lea edi, [ebp-12Ch]
22: C7 85 D0 FE FF+ mov dword ptr [ebp-130h], 0
23: 50 push eax
24: 6A 02 push 2
25: F3 AB rep stosd
26: E8 2D 2F 00 00 call CreateToolhelp32Snapshot
27: 8B F8 mov edi, eax
28: 83 FF FF cmp edi, 0FFFFFFFFh
29: 75 09 jnz short loc_10001CB9 (line 35)
30: 33 C0 xor eax, eax
31: 5F pop edi
32: 8B E5 mov esp, ebp
33: 5D pop ebp
34: C2 0C 00 retn 0Ch
35: loc_10001CB9:
36: 8D 85 D0 FE FF+ lea eax, [ebp-130h]
37: 56 push esi
38: 50 push eax
39: 57 push edi
40: C7 85 D0 FE FF+ mov dword ptr [ebp-130h], 128h
41: E8 FF 2E 00 00 call Process32First
42: 85 C0 test eax, eax
43: 74 4F jz short loc_10001D24 (line 70)
44: 8B 35 C0 50 00+ mov esi, ds:_stricmp
45: 8D 8D F4 FE FF+ lea ecx, [ebp-10Ch]
46: 68 50 7C 00 10 push 10007C50h
47: 51 push ecx
48: FF D6 call esi
49: 83 C4 08 add esp, 8
50: 85 C0 test eax, eax
51: 74 26 jz short loc_10001D16 (line 66)
52: loc_10001CF0:
53: 8D 95 D0 FE FF+ lea edx, [ebp-130h]
54: 52 push edx
55: 57 push edi
56: E8 CD 2E 00 00 call Process32Next
57: 85 C0 test eax, eax
58: 74 23 jz short loc_10001D24 (line 70)
59: 8D 85 F4 FE FF+ lea eax, [ebp-10Ch]
60: 68 50 7C 00 10 push 10007C50h
61: 50 push eax
62: FF D6 call esi
63: 83 C4 08 add esp, 8
64: 85 C0 test eax, eax
65: 75 DA jnz short loc_10001CF0 (line 52)
66: loc_10001D16:
67: 8B 85 E8 FE FF+ mov eax, [ebp-118h]
68: 8B 8D D8 FE FF+ mov ecx, [ebp-128h]
69: EB 06 jmp short loc_10001D2A (line 73)
70: loc_10001D24:
71: 8B 45 0C mov eax, [ebp+0Ch]
72: 8B 4D 0C mov ecx, [ebp+0Ch]
73: loc_10001D2A:
74: 3B C1 cmp eax, ecx
75: 5E pop esi
76: 75 09 jnz short loc_10001D38 (line 82)
77: 33 C0 xor eax, eax
78: 5F pop edi
79: 8B E5 mov esp, ebp
80: 5D pop ebp
81: C2 0C 00 retn 0Ch
82: loc_10001D38:
83: 8B 45 0C mov eax, [ebp+0Ch]
84: 48 dec eax
85: 75 15 jnz short loc_10001D53 (line 93)
86: 6A 00 push 0
87: 6A 00 push 0
88: 6A 00 push 0
89: 68 D0 32 00 10 push 100032D0h
90: 6A 00 push 0
91: 6A 00 push 0
92: FF 15 20 50 00+ call ds:CreateThread
93: loc_10001D53:
94: B8 01 00 00 00 mov eax, 1
95: 5F pop edi
96: 8B E5 mov esp, ebp
97: 5D pop ebp
98: C2 0C 00 retn 0Ch
99: _DllMain@12 endp
所以第 70-74 行本身没有任何意义,但确实服务于最初的目的 - 如果 Process32First()/Process32Next()
returns FALSE
那么代码跳转到这里并最终退出0。
如果找到所需的进程,则 eax/ecx 分别设置为 ParentProcessID/ProcessID,以便函数继续。
无论如何,书中还有第 83-85 行:
...lpStartAddress 为 0x100032D0。该块可以反编译如下:
if (fdwReason == DLL_PROCESS_DETACH) { return FALSE; }
if (fdwReason == DLL_THREAD_ATTACH || fdwReason == DLL_THREAD_DETACH) { return TRUE; }
CreateThread(0, 0, (LPTHREAD_START_ROUTINE) 0x100032D0, 0, 0, 0);
return TRUE;
第 83-85 行实际检查 fdwReason
是否等于 DLL_PROCESS_ATTACH
(如果不等于则绕过对 CreateThread
的调用,这很有意义),并且没有特殊情况DLL_PROCESS_DETACH
.
我会说这本书确实缺乏适当的结构,书中有些东西是理所当然的,而另一些可能是平凡的东西却被强调了。还是很好的资源。
哦,谁说这很容易。