软讯网络 > 网络安全 > 黑客技术 > 为OICQ 820添加显示IP地址和端口号的功能(2)
【标 题】:为OICQ 820添加显示IP地址和端口号的功能(2)
【关键字】:
C,IP,ICQ,20,IP地址,端口,IP地址,OICQ,OICQ,820,IP
【来 源】:网络
为OICQ 820添加显示IP地址和端口号的功能(2)
言归正传,既然获得了关键的数据指针,那可以说我们已经成功了90%了,接下来的就是显示出来而已。但这也需要反复的试验和修改。让我们 好好来回味一下这一过程:
首先,要实现新的功能毫无疑问需要添加代码和数据,以及执行一定的API函数,所以需要找到适当的方法添加代码到原程序之中。完美的方法就 是作一个外壳添加程序,在原程序中添加所需要的段(数据段,程序段),以及添加Import表表项,以用于新的API函数的地址定位,还要修改PE文件头中的各项相关信息,这 就是病毒的做法。这无疑非常的复杂和繁琐,有兴趣的同学可以参见我以前的文章“关于95下可执行文件的加密研究”。其实我们的要添加的程序量并不大,充其量也就零零星 星的几百个字节,而且用到的API函数也不多。所以我们采取了手动修改添加的方法,但也需要必要的条件和方法。看我以下的分析和方法:
PE可执行文件的逻辑结构是段,比如代码段“.text”、数据段“.data”、资源段“.rscs”等等。这些段大小都是按文件对齐,也就是说段大小 至少会按10h对齐,一般是1000h(4096字节),这由文件头中指定(链接的时候确定)。但是代码也好数据也好,不可能做到长度刚好是对齐的。也就是说,段的大小是大于段 中代码或数据实际大小的。他们之间的差值就是该段冗余的空间,这个空间被称为“空隙”。有一些简单的PE文件减肥软件就是使用去掉“空隙”的方法来减肥的。这个“空隙”可 以被我们用来放置代码、数据以及堆栈。我常用的分析PE文件文件头的工具软件是Borland以前在C++系列软件中带的“Tdump.exe”。让我们看看实际分析的结果:
Object table:
# Name VirtSize RVA PhysSize Phys off Flags
-- -------- -------- -------- -------- -------- --------
01 .text 000D0637 00001000 000D1000 00001000 60000020 [CER]
02 .rdata 000320E8 000D2000 00033000 000D2000 40000040 [IR]
03 .data 00039848 00105000 00012000 00105000 C0000040 [IRW]
04 .rsrc 0003E4C0 0013F000 0003F000 00117000 40000040 [IR]
以上是用Tdump看到的Oicq.exe的段信息(它的Oject就是我们所说的段)。我们肯定是首选.text段进行观察(.text是代码段,Flag为CER,意思就是包 含代码、可执行、可读的意思(Contains code, Execute,Readable))。可以看到,.text段代码实际长度D0637h,物理长度D1000h,文件偏移位置为1000h处。OK,这个段有 D1000h-D0637h=C9Ch的“空隙”。这个长度完全可以满足我们的需要了,而且代码数据堆栈都可以放在这个区域内。众所周知,要作为数据段使用,段的属性(Flags)需要可写。 好了,只需要改写“.text”的属性即可,可写属性的值是80000000h,然后加上原来的60000020h后,就是C0000020h了,也就是变成了CERW属性(具体修改方法详见“代码段段属 性修改”)。看看我们更改后的用Tdump分析的结果。
Object table:
# Name VirtSize RVA PhysSize Phys off Flags
-- -------- -------- -------- -------- -------- --------
01 .text 000D0637 00001000 000D1000 00001000 C0000020 [CRW]
02 .rdata 000320E8 000D2000 00033000 000D2000 40000040 [IR]
03 .data 00039848 00105000 00012000 00105000 C0000040 [IRW]
04 .rsrc 0003E4C0 0013F000 0003F000 00117000 40000040 [IR]
好了,找到了放置代码数据和堆栈的地方,也就是其实偏移1000h+D0637h 的地方。为了对齐边界,我们采用D1640h这个值(文件偏移)。用Tdump 查看代码段基址(Code Base)和PE文件映象基址(Image Base),分别是1000h和400000h,可以算出我们的程序在装入后的实际地址,400000h+1000h+D0640h=4D1640h。也就是 说我们的代码在被系统装入后在内存4D1640h处,这在以后程序跳转处用到。
添加代码的工作已经做好,现在关键的问题就是编制具体的代码,以用于IP地址和端口号的保存和显示。
首先是对对象数据中IP地址和端口信息的保存,我们在获得该数据指针后(程序00425157处),更改程序使程序直接跳转到我们的保存程序中 (4D1640h)。该数据的指针首址放在EAX,由于该段程序有些寄存器的值都有用,堆栈也不能乱压。所以我们首先修改了栈指针,使所有的堆栈活动都在我们的“空隙”中进行 (堆栈顶端4D1900h)。然后保存几个寄存器的值(压栈)。IP地址和端口号分别在该数据结构+214h和+218h的地方(也就是EAX+214h和EAX+218h)。IP地址是一个字符串指针 ,端口是个32位整数。我们要做的就是把他们都转换成字符串,保存在自己的地盘中。我们巧妙的用了一个wsprintfA函数把字符串和端口号输出到一个地址上(随便在我们的 “空隙”中找个空闲的地址,我用的是4D1700h,4D1720h中放的是格式化字符串“%s:%d”)。实际这段程序翻译成C语言就是printf(“%s:%d”,char *ip,int port),这样 我们就把字符串形式的数据保存在了数据区里。值得注意的是,由于我们修改了原程序中的有用的代码用于跳转程序,所以在我们的程序中就需要加上(cmp dword ptr [eax+000001DC], ecx)这句代码,退出我们的程序之前恢复栈指针和各寄存器,用一条无条件跳转指令转回到原程序继续执行。
细心的同学可能注意到我们没有显式的调用wsprintfA函数,但是我们实际上是调用了。在显示IP的那段附加程序中调用SetWindowTextA的调用也 是这样。这是怎么回事呢?我们知道所有API函数的调用前都需要重定位,这个过程发生在系统装入这个PE程序的时候。系统按照PE文件中Import表的内容对API函数在程序中的 地址进行填写。我们没有修改Import表,所以,如果直接写上汇编代码,系统是不会为它定位的。由于Import表比较复杂,更改它是个非常繁琐的事情。所以我们采用变通的方 法,既然系统为用到的API函数地址都作了重定位,所以API函数的地址信息也就存在了。
具体的做法如下:
找到原程序中有调用wsprintfA的地方:
:0049CE30 FF1560274D00 Call USER32.wsprintfA
:0049CE36 83C410 add esp, 00000010
:0049CE39 EB1A jmp 0049CE55
|