这些都是我以前玩的,写到了Live Spaces上面了,以后把技术文章,都转过来。【玩,没有轻蔑的意思。按照那天在饼子堂的聊天说法,没有深入的研究,才叫玩。】
用机器码写Hello World (2006/1/24)
http://healerkx.spaces.live.com/blog/cns!9485ffc4816f2cad!267.entry为了做到这一点,我写了4个小程序。
一个是Server。它实际上是我写出来具有缓冲区溢出漏洞的victim。所以我不用检测其中的漏洞在哪了。当然了,你要相信我,我也不会发现漏洞的技术。我看到WinDbg上千万行的白底黑字的输出就会头疼。
还有一个Boft,它很简单,就是一个攻击程序。所以它简单,是因为它只含有发送的功能,而shellcode被我拿出来,写到了文件里了。
一个简单的编译器,现在这个阶段叫它编译器太过了。其实他就是一个代码的转化器,它可以把我手工键入的机器码和注释,转换为Shellcode可用的机器码。
最后一个是Shellcode,这个程序就是所有的关键了。当然了,这个可不是掌握了汇编就可以写出来的。关键在于,我前面提出的Server相当于Shellcode的运行环境。所以你的Shellcode里要包含指令外,还要有指令用到的数据。这样,你就不能用C/C++和一些汇编程序汇编出来的机器码了。因为它们非常容易包含绝对的地址。这个绝对地址在Shellcode工程里可以找到Hello World,但是被注入到Server后,就一定找不到Hello World了。所以,这个Shellcode里要含有数据。并且你的指令要进行相对寻址。所以我写好的Shellcode在前面加上NOP和JMP ESP的地址就可以了。而且Server是自己写的,所以很容易确定NOP的数量,使得JMP ESP的地址很容易覆盖栈上的原来EIP数据。更详细的说明,我在年后写出。提供给对这方面有兴趣的朋友参考。虽然这没有什么难度,而且baidu上也可以找到很多资料。但是很多细节足够难倒初学者了。
代码_asm
{
push -11
mov eax, 0x7c812ca9
call eax
mov edx, eax
jmp call_write_console
V1:
_emit 1
_emit 1
_emit 1
_emit 1
string:
_emit 'H'
_emit 'e'
_emit 'l'
_emit 'l'
_emit 'o'
_emit ' '
_emit 'W'
_emit 'o'
_emit 'r'
_emit 'l'
_emit 'd'
_emit '\n'
_emit 0
call_write_console:
call nextline
nextline:
pop ebx
sub ebx, 22
push 0
push ebx
push 12
add ebx, 4
push ebx
push edx
mov eax, 0x7c81cbed
call eax
}
关于缓冲区溢出攻击的一些研究说明 (2006/2/9)
http://healerkx.spaces.live.com/blog/cns!9485ffc4816f2cad!275.entry年前虽然说要写的,但是这种东西找资料,自己再下功夫看都能明白的,写得太直白了,就没有意思了,但是有朋友非要我写,而且我发现很多资料在某些地方说得很含糊,造成了阅读障碍。所以我还是写出来吧。
还得声明先,我的水平还得的远了,写出来的东西,有些就是copy来的;而且我的重点在于道出一些资料上少见的东西。并且,说错的地方,请明白的朋友一定要指出来,也好让我再清楚些。
首先说明的是,Win32的缓冲区溢出攻击比Linux的稍微难一些,但是是相似的。我们这里只说栈溢出攻击的情况,而且只说NNNNNRSSSSS格式。
NNNNNNRSSSSSS,N就是Nop指令,R是Ret的意思,我们会将栈上保存EIP的内存覆盖为JMP ESP的地址,S就是Shellcode了。
这里先说JMP ESP的地址,有人的疑问是“什么是机器码的地址?”。其实,机器码就是机器码,它们是没有所谓的地址的。这里所说的JMP ESP的地址,是指JMP ESP的机器码”FF E4”在进程中的某4 BYTES的内存上有这样的字符。实际上,很多黑客软件会在kernel32.dll,user32.dll里找JMP ESP的机器码字串。
原因是这样的:
当EIP指向Ret指令的时候,而Ret相当于POP EIP,执行后就会把原来PUSH到栈上的EIP恢复到EIP内。
这里还要打断一下,因为很多朋友看不到PUSH EIP的指令,那是因为CALL指令的缘故,CALL一个函数的时候,相当于PUSH EIP【似乎这里有问题,应该是EIP + CALL指令长度】,然后在JMP到那个函数的地址。所以栈上会有原EIP的值,然后接下来就会执行PUSH EBP了。
所以,栈的情况是,内存地址由高到低:原EIP,原EBP,其他的一些寄存器的值,然后就是函数栈的参数,变量了。我们用NNNNNNRSSSSS格式的 Shellcode覆盖这段栈,使得JMP ESP的地址覆盖到存放原EIP的内存地址。这样,Ret后,就会执行JMP ESP,而ESP往后移动,这样就跳到我们的Shellcode上了。
至于NNNRSSSS中N的个数是由栈的情况来定的,通常可以通过试验或者调试计算出来。这里需要说明的是,如果Shellcode比较短,那么NNNNN的部分可以写入Shellcode,只要在S的部分往前跳转就可以了。
还有,Shellcode如果被strcpy这样的函数处理。还要避免含有’\0’,这个我还没有研究。只是听说有这个问题需要注意。
另一个重要的问题是Shellcode的相对地址问题。比如说我们的Shellcode里需要字符串,我们要把这些字符串信息发送过去。而实际的C和汇编 编译器会把这些字符串常量编译为绝对的地址。那么这个地址就不能被Shellcode所采用了。所以我们要把一个可以进行相对寻址的字符串常量写入 Shellcode。
这里,饼子堂的“支书”教了我一招,用_emit指令来腾出指令空间存放字符串常量。我在前面的回复里给出了这样的代码,可以参考一下。
那么在这里问题里面就有一个获得EIP的问题了,Win32汇编语言是不能显式操作EIP寄存器的(我目前所知的),所以要通过一个小技巧来获得EIP的 值。这个就是CALL NEXTLINE,这个在那段代码里也有。其中的POP EBX,就可以将EIP的值存入EBX中。其中的原理,前面也说过了。
今天就说这么多吧,欢迎拍砖。
本进程内Hook API的简单做法 (2006/6/29)
http://healerkx.spaces.live.com/blog/cns!9485ffc4816f2cad!398.entry
本文提供一种超级简单的Hook进程内API的方法。主要使用到的Win32 API有以下这么多,不熟悉的朋友可以先看看它们的文档。GetCurrentProcess(...)WriteProcessMemory(...)所以说他简单,是因为在这个程序里没有太多的计算相对地址的东西。而且这个例子还有一个算是幸运的地方。那就是当我得到一个函数的地址的时候,然后去调用它的机器码恰好是5个字节。8B 45 F8
FF D0这个与E8 20 15 FF FF, call addr地址等长度。我们这里以Hook 本进程内的WriteFile为例子。我们声明一个和它同型的函数:BOOL
WINAPI
MyWriteFile(HANDLE hFile, LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped);
我们将要所有的WriteFile都先走我们的函数MyWriteFile,然后再执行真正的WriteFile实现部分。于是我们首先要跟踪WriteFile的调用位置。改写那里的机器码。void* m = MyWriteFile;
__asm {
mov eax, m
call eax
}我们先得到以上代码的Shellcode,就是8B 45 F8 FF D0;
我们写它到一个我们找到的固定地址,CHAR szBuffer[5] = { 0x8B, 0x45, 0xF8, 0xFF, 0xD0 };
DWORD d = 0;
WriteProcessMemory(GetCurrentProcess(), (VOID*)0x7C810FA6, szBuffer, 5, &d);这样,调用WriteFile的时候,就会跳转到MyWriteFile上了。当然了,我们还要能够调用真正的WriteFile,我们只要在MyWriteFile的开始把那段机器码改回来就可以了。这个和上面的方法是一样的。BOOL WINAPIMyWriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
CHAR szBuffer[5] = { 0xe8, 0x20, 0x15, 0xff. 0xff };
DWORD d = 0;
BOOL b = WriteProcessMemory(GetCurrentProcess(), (VOID*)0x7C810FA6, szBuffer, 5, &d);
return WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}
这样就可以了,简单吧? 这么改来改去的好处是,你不用自己出来太多的栈指针的问题。否则自己弄汇编来处理这些事情,既要写很多代码,又容易出现错误。OK,全篇结束,这么做的价值不是很大,要是Hook 其他进程的API了,那就做屏幕取词好了。呵呵。但是原理都是相同的啊。另外值得加的还有很多Comments,特别是李西西(印象里,当时他是金山毒霸的一员),他的回复是:帅哥, hook了之后你又把代码copy回去了? 这个算是一次性hook? 考虑多线程了吗? 永久hook怎么办? 难道再重新hook一次?
嗯,我想我在这里考虑得太简单了,实际应用中确实会有问题罢。但是做人不能太像《0 Bug》一书的作者肖舸了。于是也一并转过来。
世界很大,技术很多,再发点技术文章,来结束这篇汇总:
押键和指令存储
http://healerkx.spaces.live.com/blog/cns!9485FFC4816F2CAD!1245.entry押键...,指令存储...,对于那些熟悉我的朋友来说,乍一看这标题,以为是讲编程呢。不是,KOF相关的。
所谓押键,就是Press a Key and Hold On。
以KOF97,八神的“白光”为例。【所谓白光,是因为梦弹一段的时候,八神划出的血痕很亮,而超必杀的瞬间,又因为时间瞬间的暂停,使得那一道光非常亮白。】
近身C + 6A + 八稚女。【用统一的数字键来表示方向吧,玩家都明白。】
可以用押键的方法,简化为如此这般:近身C(押住) + 26A + 624A。这样,就不会变成C接暗勾手了。
理论简而言之,C + 26A,你看到的将会是重拳->暗勾手。而如果你押住C,那么26A将不会出必杀技。系统就是如此设计的,当你押住ABCD中某键的时候,你是不会 再打出必杀技的。这样押住C,八神就只能在C后打出梦弹了,而不会出错招。并且将指令26存储起来。【指令存储稍后再说】。
再说些比较有用的押键连续技。
草薙的近身B + 2B + A + 无式(最终决战奥义),可以演变成如此输入法:
近身B + 2B(押住B) + 26A + 26A。【注意,必须是26A,草薙的两个大招就是这样,无式用A出,大蛇薙用C出,否则接不上。】
还有就是Robert的极限流连舞脚接龙虎乱舞,很典型的取消技。
C + 极限流连舞脚 + 2A(取消) + 龙虎乱舞,可以演变成:
C + 极限流连舞脚 + 2A(押住) + 26 + 松开A + 524A。否则很容易出26A,或者来不及出超必杀。
再有就是二阶堂的2B * 3 >> 雷光拳:
可以2B (2押住)>> 6B >> 6B >> (2松开)6A。... ...
指令存储就简单了,
做一个简单的实验,八神离近对手,但不是近身。【也别太远了,大概不到一个人的身位吧,不过某些角色的指令投太过分了,居然能抓到一个身为远】
先输入426,然后不要松开6,那么八神会往前走吧?! 然后近身的时候输出C,那么一个屑风就出来了。
OK,讲点实际的,比如大门,克拉克,夏尔米都有的624624A超必杀,也可以使用此理论进行实战出招,近敌的时候先624,然后跑至近身,再624A or C即可。这样避免了你到对手近身再输入两个反半圆的窘迫。
先说这么多吧,以后再说跑动蓄力。【难道这个就不技术了?】