能力值:
( 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又是一个成员,这代表什么意思呢
能力值:
(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则不要求。储存/读取字符时,带结束标记或者带字符长度,由于没有统一标准,所以就分两个长度方便使用了。
能力值:
(RANK:570 )
28 楼
这个API跟WINDOWS基础以及API编程无关
只是一个简单的计算字符函数,即使你是在其他平台编程,也是要用到的。当然高级语言已经抽象了它,使你感觉不到CPU实际上是如何比较计算出字符长度。唯一的关系大概就是作为一个API,它必须能自己检查输入参数是否合法,得出的结果是否在允许的范围内。
在实际应用中,处理大量数据时,你仍然需要优化这个算法(处理少量数据暂时是该算法最好,计算大量数据长度则有另外一种算法,利用逻辑运算一次读取4个字节的,刚好与寄存器的长度对应。)。《汇编语言的艺术》里面有提到过效率问题。循环语句和判断语句,应该尽量少用,而是想办法用一些运算来代替它。
能力值:
( LV2,RANK:10 )
29 楼
详细+彻底,收了~
能力值:
( LV2,RANK:10 )
30 楼
看了半天,实在没搞懂!
能力值:
(RANK:570 )
31 楼
麻烦具体说说是哪里看不懂,一行一行代码看下来,哪一行不懂就说
能力值:
( LV2,RANK:10 )
32 楼
多谢老大的帖子,明确的目的+精彩的例子,让我感觉收获不少
第一题已经基本明白了,第二题还在做
我的感觉是多查资料,实在不懂就一个指令一个指令地仔细体会,详细揣摩。关键还是自己用功,差不多了之后再看别人的解说,功到自然成
能力值:
( 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
能力值:
( LV2,RANK:10 )
34 楼
ou欧来学习学习了
能力值:
( LV2,RANK:10 )
35 楼
能力值:
(RANK:570 )
36 楼
这练习对33楼来说好象简单了
能力值:
( LV2,RANK:10 )
37 楼
33楼是高人啊
在训练结束时给我们总结一番也是费心了呢
能力值:
( LV12,RANK:770 )
38 楼
笨笨雄老大给你学逆向0环core api
支持...
能力值:
( LV2,RANK:10 )
39 楼
什么叫逆向?
能力值:
( LV9,RANK:1130 )
40 楼
看天书看天书。
感觉回帖就像是写书的人。因为我一看书就想睡觉。
能力值:
( LV2,RANK:10 )
41 楼
搞了半天,逆向就是看图写话,刚看罗云彬的书大半个星期,里面没说
能力值:
(RANK:570 )
42 楼
不搞懂你的意思。。。
能力值:
( LV2,RANK:10 )
43 楼
我刚从火星来
正在查指令意思,做第一题。
才发了5贴就没了啊。要等明天,只好编辑了。
逆了半天向,终于搞明白了一点意思。刚才把那个 HELLO WORLD 的入门程序用Ollydbg打开,对照着汇编源代码,仔细比看了下,MESSAGEBOXA和EXITPROCESS在Ollydbg里面都变成了CALL, CALL前的几个PUSH都是参数。单看这个,逆向出来就是这个入门程序实现出来的功能效果。
再进里面的CALL,从里面的代码逆向出来的就是那个CALL的功能、、、不过我进不去那2个CALL,还不会用Ollydbg
逆向就是从具体的一大堆代码里,看出来他要做的是啥事,怎么做的。俺顿悟了
谢谢笨笨雄版主 ,我正在死拼第一题。
能力值:
(RANK:570 )
44 楼
欢迎来到地星
懂查指令,证明你已经很不错了。加油
能力值:
( LV4,RANK:50 )
45 楼
最初由 sunyin 发布 我只能看懂一点点
me too
能力值:
( LV2,RANK:10 )
46 楼
单个的、简单常见的指令通过查资料基本能搞懂
努力查资料中
能力值:
( LV2,RANK:10 )
47 楼
十年之约~这是此版最早的一篇文章,顶出来。
起床看了部movie,《霸王别姬》很是怀念,也使我回忆起了很多人事。没看过的朋友可以去看看