首页
社区
课程
招聘
新兵训练场第一期(11.30更新提示)
发表于: 2006-11-27 20:27 33303

新兵训练场第一期(11.30更新提示)

2006-11-27 20:27
33303
收藏
免费 7
支持
分享
最新回复 (46)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
26
TO 22楼

我把这个api当成undocument api,我再分析一下
从函数内部的处理可以还原函数原型
RtlInitUnicodeString(OUT *EAX ,IN *EBX),意思是传递一个字符串指针EBX,返回一个指针,具体这个返回的指针指向什么结构,还要继续分析

   edi=ebx
   edx=eax
   *edx=0=*eax
   *(edx+4)=edi
if (edi==0)  return 0
ecx=strlen(eax); //关于那个repne的命令我还是不太懂,我手边资料太少,
*(edx+2)=ecx   //上网也不方便,请兄弟帮忙详解此命令(执行这个命令时各FLAG或REGISTER的状态,此命令何时返回)
ecx-=2
*(edx)=ecx
执行了上面三行后,很明显发现edx指向的4byte内存,分成了两个word段,因此可以这样看
struct edx
{
    word ecx-2
    word ecx
}
刚才已经知道edx=eax,因此返回时eax就指向了一个结构
分析后再查查这个API,看到原型果然一致,它返回了一个指向UNICODE_STRING的字针

我想我也大概分析了七八分了,能分析成这样我想跟我的WINDOWS基础和使用了不少API编程的经验。
但我自己对汇编语言了解确实基础不牢,因此看ECX得出字串长度部分确实j晕晕的
希望笨笨雄详解此段汇?代码,将其抽象出具体意义

TO 23楼
     谢谢你昨天的解释,但我还是有疑问。即然NOT ECX后ECX已经返回了字串的长度,为什么还要shl ecx使 ecx*2呢,
     还有ecx到底是返回了包括0结尾的长度还是不包括呢
    另返回的结构中,ecx为一个成员,ecx-2又是一个成员,这代表什么意思呢
2006-11-30 09:42
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
27
最初由 shadowtear 发布
TO 22楼

我把这个api当成undocument api,我再分析一下
从函数内部的处理可以还原函数原型
RtlInitUnicodeString(OUT *EAX ,IN *EBX),意思是传递一个字符串指针EBX,返回一个指针,具体这个返回的指针指向什么结构,还要继续分析
........


repne   scas word ptr es:[edi]

word ,代表每次比较一个字,也就是2个字节。

所以NOT ECX之后,还需要乘2

repne ,每循环一次,ECX减1。只要标记寄存器的0标记不为1(ECX或者比较字符串都有可能使标记寄存器的0标记为1),一直循环下去

scas的实质,就是用AX - WORD PTR ES:[EDI]

当比较结果为0时,循环就会结束(注意ECX在最后一次比较中,还是会减一,所以得出的长度是包括结束字符标记00 00)

同样地,ECX为0时,循环也会结束,所以ECX必须给定一个最大值FFFFFFFF。否则循环在比较之前就会停下来

UNICODE是以2个字节代表一个字符的,所以每次比较是一个WORD

ASCII是以1个字节代表一个字符,如果计算长度则要用BYTE

整个函数的流程就是

初始化输出结构=>检查输入参数是否合法=>利用循环比较的方法来判断字符长度=>检查输出是否达到上限=>将结果填充入输出结构

为什么会有长度和最大长度呢?

因为有的API要求输入字符长度,有的API则不要求。储存/读取字符时,带结束标记或者带字符长度,由于没有统一标准,所以就分两个长度方便使用了。
2006-11-30 12:09
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
28
这个API跟WINDOWS基础以及API编程无关

只是一个简单的计算字符函数,即使你是在其他平台编程,也是要用到的。当然高级语言已经抽象了它,使你感觉不到CPU实际上是如何比较计算出字符长度。唯一的关系大概就是作为一个API,它必须能自己检查输入参数是否合法,得出的结果是否在允许的范围内。

在实际应用中,处理大量数据时,你仍然需要优化这个算法(处理少量数据暂时是该算法最好,计算大量数据长度则有另外一种算法,利用逻辑运算一次读取4个字节的,刚好与寄存器的长度对应。)。《汇编语言的艺术》里面有提到过效率问题。循环语句和判断语句,应该尽量少用,而是想办法用一些运算来代替它。
2006-11-30 12:17
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
29
详细+彻底,收了~
2006-11-30 12:24
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
30
看了半天,实在没搞懂!
2006-11-30 13:15
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
31
麻烦具体说说是哪里看不懂,一行一行代码看下来,哪一行不懂就说
2006-11-30 13:51
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
32
多谢老大的帖子,明确的目的+精彩的例子,让我感觉收获不少
第一题已经基本明白了,第二题还在做

我的感觉是多查资料,实在不懂就一个指令一个指令地仔细体会,详细揣摩。关键还是自己用功,差不多了之后再看别人的解说,功到自然成
2006-11-30 17:42
0
雪    币: 101
活跃值: (12)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
33
第一题:逆向RtlInitUnicodeString,这是一个NATIVE API。用处如字面的意思了。

77F8BB8C    53              push    ebx
77F8BB8D    8D86 DC010000   lea     eax, dword ptr [esi+1DC]
77F8BB93    50              push    eax
77F8BB94    E8 D49A0000     call    RtlInitUnicodeString

上面是它的调用过程,EBX是某字符的指针,EAX指向一个空内存地址

77F9566D >  57              push    edi
77F9566E    8B7C24 0C       mov     edi, dword ptr [esp+C]
77F95672    8B5424 08       mov     edx, dword ptr [esp+8]
77F95676    C702 00000000   mov     dword ptr [edx], 0
77F9567C    897A 04         mov     dword ptr [edx+4], edi
77F9567F    0BFF            or      edi, edi
77F95681    74 21           je      short 77F956A4
77F95683    83C9 FF         or      ecx, FFFFFFFF
77F95686    33C0            xor     eax, eax
77F95688    F2:66:AF        repne   scas word ptr es:[edi]
77F9568B    F7D1            not     ecx
77F9568D    D1E1            shl     ecx, 1
77F9568F    81F9 FEFF0000   cmp     ecx, 0FFFE
77F95695    0F87 1BBB0100   ja      77FB11B6
77F9569B    66:894A 02      mov     word ptr [edx+2], cx
77F9569F    49              dec     ecx
77F956A0    49              dec     ecx
77F956A1    66:890A         mov     word ptr [edx], cx
77F956A4    5F              pop     edi
77F956A5    C2 0800         retn    8

77FB11B6    B9 FEFF0000     mov     ecx, 0FFFE
77FB11BB  ^ E9 DB44FEFF     jmp     77F9569B

查MSDN ,知道这个函数原型
VOID
  RtlInitUnicodeString(
    IN OUT PUNICODE_STRING  DestinationString,
    IN PCWSTR  SourceString
    );
typedef struct _UNICODE_STRING {
  USHORT  Length;
  USHORT  MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING *PUNICODE_STRING;

可知道函数入了2个参数, 第一个参数是一个unicode_string的指针, 第2个参数是一个unicode的指针
对照的写
#define IN
#define OUT
typedef unsigned short WCHAR, USHORT
typedef const WCHAR * PCWSTR
typedef unsigned long DWORD
typedef struct _UNICODE_STRING {
  USHORT  Length;
  USHORT  MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING *PUNICODE_STRING;
void RtlInitUnicodeString(IN OUT PUNICODE_STRING  DestinationString, IN PCWSTR SourceString)
{
        DWORD lens;
        // 入函数的时候, esp为ret eip, esp+4为第一个参数,esp+8为第2个参数
        // 但是因为他又push edi了。 所以+8为第一个参数(edx), +c为第2个参数(edi)

        // mov     dword ptr [edx], 0
        DestinationString.Length = 0;
        DestinationString.MaximumLength = 0;

        //mov     dword ptr [edx+4], edi
        DestinationString.Buffer = SourceString;

        // or      edi, edi
        // je      short 77F956A4
        if (NULL == SourceString) return;

        // or      ecx, FFFFFFFF
        // xor     eax, eax
        // repne   scas word ptr es:[edi]
        // not     ecx
        // shl     ecx, 1
        lens = (wcslen(SourceString)+1) * 2;

        // cmp     ecx, 0FFFE
        // ja      77FB11B6
        if (lens >= (USHORT)(~0)) lens = (USHORT)(~0) - 2;

        // mov     word ptr [edx+2], cx
        // dec     ecx
        // dec     ecx
        // mov     word ptr [edx], cx
        DestinationString.MaximumLength = lens;
        DestinationString.Length = lens - 2;       
}

第二题:逆向下面算法,假设将任意32位数据保存在EAX中,得出的数据?原理是什么?

mov edx,eax
shr eax,1
and edx,55555555
and eax,55555555
shl edx,1
or  eax,edx
mov edx,eax
shr eax,2
and edx,33333333
and eax,33333333
shl edx,2
or  eax,edx
mov edx,eax
shr eax,4
and edx,0f0f0f0f
and eax,0f0f0f0f
shl edx,4
or  eax,edx
bswap eax

假如eax为0x12345678, 那么最后结果为32位倒序
eax = 0001 0010 0011 0100 0101 0110 0111 1000
分4个步骤.
第一个步骤55555555
2进制展开为01010101010101010101010101010101

右移一位后,跟这个值做and操作, 那么其实就是获取了原数的偶数位. 并且偶数位全部移动到鸡数位上去了
先and, 在左移一位, 恰恰相反。
结果这个只有全鸡数和全偶数的东东or一下, 其实就是相当把原数字交换。 就是
0010 0001 0011 1000 1010 1001 1011 0100  也就是说, 2位一组单元, 是反序

第2个步骤是333333
这个3也不简单, 他其实是 0011 0011 0011。。。。。
又是左移2位和右移2位的操作,结果就是把2位2位一换。 就是 4位一组单元, 是反序
1000 0100 1100 0010 1010 0110 1110 1101

同样第3个步骤为0000 1111 0000 1111 ,4位4位一交换。 就是 每个字节都反序
0100 1000 0010 1100 0110 1010 1101 1110

最后,字节序交换。bswap
0110 1010 1101 1110 0100 1000 0010 1100    所有字节反序, 也就是说32位相对于原来的32位, 是反序

最后一题:很简单,每个程序加载之后,在入口点都会看到类似的堆栈

0006FFC4   77E687F5  返回到 KERNEL32.77E687F5
0006FFC8   005916A8
0006FFCC   00000056
0006FFD0   7FFDF000
0006FFD4   00000200
0006FFD8   0006FFC8
0006FFDC   00000200
0006FFE0   FFFFFFFF  SEH 链尾部
0006FFE4   77E7F0B4  SE处理程序
0006FFE8   77E68EC8  KERNEL32.77E68EC8
0006FFEC   00000000
0006FFF0   00000000
0006FFF4   00000000
0006FFF8   01006420  NOTEPAD.<模块入口点>
0006FFFC   00000000

按堆栈第一个77e687f5, 返回, 然后od中ctrl+g到这里。
在这个函数下端点。 把od设置入口在system point。
然后ctrl+f2。 可以发现这样的代码
7C8123C2                                     6A 0C            PUSH 0C
7C8123C4                                     68 F023817C      PUSH kernel32.7C8123F0
7C8123C9                                     E8 AB930100      CALL kernel32.7C82B779
7C8123CE                                     8365 FC 00       AND DWORD PTR SS:[EBP-4],0
7C8123D2                                     6A 04            PUSH 4
7C8123D4                                     8D45 08          LEA EAX,DWORD PTR SS:[EBP+8]
7C8123D7                                     50               PUSH EAX
7C8123D8                                     6A 09            PUSH 9
7C8123DA                                     6A FE            PUSH -2
7C8123DC                                     FF15 4413807C    CALL DWORD PTR DS:[<&ntdll.NtSetInformati>; ntdll.ZwSetInformationThread
7C8123E2                                     FF55 08          CALL DWORD PTR SS:[EBP+8]
7C8123E5                                     50               PUSH EAX
7C8123E6                                     E8 BC3D0100      CALL kernel32.ExitThread

其中kernel32.7C82B779是用来构造seh,  CALL DWORD PTR SS:[EBP+8]这个是进到程序入口。

关于入口地址什么时候入栈,od的trace功能可以发现, 是在一个syscall中完成的, 应该用了imagehelp函数定位到的enterpoint
2006-12-1 16:50
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
34
ou欧来学习学习了
2006-12-1 23:53
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
35
33楼讲得好充分呀,学习了
2006-12-2 00:08
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
36
这练习对33楼来说好象简单了
2006-12-2 23:27
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
37
33楼是高人啊
在训练结束时给我们总结一番也是费心了呢
2006-12-2 23:34
0
雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
38
笨笨雄老大给你学逆向0环core api
支持...
2006-12-5 09:03
0
雪    币: 350
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
39
   什么叫逆向?
2006-12-17 02:52
0
雪    币: 405
活跃值: (10)
能力值: ( LV9,RANK:1130 )
在线值:
发帖
回帖
粉丝
40
看天书看天书。
感觉回帖就像是写书的人。因为我一看书就想睡觉。
2006-12-17 09:28
0
雪    币: 350
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
41
    搞了半天,逆向就是看图写话,刚看罗云彬的书大半个星期,里面没说
2006-12-17 14:58
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
42
不搞懂你的意思。。。
2006-12-17 15:07
0
雪    币: 350
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
43
我刚从火星来
正在查指令意思,做第一题。

  才发了5贴就没了啊。要等明天,只好编辑了。

逆了半天向,终于搞明白了一点意思。刚才把那个 HELLO WORLD 的入门程序用Ollydbg打开,对照着汇编源代码,仔细比看了下,MESSAGEBOXA和EXITPROCESS在Ollydbg里面都变成了CALL, CALL前的几个PUSH都是参数。单看这个,逆向出来就是这个入门程序实现出来的功能效果。
再进里面的CALL,从里面的代码逆向出来的就是那个CALL的功能、、、不过我进不去那2个CALL,还不会用Ollydbg  
   逆向就是从具体的一大堆代码里,看出来他要做的是啥事,怎么做的。俺顿悟了

谢谢笨笨雄版主 ,我正在死拼第一题。
2006-12-17 15:16
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
44
欢迎来到地星
懂查指令,证明你已经很不错了。加油
2006-12-17 15:23
0
雪    币: 214
活跃值: (15)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
45
最初由 sunyin 发布
我只能看懂一点点

me too
2006-12-19 00:30
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
46
单个的、简单常见的指令通过查资料基本能搞懂

努力查资料中
2006-12-19 12:00
0
雪    币: 11
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
47
十年之约~这是此版最早的一篇文章,顶出来。

起床看了部movie,《霸王别姬》很是怀念,也使我回忆起了很多人事。没看过的朋友可以去看看
2016-12-11 11:44
0
游客
登录 | 注册 方可回帖
返回
//