首页
社区
课程
招聘
6
[旧帖] [原创]跟我编写补丁小工具-第一课 0.00雪花
发表于: 2012-5-26 21:14 5875

[旧帖] [原创]跟我编写补丁小工具-第一课 0.00雪花

2012-5-26 21:14
5875

新人第一次发文章,而且还是教程类....像我这样文笔不好的...写得不好各位莫怪那


在我编写这个程序的时候是分3部分编写,所以我觉得教程还是分3次的好,今天这篇是第一篇,在进入第一部分之前我先把整个程序大概个所以然出来


这就是我们最后要完成的程序,整个工程主要由C语言和少部分汇编完成


好了,现在给大家一个对本程序的整体印象


工具由3部分组合而成,一部分就是上面这幅图,也就是工具主要部分,比如添加补丁地址,设置补丁方式等界面上的东西,除去这些界面上的东西和一些数据的获得,也就剩个生成补丁了,在最后一课大家会看到这个函数的实现,顺便提下..定制补丁那2个选项大家可以暂时忽略掉,我是觉得有点鸡肋了


工具的剩下2部分一个是Loader补丁一个patch补丁(内存补丁和文件补丁),单说补丁的话就算一部分了


如果写过补丁程序的话应该会很清楚,只是补丁都是针对某一个程序,每次都写过的话就麻烦了,所以我们来编写工具靠工具“生产”补丁,而用来生产补丁的补丁就成了“补丁模版”,有个模版稍加改变,就能产生需要的补丁程序,工具“生产”补丁主要就靠“模版”啦



所以要编写整个工具我们就得先从“模版”开始,模版固定了工具也好写了


大概也所以个然了,现在就开始工具的第一个也是最简单的“模版”------Loader



编写这个Loader"模版",我们首先就要考虑怎么个模版法,补丁方法是其次


一个补丁,首先会有针对的文件,然后是补丁的地址和地址的数据等变量,我们的目标就是“模版”到补丁工具能够轻松的修改他!


于是,我们只要为这些数据专门安排一个区段,那么工具不就能迅速的定位了吗?到这相信各位大概清楚了补丁“模版”的实现,下面就是部分代码中:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//需要工具修正的节为.sdata节
#pragma data_seg(".sdata")
DWORD   wTypeOfPatch = 0;              //我们支持多种补丁方法,所以加个变量指示补丁类型
DWORD   dwPatchNum = 2;           //补丁数量
//偏移8(指在.sdata节中的偏移)
TCHAR   szFileName[MAX_PATH] = { 0 };
//偏移528
DWORD   dwPatchAddress[16] = { 0}  //////////////////////利用调试寄存器打丁///////////////////////////////////////////////////////
/////////////打此类补丁应在补丁地址第一个地址填上希望中断的地址以确保所有地址数据已解码,以保证补丁正确性///////////
//偏移592
BYTE    byOldData[16] = { 0};       //补丁处旧数据和新数据
//偏移608
BYTE    byNewData[16] = { 0};
#pragma data_seg()
#pragma comment(linker, "/SECTION:.sdata,ERW")//添加写属性提供修正可能

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

上传的附件:
收藏
免费 6
支持
分享
赞赏记录
参与人
雪币
留言
时间
伟叔叔
为你点赞~
2024-5-31 04:52
心游尘世外
为你点赞~
2024-5-31 01:37
QinBeast
为你点赞~
2024-5-31 01:27
飘零丶
为你点赞~
2024-3-28 02:25
shinratensei
为你点赞~
2024-1-29 05:50
PLEBFE
为你点赞~
2023-3-7 00:43
最新回复 (26)
雪    币: 45
活跃值: (55)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
牛B,膜拜!
2012-5-27 10:29
0
雪    币: 49
活跃值: (40)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
上一课我们做出了最简单的一个Loader,这次我们就要做一个最复杂的patch了,不单代码比上次多多了,还要混合汇编~~~
“模版”的数据在这咱就不多说了,数据依然是那些个数据,节区依然是那个.sdata,要看的到上一课看哈
在这里我们要实现的patch,也就是文件补丁----给PE文件附加上执行代码
在这个“模版”patch中,我实现了4种补丁类型,分别是:
1
2
3
4
5
6
#define ADD_LAST_SECTION  1   //添加代码到最后一个区段
#define ADD_NEW_SECTION  2  //添加代码到一个新建的区段
#define ADD_TO_HEADER       3  //添加代码到PE头部
//除了以上3种外还有一种是寻找已存在区段空闲处并插入代码,这种插入PE头的修改更简单,只是比较容易失败,这里就不实现了
//这里再加一种字节补丁,针对一些简单的程序
#define BYTE_PATCH       4
由于PE文件对齐后存在的间隙给我们的补丁代码带来了生存空间,我们的目标就是想法设法让我们的补丁代码进入目标程序,让目标程序一运行就自动给我们补丁,我们暂且不管补丁代码如何补丁,现在的目标就是把代码加入PE文件并让他很好的运行起来,所以一开始并不需要补丁的实现,我们可以把补丁编写为弹出一个对话框来作为测试
现在假设存在以下2个变量
Appendcode_Start;                //附加代码段起始处
Appendcode_End;            //附加代码段结束处
于是我们的补丁代码起始地址为&Appencode_Start,大小为(&Appencode_End-&Appencode_Start)
有了这2个变量,我们就可以专注于如何添加代码到PE中了
首先我们实现第一种类型的代码添加---添加到最后一个区段
对于了解PE的朋友们都会知道,PE中的数据是分段存储的,而在节表中记录了这些区段的信息,而最后一个区段比较特殊,如果我们构造的好的话在能够加载的情况下应该可以无限制的添加代码,别的先不说,先来个添加代码后的整体结构大家应该就会比较清楚了(下面的东西修改了几次都对不齐,不知道怎么整,就这样将就吧= = )
//_________________
//|                                |   
//|            PE头         |         第一部分
//|                                |
//|________________|
//|_______节表______|
//|                                |
//|_______对齐______|
//|                                |
//|                                |
//.                                .
//.        各区段          .         第二部分
//.                                .
//|                                |
//|________________|
//.___对齐后间隙______.
//!                                !
//!____附加代码______!        第三部分
//!____最终对齐______!

被补丁后的整个PE文件结构如上,其中第一第二部分是整个文件原始情况
加上第三部分后就是我们的目标文件了至于具体的如何加上去,用代码说话吧!
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
HANDLE  hFile ;//文件句柄
HANDLE  hMap; //映射文件句柄
 
//////////////////////////////添加代码到最后一个区块/////////////////////////////////////
 
BOOL AddToLastSection(PBYTE lpMemory, DWORD dwFileSize)
{
    PIMAGE_NT_HEADERS       lpNtHeaders;
    PIMAGE_SECTION_HEADER   lpSectionHeader;
    PIMAGE_SECTION_HEADER   lpLastSectionHeader;
    DWORD                   dwNewFileSize;      //最终文件大小
    DWORD                   dwFileAlignSize;    //原文件对齐后大小
    DWORD                   dwLastSectionAlignSize; //最后区段内存对齐后大小
    DWORD                   dwPatchSize;        //补丁大小
    DWORD                   dwFileAlign;        //文件对齐粒度
    DWORD                   dwSectionAlign;     //内存对齐粒度
    //DWORD                 dwLastSectionSize;
    //DWORD                 dwPatchStart;   //指定补丁要复制到的文件偏移起始
    PBYTE                   lpNewFile;      //最终文件缓存
    DWORD                   dwSectionNum;
 
    lpNtHeaders     = (PIMAGE_NT_HEADERS)( lpMemory + ((PIMAGE_DOS_HEADER)lpMemory)->e_lfanew );
    lpSectionHeader = (PIMAGE_SECTION_HEADER)(lpNtHeaders + 1);
     
    dwSectionNum = lpNtHeaders->FileHeader.NumberOfSections ;
    lpLastSectionHeader = lpSectionHeader + dwSectionNum - 1;
     
    dwFileAlign     = lpNtHeaders->OptionalHeader.FileAlignment;
    dwSectionAlign  = lpNtHeaders->OptionalHeader.SectionAlignment;
    dwFileAlignSize = Align(dwFileSize, dwFileAlign);   //求原文件对齐大小
     
    dwPatchSize     = ((DWORD)&Appendcode_End ) - ( (DWORD)&Appendcode_Start ); //获得补丁代码大小
 
    dwNewFileSize   = Align(dwFileAlignSize + dwPatchSize, dwFileAlign); //获得最终文件对齐后大小
    dwLastSectionAlignSize  = Align(lpLastSectionHeader->Misc.VirtualSize + dwPatchSize, dwSectionAlign); //获得内存中最后区段大小
 
    lpNewFile       = (PBYTE)VirtualAlloc (NULL, dwNewFileSize, MEM_COMMIT, PAGE_READWRITE);
    if ( !lpNewFile )  //分配内存失败
        return FALSE;
 
    //复制原文件数据
    memset(lpNewFile, 0, dwNewFileSize);
    memcpy(lpNewFile, lpMemory, dwFileSize);
    //复制完毕,关闭映射文件和句柄 (不关闭后面就无法创建新文件,本来想在AddCode关闭,结果试了抛异常什么的还是没用,只能hMap设置成全局变量然后在这关了,实在不雅观啊‘_’)
    UnmapViewOfFile(lpMemory);
    CloseHandle(hMap);
    CloseHandle(hFile);
 
    //复制补丁代码前先转储补丁数据
    PBYTE   pBuffer     = (PBYTE)(&Patch_Data);   //指向附加代码补丁数据处
    //(*(DWORD*)pBuffer)    = dwPatchNum;
    memcpy(pBuffer + 4, dwPatchAddress, 16*sizeof(DWORD) );
    pBuffer += 4 + 16*sizeof(DWORD);
    memcpy(pBuffer, byOldData, 16);
    memcpy(pBuffer+16, byNewData, 16);
 
    //复制补丁代码
    memcpy(lpNewFile + dwFileAlignSize, &Appendcode_Start, dwPatchSize);
 
    //修正PE头数据
    PIMAGE_NT_HEADERS       lpNewNtHeaders;
    PIMAGE_SECTION_HEADER   lpNewSectionHeader;
    PIMAGE_SECTION_HEADER   lpNewLastSection;
    DWORD*                  lpNewEntry;         //指向新入口处
    DWORD                   OldEntry;
     
    lpNewNtHeaders      = (PIMAGE_NT_HEADERS)( lpNewFile + ((PIMAGE_DOS_HEADER)lpNewFile)->e_lfanew );
    lpNewSectionHeader  = (PIMAGE_SECTION_HEADER)(lpNewNtHeaders + 1);
    lpNewLastSection    = lpNewSectionHeader + dwSectionNum - 1;
 
     
    //给最后区段添加读写执行属性
    lpNewLastSection->Characteristics  |= 0xC0000020;
     
    //修正最后一个区段的偏移量
    lpNewLastSection->SizeOfRawData      = dwNewFileSize - lpNewLastSection->PointerToRawData; 
    lpNewLastSection->Misc.VirtualSize   = Align( GetValidSize(lpNewFile, lpNewLastSection), dwSectionAlign);//Align(lpNewLastSection->Misc.VirtualSize + dwPatchSize, dwSectionAlign) ;
     
    //修正镜像大小
    lpNewNtHeaders->OptionalHeader.SizeOfImage   = Align(lpNewLastSection->VirtualAddress + lpNewLastSection->Misc.VirtualSize, dwSectionAlign);
 
    //修正入口地址
    OldEntry    = lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint;
    lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint   = OffsetToRVA( (IMAGE_DOS_HEADER *)lpNewFile, dwFileAlignSize) ;
     
    //修正补丁代码跳回OEP的参数
    lpNewEntry          = (DWORD*)(lpNewFile + dwFileAlignSize + dwPatchSize - 5);
    *lpNewEntry         = OldEntry - (lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint + dwPatchSize - 1);
 
    //补丁完毕,写回文件
    HANDLE  hNewFile;
    DWORD   dwRead;
    if (INVALID_HANDLE_VALUE == ( hNewFile = CreateFile (szFileName, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
        return FALSE;
 
    WriteFile(hNewFile, lpNewFile, dwNewFileSize, &dwRead, NULL);
    CloseHandle(hNewFile);
    return TRUE;
}
//这里再附上调用添加代码函数的主要函数,保证完整性
BOOL AddCode( )
{
    DWORD   dwFileSize; //文件大小
    PBYTE   lpMemory;   //内存映射指针
     
    if (INVALID_HANDLE_VALUE != ( hFile = CreateFile (szFileName, GENERIC_READ , FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
    {
        dwFileSize  = GetFileSize (hFile, NULL);
        if (dwFileSize)
        {
            hMap    = CreateFileMapping (hFile, NULL, PAGE_READONLY, 0, 0, NULL);
            if (hMap)
            {
                lpMemory    = (BYTE *)MapViewOfFile (hMap, FILE_MAP_READ, 0, 0, 0);
                if (lpMemory)
                {
                    //使用指定方法打补丁
                    switch(dwTypeOfPatch)
                    {
                        case ADD_LAST_SECTION:
                            if (!AddToLastSection(lpMemory, dwFileSize) )
                                return FALSE;
                            break;
 
                        case ADD_NEW_SECTION:
                            if (!AddToNewSection(lpMemory, dwFileSize) )
                                return FALSE;
                            break;
 
                        case ADD_TO_HEADER:
                            if (!AddToHeaderSection(lpMemory, dwFileSize) )
                                return FALSE;
                            break;
 
                        case BYTE_PATCH:
                            if (!BytePatch(lpMemory, dwFileSize))
                                return FALSE;
                            break;
 
                    }
                     
                    return TRUE;
                }
                else
                    MessageBox (GetActiveWindow() , TEXT("目标文件打开失败"), NULL, MB_OK);
            }
            else
                MessageBox ( GetActiveWindow() , TEXT("目标文件打开失败"), NULL, MB_OK);
        }
    }
    else
        MessageBox (GetActiveWindow() , TEXT("目标文件打开失败"), NULL, MB_OK);
    return FALSE;
}
上面的注释应该写的比较清楚了,这样我们就完成了一个添加代码的方法,第二种方法是给PE增加一个新区段,这种方法需要PE头至少有容纳一个节表(20个字节)的空闲容量,不过由于PE头也可以增加大小(最大0x1000字节),所以这种方法基本也能成功,只是增大了PE头的话就需要调整所有节表的文件偏移,要麻烦一些具体实现就不贴出了,可以去看附件的代码

到这里,整个patch的添加代码部分就实现了,下面就是具体编写实现补丁的代码,这个代码用汇编编写,涉及到自定位,动态加载等技术,在贴出代码前有些东西要提下,由于用mov eax,[esp]的方法获得kernel32.dll内部地址然后向前搜索kernel32.dll基地址的方法虽然稳定,但有局限性,比如用这种方法只能给目标文件实现一次补丁,如果继续添加补丁之前的补丁的mov eax,[esp]就会无效,等于说这种方法只能给目标文件添加一次补丁代码,而且代码量相对来说比较大。
所有我这里用的是从系统PEB结构获得基地址,仅需要以下几行代码即可
1
2
3
4
5
6
assume fs:nothing
mov eax,fs:[30h]
mov eax,[eax+0ch]
mov esi,[eax+1ch]
lodsd
mov eax,[eax+8]
只需要上面几行代码就可以获得kernel32基地址,具体涉及到TEB,PEB,PEB_LDR_DATA结构,这种方法在XP上能很好的工作。但是事实上用这种方法在WIN7下获得的是kernelbase.dll的基址而不是kernel32。还有一种,通过SEH框架最后一个异常处理地址,这个根据WINPE权威指南上说是kernel32.dll中,所以可以从此入手。而经测试后在WIN7中这个地址也换了,已经是位于ntdll.dll中的地址。
嗯,看似无路可走,其实PEB的方法还是可行的,在WIN7下查询kernelbase.dll导出表,发现其中虽然没有了LoadLibrary函数,但是有LoadLibraryEx函数,GetProcAddress函数也有,有这2个函数就够了,我们用PEB的动态加载就能够实行,注意是LoadLibraryEx函数哦!比LoadLibrary多了2个参数,调用时需要注意!
还有一个问题就是如何将前面的C代码和汇编代码结合在一起编译,首先就要导出某个变量的符号供其他代码使用,这个问题存在的原因主要是链接时外部符号无法识别,究其根本就是调用约定和名称修饰,具体可以参考C/C++名称修饰,加密解密的第16章也有讲到这个混合编译时的设置等,可以自行查找
讲了这么多,下面就贴出代码落~~
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
include     c:\masm32\include\windows.inc
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 导出变量供补丁工具使用
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;导出的变量
PUBLIC  Appendcode_Start    ;附加代码起始处   
PUBLIC  Appendcode_End      ;附加代码结束处
PUBLIC  Patch_Data      ;补丁数据处
 
        .code
Appendcode_Start LABEL      DWORD
 
    jmp _NewEntry
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;重要的函数名,为兼容WIN7 kernelbase.dll,使用LoadLibraryExA函数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
szLoadLibraryExA db 'LoadLibraryExA',0 
szGetProcAddress db 'GetProcAddress',0
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;SEH错误Handler,用于在错误中回复并跳转到安全位置
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_SEHHandler proc _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatchertext
    pushad
    mov esi,_lpExceptionRecord
    assume  esi:ptr EXCEPTIONRECORD
    mov edi,_lpContext
    assume  edi:ptr CONTEXT
    mov eax,_lpSEH
    push    [eax+0ch]
    pop [edi].regEbp
    push    [eax+08]
    pop [edi].regEip
    push    eax
    pop [edi].regEsp
    assume  edi:nothing,esi:nothing
    popad
    mov eax,ExceptionContinueExecution
    ret
_SEHHandler endp
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;用PEB获取基址的方法,WIN7中获得的实际是kernelbase.dll的基地址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_GetKernel32Base proc
    local   @dwRet
 
    pushad
     
    assume fs:nothing
    mov eax,fs:[30h]    ;获取PEB所在地址
    mov eax,[eax+0ch]   ;获取PEB_LDR_DATA 结构指针
    mov esi,[eax+1ch]   ;获取InInitializationOrderModuleList 链表头
                ;第一个LDR_MODULE节点InInitializationOrderModuleList成员的指针
    lodsd           ;获取双向链表当前节点后继的指针
    mov eax,[eax+8]     ;获取kernel32.dll的基地址(WIN7中是kernelbase.dll基址)
    mov @dwRet,eax
    popad
     
    mov eax,@dwRet
    ret
_GetKernel32Base endp
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;查找导出表获取指定API地址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_GetApi proc    _hModule,_lpszApi
    local   @dwReturn,@dwSize
    pushad
     
    call    @F
    @@:
    pop ebx
    sub ebx,@B
     
    assume  fs:nothing
    push    ebp
    push    [ebx+offset error]
    push    [ebx+offset _SEHHandler]
    push    fs:[0]
    mov fs:[0],esp
     
    mov edi,_lpszApi
    mov ecx,-1
    xor eax,eax
    cld
    repnz   scasb
    sub edi,_lpszApi
    mov @dwSize,edi
 
    mov esi,_hModule
    add esi,[esi+3ch]
    assume  esi:ptr IMAGE_NT_HEADERS
    mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress
    add esi,_hModule
    assume  esi:ptr IMAGE_EXPORT_DIRECTORY
 
    mov ebx,[esi].AddressOfNames
    add ebx,_hModule
    xor edx,edx
    .while  edx <    [esi].NumberOfNames
        push    esi
        mov edi,[ebx]
        add edi,_hModule
        mov esi,_lpszApi
        mov ecx,@dwSize
        cld
        repz    cmpsb      ;搜索指定API的字符串
        .if !ecx
            pop esi
            jmp @F  ;成功
        .endif
        next:
        pop esi
        inc edx
        add ebx,4
    .endw
    jmp error
    @@:
    sub ebx,[esi].AddressOfNames        
    sub ebx,_hModule     ;获得偏移
    shr ebx,1                   ;由于索引数组是WORD数组,所以右移一位
    add ebx,[esi].AddressOfNameOrdinals
    add ebx,_hModule
    movzx   eax,word ptr [ebx]
    shl eax,2                    ;将取得的索引左移2位获得字节偏移
    add eax,[esi].AddressOfFunctions    
    add eax,_hModule
 
    mov eax,[eax]       ;取得目标API地址
    add eax,_hModule
    mov @dwReturn,eax
    error:
    pop fs:[0]
    add esp,0ch
    assume  esi:nothing
    popad
    mov eax,@dwReturn
    ret
_GetApi endp
 
;补丁所需要的函数和全局变量
szCreateThread      db  'CreateThread',0
szGetTickCount      db  'GetTickCount',0
szVirtualProtect    db  'VirtualProtect',0
lpGetTickCount      dd  0
StartCount      dd  0
 
;以下变量需要补丁程序修正
Patch_Data  LABEL       DWORD
;dwTypeOfPatch  dd  0               ;指示补丁类型
dwPatchNum  dd  0               ;补丁数量  
dwPatchAddress  dd  16 dup(0)           ;补丁地址
byOldData   db  16 dup(0)           ;补丁处旧数据和新数据
byNewData   db  16 dup(0)          
 
;这个线程是实现补丁的部分,循环检测补丁地址的数据并补丁,还加入了一个5分钟的超时检测
_Thread proc    _lpVirtualProtect
    local   @lpGetTickCount,@temp,@StartCount,@num
 
    pushad
    call    @F
    @@:
    pop ebx
    sub ebx,@B
    mov edx,dword ptr [ebx+offset lpGetTickCount]
    mov @lpGetTickCount,edx
    mov edx,dword ptr [ebx+offset StartCount]
    mov @StartCount,edx
 
    mov ecx,dword ptr [ebx+offset dwPatchNum]
    mov @num,ecx
    .while  TRUE
        call    @lpGetTickCount
        sub eax,@StartCount
        cmp eax,493e0h      ;大于五分钟则超时退出线程
        jg  _exit
        ;开始检测补丁地址
        lea esi,dword ptr [ebx+offset dwPatchAddress]  ;指向补丁地址
        lea edi,dword ptr [ebx+offset byOldData]       ;补丁处旧数据
        lea edx,dword ptr [ebx+offset byNewData]       ;补丁处新数据
        ;检测所有补丁处字节
        mov ecx,dword ptr [ebx+offset dwPatchNum]
        _peek:
            push    ecx
            mov ecx,dword ptr [esi]
            xor eax,eax
            mov al,byte  ptr [ecx]      ;取补丁处数据
            cmp al,byte  ptr [edi]      ;补丁处是否解码
            jne _mismatch
             
            ;更改页面为读写执行,以确保补丁地址处拥有读写执行权限
            pushad
            lea     eax,@temp
            push    eax
            push    40h
            push    100h
            push    ecx
            call    _lpVirtualProtect
            popad      
            mov al,byte ptr [edx]   ;进行补丁
            mov byte ptr [ecx],al
            dec @num
            _mismatch: 
            inc edi
            inc edx
            add esi,4
            pop ecx
            cmp @num,0
            je  _exit
        loop    _peek
    .endw
    _exit:
    popad
    ret
_Thread endp
 
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;补丁功能部分
;_dwKernelBase:     kernel32.dll基址
;_lpGetProcAddress: GetProcAddress地址
;_lpLoadLibraryExA  LoadLibraryExA地址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;这里的作用是获得一些必须的函数,然后创建补丁线程
_Patch  proc    _dwKernelBase,_lpGetProcAddress,_lpLoadLibraryExA
        local   @lpVirtualProtect      
        local   @temp
 
        pushad
        lea edx,dword ptr [ebx+offset szVirtualProtect]
        push    edx
        push    _dwKernelBase
        call    _lpGetProcAddress
        cmp eax,0
        je  _exit
        mov @lpVirtualProtect,eax
        lea edx,@temp
        push    edx
        push    40h
        push    1000h
        lea edx,dword ptr [ebx+offset lpGetTickCount]
        push    edx
        call    @lpVirtualProtect   ;确保这个附加代码处全局变量位置可写
        lea edx,dword ptr [ebx+offset szGetTickCount]
        push    edx
        push    _dwKernelBase
        call    _lpGetProcAddress
        .if eax
            mov dword ptr [ebx+offset lpGetTickCount],eax
            call    eax
            mov dword ptr [ebx+offset StartCount],eax
            lea edx,dword ptr [ebx+offset szCreateThread]
            push    edx
            push    _dwKernelBase
            call    _lpGetProcAddress
            .if eax
                lea     edx,@temp
                push    edx
                push    0
                push    @lpVirtualProtect               ;线程参数为VirtualProtect函数的地址
                lea edx,dword ptr [ebx+offset _Thread]
                push    edx
                push    0
                push    0
                call    eax ;创建监测线程进行补丁
            .endif
        .endif
        _exit:
        popad
        ret
_Patch  endp
;从导入表获得GetProcAddress函数和LoadLibraryExA函数地址
_start  proc
    local   @dwKernel32Base
    local   @lpGetProcAddress,@lpLoadLibraryExA
     
    pushad
 
    call    _GetKernel32Base
    .if eax
        mov @dwKernel32Base,eax
        lea edx,dword ptr [ebx+offset szGetProcAddress]
        push    edx
        push    eax
        call    _GetApi
        mov @lpGetProcAddress,eax
    .endif
    .if @lpGetProcAddress
        lea edx,dword ptr [ebx+offset szLoadLibraryExA]
        push    edx
        push    @dwKernel32Base
        call    @lpGetProcAddress
        .if eax
            mov @lpLoadLibraryExA,eax
            push    eax
            push    @lpGetProcAddress
            push    @dwKernel32Base
            call    _Patch
        .endif
    .endif
 
    popad
    xor eax,eax
    ret
_start  endp
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;PE文件新入口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_NewEntry:
    call    @F
    @@:
    pop ebx
    sub ebx,@B
    call    _start
    ;ret
    jmpToStart db 0E9h,0F0h,0FFh,0ffh,0ffh  ;需要补丁程序修正,放在这个位置只要在&Appencode-5处赋值就可以修正了,比较方便
    ret
Appendcode_End LABEL        DWORD
end
经过一系列代码运行测试后最终才使用上面的补丁代码,然后再在C代码中对汇编代码中需要修正的数据进行修正,测试运行,其实由于是文件补丁,我们还得考虑重复补丁的情况~
所以就在patch中加了个CRC32验证,验证失败就认为可能损坏或已经补丁,详细的可以下载附件。做完这些工作后,我们的patch"模版"就完工了,虽然也不是最终版本,最终的修正都放到最后一篇讲,因为我也是在最后工具写完测试才发现的问题!
第二篇出了,第一篇都没什么人反应啊(沮丧中...),第三篇尽量早点发吧
下面传上patch的代码,整个工程的在第一篇附件中有
Patch.rar
上传的附件:
2012-5-27 11:28
0
雪    币: 49
活跃值: (40)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
经过前面2课,我们已经把需要的补丁“模版”完成,到这最后一课我们就要完成最后一步----------编写一个补丁工具了
在编写之前先要说一点需要的东西,可能有些人不知道,因为代码中用到了<windowsx.h>这个头文件
这个头文件主要就是一个消息分流器,里面有很多各种宏定义,比如消息分流,API宏,控件宏等,主要就是为了简化WIN32程序的开发,例如下面这个主窗口过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
BOOL CALLBACK DialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        HANDLE_MSG(hDlg, WM_INITDIALOG, Main_OnInitDialog);
        HANDLE_MSG(hDlg, WM_COMMAND , Main_OnCommand    );
 
        case WM_CLOSE:
            EndDialog (hDlg, 0);
            return true;
    }
    return FALSE;
}
中的HANDLE_MSG的作用就是将某个消息分流到某个函数中,同时参数也定义好了,省去了一大堆的case和不同消息的参数不同意义带来的麻烦,至于一些控件宏更是一目了然看名字就知道是干嘛的这就不说了,然后我还定义了几个列表视图的控件宏方便使用,在"ApiMacro.h"头文件中可以找到,然后还用了超类化定义出几个16进制控件,具体方法是根据老罗WIN32汇编程序设计来的,还有个RvaToOffset.h文件,其中定义了一些RVA和文件偏移之间互相转换的函数以及几个小函数,最后还加上了一个指示WIN7界面风格的链接参数,这可是我找了很久才找到的,以前不知道的时候那界面真是难看死了,下面这个代码至少在VS2008中是有效的:
1
2
//添加WIN7风格界面
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0'\processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
加上这个界面就漂亮多了。界面上的东西就不多说了,拉拉控件百度下就OK,下面贴上完成的界面:

界面做好了,下面就来说说如何创建补丁,我们在前面不是弄出了2个补丁"模版"吗?在这里我们就把补丁模版的exe文件作为工具的自定义资源加入,这样工具和“模版”就成为一个整体了,当我们需要创建补丁的时候只需要用FindResource,LoadResource,LockResource这几个API取得资源,然后写入文件,这个补丁"模版"就再次作为EXE出现了,然后我们只需要根据补丁工具的数据对"模版"数据进行修正,一个补丁就这样制造出来了,这里特别为补丁的生成写了一个函数,就贴在下面了:
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//创建补丁文件,补丁模版以资源的形式存储在程序中
//参数:szPatchName:创建的补丁文件名     szFileName:目标文件名     lpPatchAddress:补丁地址数组 
//     lpNewByte:补丁原始数据数组       lpNewByte:补丁新数据数组  dwTypeOfPatch:补丁类型
//     dwPatchNum:补丁数量              ID:补丁模版的资源ID          bCRC32:是否加入CRC32文件验证
BOOL CreatePatch(TCHAR szPatchName[ ], TCHAR szFileName[ ], DWORD lpPatchAddress[ ],BYTE lpOldByte[ ],BYTE lpNewByte[ ], DWORD  dwTypeOfPatch, DWORD dwPatchNum, DWORD ID, BOOL bCRC32 )
{
    static  char    secName[8] = ".sdata";
    DWORD   CRC32;
    if (bCRC32)       //这里是CRC32的验证
    {
        CRC32 = GetCRC32(szFileName );
        if (!CRC32)
        {
            MessageBox(NULL, TEXT("CRC32提取出错"), NULL, 0);
            return FALSE;
        }
    }
     
    DWORD   dwResSize;
    PBYTE   lpResData;
    HGLOBAL hGlobal;
    HRSRC hRes  = FindResource(hInst, MAKEINTRESOURCE(ID), L"PETYPE" );
    if (hRes)
    {
        dwResSize   = SizeofResource(hInst, hRes);
        hGlobal     = LoadResource(hInst, hRes);
        if (hGlobal )
        {
            lpResData   = (PBYTE)LockResource(hGlobal);
            if (lpResData )
            {
 
///////////////////////////////////开始写入文件并修正补丁中的参数//////////////////////////////////////////
 
                HANDLE                  hFile, hMap;
                PBYTE                   lpMemory;
                PIMAGE_NT_HEADERS       lpNtHeaders;
                PIMAGE_SECTION_HEADER   lpSectionHeader;
                PBYTE                   lpSectionData;
                DWORD*                  lpCRC32;
                DWORD                   dwFileSize, dwRead, dwSectionNum;
 
                if (INVALID_HANDLE_VALUE != ( hFile = CreateFile (szPatchName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
                {
                    WriteFile(hFile, lpResData, dwResSize, &dwRead, NULL);    //写入文件
                    dwFileSize  = GetFileSize (hFile, NULL);
                    //修正数据
                    if (dwFileSize)
                    {
                        hMap    = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
                        if (hMap)
                        {
                            lpMemory    = (BYTE *)MapViewOfFile (hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
                            if (lpMemory)
                            {
                                lpNtHeaders     = (PIMAGE_NT_HEADERS)(lpMemory + ((PIMAGE_DOS_HEADER)lpMemory)->e_lfanew);
                                if (bCRC32)    //写CRC32值
                                {
                                    lpCRC32         = (DWORD*)((PBYTE)(lpNtHeaders)-4);
                                    *lpCRC32        = CRC32;
                                }
                                dwSectionNum    = lpNtHeaders->FileHeader.NumberOfSections;
                                lpSectionHeader = (PIMAGE_SECTION_HEADER)(lpNtHeaders + 1);
                                 
                                //查找需修正变量所在区段
                                for (DWORD i=0; i < dwSectionNum; i++, lpSectionHeader++)
                                {
                                    if ( !lstrcmpiA( (LPCSTR)lpSectionHeader->Name, secName) )
                                        break;
                                }
                                lpSectionData           = lpMemory + RvaToOffset( (PIMAGE_DOS_HEADER)lpMemory, lpSectionHeader->VirtualAddress);
////////////////////////////////////////////////////////////修正变量///////////////////////////////////////////////////////////////////////////
                                int x;
                                *(DWORD*)lpSectionData  = dwTypeOfPatch;
                                *(DWORD*)(lpSectionData+4) = dwPatchNum;
                                 
                                for(x=lstrlen(szFileName); x > 0; x--)
                                    if(szFileName[x] == TEXT('\\') )
                                        break;
                                 
                                lstrcpy( (LPWSTR)(lpSectionData+8), &(szFileName[x]) );
                                memcpy(lpSectionData+528, lpPatchAddress, ITEM_NUM*sizeof(DWORD));
                                memcpy(lpSectionData+592, lpOldByte, ITEM_NUM*sizeof(BYTE));
                                memcpy(lpSectionData+608, lpNewByte, ITEM_NUM*sizeof(BYTE));
////////////////////////////////////////////////////////////修正完毕////////////////////////////////////////////////////////////////////////////
                                 
                                UnmapViewOfFile (lpMemory);
                                CloseHandle (hMap);
                                CloseHandle (hFile);
                                return TRUE;
                            }
 
                        }
 
                    }
 
                }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                return FALSE;
            }
        }
    }
    return FALSE;
}
到这里,我们的补丁工具就基本完成了,真的完成了?还没有!接下来就会出现我在前面提到的某个问题了!
事实上我们前面的代码单独有用,配上这个工具来动态生成用到实际补丁当中就无效了!为什么?且看下文。
现在来测试效果,其实你一测试就会发现,无论怎么生成,补丁都没有效果,我编写的过程就是这样,还不停的调工具程序,完全没有错误,生成的补丁的对应区段数据也正确写入,没有一点问题。
于是只能OD调试补丁程序一点点的查找,最后发现,在根据补丁类型数据进行switch跳转的地方发现,这个switch跳转不见了!仔细一想,原来是被编译器优化掉了这个跳转,因为“模版”中补丁类型的数据等于0,是个常量,所以编译器就直接优化掉跳转而不去管他。
所以我试着不给指示类型的数据初始值,然后继续调试,这次就更麻烦了,不给他初始值他都没在我们的目标区段.sdata占用空间,变量都找不到更别提修正了,于是就别想用初值来。。。于是想着实在没办法就只有用内联汇编实现跳转了,不过这样太难看了于是就问别人,结果还真问到了,一个用于指示链接器优化的代码#pragma optimize("",on|off)
于是只需要在包含跳转的代码函数体前后分别加上
1
2
3
#pragma optimize("",off)
//........函数体
#pragma optimize("",on)
就OK了
了解了这一层次的原因,我们就应该想到,所有用上了这几个数据的地方都可能存在这种问题,于是经过测试发现确实是这样,除了那几个数组数据,别的都存在这情况,不过如果在整个函数上都去掉优化那效率舍弃就太大了,于是我选择了将相关位置视情况改为内联汇编,这样就成功解决了这个问题,解决了这些问题后才是我们最终的“模版”补丁。
于是经过这些修改后,我们的补丁工具终于”出炉“了!

下面再提下那个”鸡肋“吧,各位应该看到界面上有个定制补丁选项吧?这个补丁选项也有个专门的函数CreateDiyPatch(),我本意是想实现一个用汇编编写的给目标文件最后一个区段加入执行代码的补丁(嗯,确实也写好了,在template文件目录下,附件可以找到),然后补丁到底要干什么由用户实现,所以如果点击了这个按钮就会弹出一个编辑器对话框,在里面写入需要的代码,然后由工具隐藏编译链接来实现补丁生成,嗯,虽然说写是写出来了这个功能,而且编译链接的结果也通过一些代码将结果以对话框的形式弹出来显示,但是一看发现masm32的库和头文件有几十MB总之很大,要移到工具里不太现实,需要机器里设置了masm32的环境变量才能正常编译链接,所以这个功能我也没有测试,不知道有没有一些小问题,下面就贴上这个函数的代码吧
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
BOOL CreateDiyPatch()
{
    static TCHAR    szStr[]= TEXT("创建自定义补丁");
    static char     szBuffer[1024];
    static TCHAR    szPatch[MAX_PATH]; 
    static TCHAR    szCurrentDirectory[MAX_PATH];
    static TCHAR    szOriDirectory[MAX_PATH];
    HANDLE  hAsmFile, hAsmMap;
    PBYTE   lpMemory;
    DWORD   dwFileSize;
    int x;
    if (DIY_OK == FALSE)
        return FALSE;
    if ( PopFileSaveDlg (g_hWnd, g_szPatchName, szStr) )
    {
        GetCurrentDirectory(sizeof(szOriDirectory), szOriDirectory);
        GetModuleFileName(hInst, szPatch, sizeof(szPatch) );
        for(x=lstrlen(szPatch); x > 0; x--)
            if(szPatch[x] == TEXT('\\') )
                break;
        lstrcpy(szCurrentDirectory, szPatch);
        lstrcpy(&(szCurrentDirectory[x]), L"\\template\\");  //获得当前目录
        lstrcpy( &(szPatch[x]), TEXT("\\template\\patch"));
        if (INVALID_HANDLE_VALUE != ( hAsmFile = CreateFile ( szPatch, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ , NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
        {
            dwFileSize  = GetFileSize (hAsmFile, NULL);
            if (dwFileSize)
            {
                hAsmMap = CreateFileMapping (hAsmFile, NULL, PAGE_READWRITE, 0, 0, NULL);
                if (hAsmMap)
                {
                    lpMemory    = (BYTE *)MapViewOfFile (hAsmMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
                    if (lpMemory)
                    {
                        memcpy(lpMemory, g_szUserCode, strlen( (char*)g_szUserCode) ); //复制自定义代码
                         
                        //下面代码用来隐藏控制台,要不编译链接的时候会一闪一闪,很难看
                        TCHAR   szConsoleTitle[100];//控制台标题
                        HWND    hCon;
                        AllocConsole(); //分配产生一个控制台
                        GetConsoleTitle(szConsoleTitle, sizeof(szConsoleTitle)); //获得控制台标题
                        hCon=FindWindow(NULL, szConsoleTitle);
                        ShowWindow(hCon, SW_HIDE);   //隐藏控制台
                         
                        //设置工作目录并编译连接,结果输出到_1.txt文件
                        SetCurrentDirectory(szCurrentDirectory);
                        system("ml /c /coff DiyPatch >> _1.txt");
                        system("link /subsystem:windows DiyPatch.obj DiyPatch.res >> _1.txt");
                        system("del DiyPatch.obj");
                        MoveFile(L"DiyPatch.exe", g_szPatchName);
                        memcpy(lpMemory, g_szBuffer, dwFileSize );//还原模版代码
                        UnmapViewOfFile(lpMemory);
                        CloseHandle(hAsmMap);
                        CloseHandle(hAsmFile);
 
                        FILE *pFile = fopen("_1.txt", "r+");   //提取_1.txt中存储的结果并显示对话框
                        int ch,i=0;
                        if (pFile != NULL)
                        {
                            while((ch = fgetc(pFile) )!= EOF)
                            {
                                if (ch == '\n')
                                    szBuffer[i++]='\r';
                                szBuffer[i++]=ch;
                            }
                            MessageBoxA(g_hWnd, szBuffer, "生成结果", 0);
                            fclose(pFile);
                        }
                        system("del _1.txt");
                        SetCurrentDirectory(szOriDirectory); //还原工作目录
                        return TRUE;
                    }
                    else    UnmapViewOfFile(lpMemory);
                }
                else    CloseHandle(hAsmMap);
            }
            else    CloseHandle(hAsmFile);
        }
    }
    return FALSE;
}

到这里整个补丁工具的三部分就讲完了,代码量大概有2000行,附件上工具的代码,整个所有工程的代码在第一课已经上传,可以在那找,另外顺便把一些用到的PE相关函数做了个DLL,附件有,比如要给PE加上你想要的代码,只要:
1
2
InitPE32(L"Test.exe");
AddCode(L"testnew2.exe", &APPEND_START, &APPEND_END-&APPEND_START, ADD_LAST_SECTION);//当然加上的补丁代码还是要自己用汇编写地~

三篇文章到此结束,可是完全没有人气啊啊啊伤心,没有人有兴趣么最后求次捧场吧(我想当会员T_T)

patchTool.rar

PE32_K.rar
上传的附件:
2012-5-28 17:47
0
雪    币: 23
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
好文章啊!!
2012-5-28 23:17
0
雪    币: 49
活跃值: (40)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
终于有个人了,感动ING
2012-5-29 00:10
0
雪    币: 1844
活跃值: (35)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
7
写了这么长,应该顶一下的
2012-5-29 01:23
0
雪    币: 24
活跃值: (27)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
help you help me  ,haha
2012-5-29 16:18
0
雪    币: 77
活跃值: (53)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
mark
2014-1-16 16:53
0
雪    币: 255
活跃值: (482)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
文章写得很好.特别是补丁代码附加到目标PE文件上进行patch,很强大.但最终的产品好像不行!!
我在XP SP3下试用不行:
目标程序加ASProtect V2.X Registered壳,无论哪种补丁方式均不成功.
不知是什么原因??难道是加了壳程序不行吗?无壳的程序没有试(如无壳还有必要用patch吗?)
一个地址一个字节进行patch,录入不方便,16个字节太少,实用性稍差.另外再增加一个编辑功能,对录入错误进行修改就好了.期待说明和进一步完善,真正变得很强大!
2014-1-17 16:46
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
看起来真的很不错,可惜我看不懂~~我还会回来的!!
2014-1-19 09:56
0
雪    币: 110
活跃值: (368)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
mark.
2014-1-20 00:50
0
雪    币: 49
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
mark ..
2014-1-20 08:38
0
雪    币: 47
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
mark
2014-1-20 08:40
0
雪    币: 2155
活跃值: (29)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
ASProtect算是一个比较强的可了。。。自带内存保护以及反内存补丁功能,当然无法patch了。。。
2014-1-20 10:50
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
学习~
2014-1-20 16:18
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
厉害。。。。。
2014-1-20 16:50
0
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
谢谢楼主分享。非常不错的文章
2014-1-20 20:48
0
雪    币: 36
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
确实不错呀,支持楼主
2014-1-20 23:15
0
雪    币: 110
活跃值: (368)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
刚看了你的代码,很不错。 不过直接用win32 api来写界面,代码好庞大
2014-1-22 23:49
0
雪    币: 37
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
必须回复,
2014-1-23 00:00
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
mark...
2014-1-23 10:15
0
雪    币: 37
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
非常感谢,这个找了好久了,终于找到个能用的,谢谢
2014-1-26 17:06
0
雪    币: 294
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
好棒~~~~支持楼主
2014-1-26 17:22
0
雪    币: 786
活跃值: (1666)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
汗颜啊  太长了  要是视频教程多好啊  看到文字头痛啊
2014-1-26 19:27
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册