tqsheng

go.....
随笔 - 366, 文章 - 18, 评论 - 101, 引用 - 0
数据加载中……

http://bbs.pcpop.com/080119/0O00560O000009621-1.html

http://bbs.pcpop.com/080119/0O00560O000009621-1.html

posted @ 2009-03-16 17:04 tqsheng 阅读(391) | 评论 (1)编辑 收藏

http://qzone.qq.com/blog/3484871-1226909513

http://qzone.qq.com/blog/3484871-1226909513
http://www.yoobao.net/redirect.php?tid=9696&goto=lastpost
http://bbs.cnsmartphone.com/thread-27080-1-1.html

http://cn.made-in-china.com/image/4f0j01yBeEiJvhEpqtM/%E6%97%A5%E6%9C%ACMAXELL%E7%BA%BD%E6%89%A3%E7%94%B5%E6%B1%A0%EF%BC%88ML2016%EF%BC%89.jpg

posted @ 2009-03-11 12:54 tqsheng 阅读(169) | 评论 (0)编辑 收藏

http://bible.younet.com/files/2005/12/17/332810.shtml

http://bible.younet.com/files/2005/12/17/332810.shtml

http://www.dpdbbs.com/thread-74895-1-1.html

登录名

1acer

密码123456

posted @ 2009-03-05 08:50 tqsheng 阅读(173) | 评论 (0)编辑 收藏

TLS callback科普小知识

TLS callback科普小知识
2008年04月29日 星期二 下午 01:47
前言:最初想了解TLS callback是来自于team509翻译的一篇关于TLS callback反ida的文章。当时问了几个朋友对TLS callback都不太了解,但是问到dummy牛,他给了一个他曾经写过的使用TLS callback anti-debugger的壳,于是就看了下TLS callback机制。如果你是新手,并对TLS callback机制感兴趣,那么这篇基础文章也许对你有用。不过,了解的就勿看了,没有什么东西 :)。另外,文章没有进行排版修改.....= =|||

======================我是邪恶的分割线=========================

每个线程拥有自己的线程局部存储,互补干扰。系统中线程局部存储是存放在线程的TEB中,每个线程都有自己的TEB因此互相独立。见下面的TEB结构中的ThreadLocalStoragePointer、TlsSlots、TlsLinksTlsExpansionSlots域。对TLS的访问通过 TlsAlloc、TlsSetValue和TlsGetValue以及TlsFree几个API进行。这些API也是对TEB中Tls相关域的访问。跟踪Tls*等API函数发现,系统通过PEB中的TlsBitmap来保存Tls的使用记录,并据此分配Tls索引,另外PEB还有 TlsExpansionCounter和TlsBitmapBits来跟踪Tls的使用情况。

选择子fs所对应的段用来存储TEB和PEB等信息,由ring3下FS选择子如下:
selector 3B, DATA32, BaseAddress 7FFDF000, Limit 00000FFF, DPL 3, Present, RW
因此在ring3下是可读写的。fs:[0]指向_NT_TIB中的ExceptionList,fs:[18]指向_NT_TIB自身,也就是指向_TEB结构;
mov eax, dword ptr fs:[18]
mov esi, dword ptr [eax+30]
此时esi指向PEB,dword ptr[esi+40]即为TlsBitmap,假设ecx为分配的索引则

mov eax, dword ptr fs:[18]
mov [eax+ecx*4+e10], data

mov eax, dword ptr fs:[18]
mov data, [eax+ecx*4+e10]

结构如下所示(windows XP professional SP2)

    kd> dt !_NT_TIB
    +0×000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
    +0×004 StackBase : Ptr32 Void
    +0×008 StackLimit : Ptr32 Void
    +0×00c SubSystemTib : Ptr32 Void
    +0×010 FiberData : Ptr32 Void
    +0×010 Version : Uint4B
    +0×014 ArbitraryUserPointer : Ptr32 Void
    +0×018 Self : Ptr32 _NT_TIB
    //SEH和栈信息

    kd> dt !_TEB
    +0×000 NtTib : _NT_TIB
    +0×01c EnvironmentPointer : Ptr32 Void
    +0×020 ClientId : _CLIENT_ID
    +0×028 ActiveRpcHandle : Ptr32 Void
    +0×02c ThreadLocalStoragePointer : Ptr32 Void
    +0×030 ProcessEnvironmentBlock : Ptr32 _PEB
    +0×034 LastErrorValue : Uint4B
    +0×038 CountOfOwnedCriticalSections : Uint4B
    +0×03c CsrClientThread : Ptr32 Void
    +0×040 Win32ThreadInfo : Ptr32 Void
    +0×044 User32Reserved : [26] Uint4B
    +0×0ac UserReserved : [5] Uint4B
    +0×0c0 WOW32Reserved : Ptr32 Void
    +0×0c4 CurrentLocale : Uint4B
    +0×0c8 FpSoftwareStatusRegister : Uint4B
    +0×0cc SystemReserved1 : [54] Ptr32 Void
    +0×1a4 ExceptionCode : Int4B
    +0×1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
    +0×1bc SpareBytes1 : [24] UChar
    +0×1d4 GdiTebBatch : _GDI_TEB_BATCH
    +0×6b4 RealClientId : _CLIENT_ID
    +0×6bc GdiCachedProcessHandle : Ptr32 Void
    +0×6c0 GdiClientPID : Uint4B
    +0×6c4 GdiClientTID : Uint4B
    +0×6c8 GdiThreadLocalInfo : Ptr32 Void
    +0×6cc Win32ClientInfo : [62] Uint4B
    +0×7c4 glDispatchTable : [233] Ptr32 Void
    +0xb68 glReserved1 : [29] Uint4B
    +0xbdc glReserved2 : Ptr32 Void
    +0xbe0 glSectionInfo : Ptr32 Void
    +0xbe4 glSection : Ptr32 Void
    +0xbe8 glTable : Ptr32 Void
    +0xbec glCurrentRC : Ptr32 Void
    +0xbf0 glContext : Ptr32 Void
    +0xbf4 LastStatusValue : Uint4B
    +0xbf8 StaticUnicodeString : _UNICODE_STRING
    +0xc00 StaticUnicodeBuffer : [261] Uint2B
    +0xe0c DeallocationStack : Ptr32 Void
    +0xe10 TlsSlots : [64] Ptr32 Void
    +0xf10 TlsLinks : _LIST_ENTRY
    +0xf18 Vdm : Ptr32 Void
    +0xf1c ReservedForNtRpc : Ptr32 Void
    +0xf20 DbgSsReserved : [2] Ptr32 Void
    +0xf28 HardErrorsAreDisabled : Uint4B
    +0xf2c Instrumentation : [16] Ptr32 Void
    +0xf6c WinSockData : Ptr32 Void
    +0xf70 GdiBatchCount : Uint4B
    +0xf74 InDbgPrint : UChar
    +0xf75 FreeStackOnTermination : UChar
    +0xf76 HasFiberData : UChar
    +0xf77 IdealProcessor : UChar
    +0xf78 Spare3 : Uint4B
    +0xf7c ReservedForPerf : Ptr32 Void
    +0xf80 ReservedForOle : Ptr32 Void
    +0xf84 WaitingOnLoaderLock : Uint4B
    +0xf88 Wx86Thread : _Wx86ThreadState
    +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
    +0xf98 ImpersonationLocale : Uint4B
    +0xf9c IsImpersonating : Uint4B
    +0xfa0 NlsCache : Ptr32 Void
    +0xfa4 pShimData : Ptr32 Void
    +0xfa8 HeapVirtualAffinity : Uint4B
    +0xfac CurrentTransactionHandle : Ptr32 Void
    +0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
    +0xfb4 SafeThunkCall : UChar
    +0xfb5 BooleanSpare : [3] UChar

    kd> dt !_PEB
    +0×000 InheritedAddressSpace : UChar
    +0×001 ReadImageFileExecOptions : UChar
    +0×002 BeingDebugged : UChar
    +0×003 SpareBool : UChar
    +0×004 Mutant : Ptr32 Void
    +0×008 ImageBaseAddress : Ptr32 Void
    +0×00c Ldr : Ptr32 _PEB_LDR_DATA
    +0×010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
    +0×014 SubSystemData : Ptr32 Void
    +0×018 ProcessHeap : Ptr32 Void
    +0×01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
    +0×020 FastPebLockRoutine : Ptr32 Void
    +0×024 FastPebUnlockRoutine : Ptr32 Void
    +0×028 EnvironmentUpdateCount : Uint4B
    +0×02c KernelCallbackTable : Ptr32 Void
    +0×030 SystemReserved : [1] Uint4B
    +0×034 AtlThunkSListPtr32 : Uint4B
    +0×038 FreeList : Ptr32 _PEB_FREE_BLOCK
    +0×03c TlsExpansionCounter : Uint4B
    +0×040 TlsBitmap : Ptr32 Void
    +0×044 TlsBitmapBits : [2] Uint4B
    +0×04c ReadOnlySharedMemoryBase : Ptr32 Void
    +0×050 ReadOnlySharedMemoryHeap : Ptr32 Void
    +0×054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
    +0×058 AnsiCodePageData : Ptr32 Void
    +0×05c OemCodePageData : Ptr32 Void
    +0×060 UnicodeCaseTableData : Ptr32 Void
    +0×064 NumberOfProcessors : Uint4B
    +0×068 NtGlobalFlag : Uint4B
    +0×070 CriticalSectionTimeout : _LARGE_INTEGER
    +0×078 HeapSegmentReserve : Uint4B
    +0×07c HeapSegmentCommit : Uint4B
    +0×080 HeapDeCommitTotalFreeThreshold : Uint4B
    +0×084 HeapDeCommitFreeBlockThreshold : Uint4B
    +0×088 NumberOfHeaps : Uint4B
    +0×08c MaximumNumberOfHeaps : Uint4B
    +0×090 ProcessHeaps : Ptr32 Ptr32 Void
    +0×094 GdiSharedHandleTable : Ptr32 Void
    +0×098 ProcessStarterHelper : Ptr32 Void
    +0×09c GdiDCAttributeList : Uint4B
    +0×0a0 LoaderLock : Ptr32 Void
    +0×0a4 OSMajorVersion : Uint4B
    +0×0a8 OSMinorVersion : Uint4B
    +0×0ac OSBuildNumber : Uint2B
    +0×0ae OSCSDVersion : Uint2B
    +0×0b0 OSPlatformId : Uint4B
    +0×0b4 ImageSubsystem : Uint4B
    +0×0b8 ImageSubsystemMajorVersion : Uint4B
    +0×0bc ImageSubsystemMinorVersion : Uint4B
    +0×0c0 ImageProcessAffinityMask : Uint4B
    +0×0c4 GdiHandleBuffer : [34] Uint4B
    +0×14c PostProcessInitRoutine : Ptr32
    +0×150 TlsExpansionBitmap : Ptr32 Void
    +0×154 TlsExpansionBitmapBits : [32] Uint4B
    +0×1d4 SessionId : Uint4B
    +0×1d8 AppCompatFlags : _ULARGE_INTEGER
    +0×1e0 AppCompatFlagsUser : _ULARGE_INTEGER
    +0×1e8 pShimData : Ptr32 Void
    +0×1ec AppCompatInfo : Ptr32 Void
    +0×1f0 CSDVersion : _UNICODE_STRING
    +0×1f8 ActivationContextData : Ptr32 Void
    +0×1fc ProcessAssemblyStorageMap : Ptr32 Void
    +0×200 SystemDefaultActivationContextData : Ptr32 Void
    +0×204 SystemAssemblyStorageMap : Ptr32 Void
    +0×208 MinimumStackCommit : Uint4B

PE和编译器支持:

    PE文件结构的目录表(directory table)中有一个是IMAGE_DIRECTORY_ENTRY_TLS (9)线程级局部存储目录,如果此处非零的话,PE将会有一个tls节,tls节以结构IMAGE_TLS_DIRECTORY(winnt.h)开始
    typedef struct _IMAGE_TLS_DIRECTORY32 {
    DWORD StartAddressOfRawData;
    DWORD EndAddressOfRawData;
    PDWORD AddressOfIndex;
    PIMAGE_TLS_CALLBACK *AddressOfCallBacks;
    DWORD SizeOfZeroFill;
    DWORD Characteristics;
    } IMAGE_TLS_DIRECTORY32;
    其中PIMAGE_TLS_CALLBACK如下:
    typedef VOID
    (NTAPI *PIMAGE_TLS_CALLBACK) (
    PVOID DllHandle,
    DWORD Reason,
    PVOID Reserved
    );
    为了方便使用TLS,编译器支持用__declspec(thread)来修饰全局或静态变量,这样的变量最终存储在TLS中,对这些变量的访问与普通变量一样,编译器负责处理其中细节分配索引和初始化等。
    IMAGE_TLS_DIRECTORY32 中的StartAddressOfRawData和EndAddressOfRawData指向的区域保存__declspec(thread)变量的初始值,AddressOfIndex指向的区域保存动态分配的Tls索引。AddressOfCallBacks指向一个回调函数,此函数与 DllMain有同样的形式,它会在线程创建前被调用,多用来完成__declspec(thread)变量的初始化等,也被用于anti- debugger(如blacklight)。
    Visual Studio C++中对此的支持请参考Thread Local Storage - The C++ Way。
    MASM中可以将tls节中的数据放到data节中,然后使用LordPE等工具改变PE中目录表中的IMAGE_DIRECTORY_ENTRY_TLS为对应的地址和大小即可,具体请见
    TLS-CallBack +IsDebuggerPresent() Debugger Detection。

参考文献
MSDN
Thread Local Storage: http://www.windowsitlibrary.com/Content/356/11/5.html
Thread Local Storage - The C++ Way:http://www.codeproject.com/threads/tls.asp
TLS-CallBack +IsDebuggerPresent() Debugger Detection:
http://www.openrce.org/reference_library/anti_reversing_view/26/TLS-CallBack%20+IsDebuggerPresent()%20Debugger




http://hi.baidu.com/ayarei/blog/item/1cfc9d0262c5900a4bfb511f.html

posted @ 2008-11-04 21:40 tqsheng 阅读(522) | 评论 (0)编辑 收藏

NEG+SBB指令组合的用处

NEG+SBB指令组合的用处

在逆向遇到NEG+SBB指令的组合,不明白原因,上网一查,找到了.嘿嘿!
mov     ebx, eax
neg      ebx
push    esi
sbb      bl, bl

注:Intel处理器的文档中声明NEG指令除了对操作数作符号取反外,它还要根据操作数的值设置CF标志位的值。如果执行NEG指令时操作数为0,则CF将被置为0。如果操作数为非零,则CF将被置为1。

NEG是一个简单的取反指令,用来对操作数的算术符号取反——有时候也称它为2的补码(two’s complement。译注:1的补码与NOT指令对应,是逻辑取反指令)。NEG用C描述的伪代码:

Result = -(Operand);

SBB是一条带借位的减法指令,这就是说SBB将第二个操作数(即右操作数)加上标志位CF,然后将结果用第一个操作数来减。SBB用C描述的伪代码:

Operand1 = Operand1 - (Operand2 + CF);

 

在上面的指令中NEG的作用应该就是注中的,根据操作数来设置CF标志位的值,如果EBX为0,那么CF将被置为0,如果EBX为非零,则CF将被置为1.然后结合SBB,我们可以将以上4条指令理解为,当EBX等于0时,BL将等于0;EBX大于0时,BL将等于-1.

 

参考:Reversing:逆向工程揭密-附录A揭密代码结构(4)

// 对返回数进行处理,这是编译器优化的结果,翻译成C可能是
// if (eax)
// return 1 ;
// else
// return 0 ;
// 求反.执行NEG指令时操作数为0,则CF将被置为0。如果操作数为非零,则CF将被置为1
/*41A72D*/ NEG EAX
// 高位借位减法,EAX的值取决于前面的neg指令。对同一个操作数执行减运算,
// 即用EAX寄存器减去EAX,如果不考虑标志位CF的话,这条指令在数学上是没有任何意义的。
// 因为将CF加到了第二个操作数上,所以结果只取决于CF。如果“CF == 1”,那么EAX将变成-1
/*41A72F*/ SBB EAX,EAX
/*41A731*/ NEG EAX // 如果EAX=0,非0可能是-1了,取反后就返回1

posted @ 2008-09-19 10:52 tqsheng 阅读(810) | 评论 (0)编辑 收藏

IDA stuff (plugins,articles,idc,etc...)

From:CRACKL@B

ftp://nezumi.org.ru/ida.full.zip

http://old.idapalace.net/

http://www.d-dome.net/idapython

http://dkbza.org/data/Introduction%20to%20IDAPython.pdf 

http://www.reconstructer.org/code/ClassAndInterfaceToNames.zip 

http://www.reconstructer.org/code/MFC42Ord2FuncNames.zip 

http://www.reconstructer.org/code/VtablesStructuresFromPSDK2003R2.zip 

http://www.reconstructer.org/code/IDAAPIHelp%20v0.3.zip

posted @ 2008-01-28 22:28 tqsheng 阅读(259) | 评论 (1)编辑 收藏

LIB 概述

http://msdn2.microsoft.com/zh-cn/library/0xb6w1f8(VS.80).aspx

posted @ 2008-01-28 19:57 tqsheng 阅读(104) | 评论 (0)编辑 收藏

链接时如何选择C Run-Time(CRT) library

链接时如何选择C Run-Time(CRT) library
 

在Windows下有六种类型CRTLib(C运行库):
Reusable Library                Switch    Library    Macro(s) Defined
----------------------------------------------------------------
Single Threaded                     /ML       LIBC               (none)
Static MultiThread                  /MT       LIBCMT         _MT
Dynamic Link (DLL)              /MD       MSVCRT        _MT and _DLL
Debug Single Threaded           /MLd      LIBCD          _DEBUG
Debug Static MultiThread        /MTd      LIBCMTD    _DEBUG and _MT
Debug Dynamic Link (DLL)    /MDd      MSVCRTD    _DEBUG, _MT, and _DLL

MT和MD都适用于多线程,其区别是:
MT为静态链接CRT,这样编译出来exe是自包含的,所以会相对大一些,但运行时不用再load CRT库。
MD为动态链接CRT,编译出来exe会小一些,运行时需要load CRT,性能有一点点损失。

任何工程都应该使用同样的CRT Library。即要么都是/ML,要么都是/MTD, 如此类推。

如果一个程序中混合使用不同类型的CRT,有时可以通过link,这样会存在不同CRT的copy,并导致以下问题:
    1)在一个lib中new出来内存,在另一个lib中delete,会crash。
    2)不能在多个lib中共享file handle。
    3)一个lib中设置locale(本地化有关),不能在另一个lib中起作用。

当工程比较大,包含的lib很多,特别当有外部lib(Third party library)存在时,link很容易发生下面这样的错误。
LIBCMTD.lib(chsize.obj) : error LNK2005: __chsize already defined in MSVCRTD.lib(MSVCRTD.dll)
这说明,你的工程使用了不同类型的CRT。这个时候首先一定要坚信这个原则:整个工程用同样的CRT Lib就可以解决问题。然后耐心一一检查每个lib。
如果恰恰某个外部lib用MT,另一个用MD,这个时候就比较痛苦。
    如果有他们源码,就编译一个MT or MD类型的lib,以统一使用一个类型CRT。
    如果没有,那可能只好选择其他的lib。

 原文地址 http://blog.vckbase.com/michael/archive/2005/06/30/9000.html

posted @ 2008-01-28 19:53 tqsheng 阅读(506) | 评论 (1)编辑 收藏

ida plus

http://www.woodmann.net/yates/documents/35.html

posted @ 2008-01-25 12:13 tqsheng 阅读(197) | 评论 (1)编辑 收藏

http://www.ccso.com/faq.html

IDA Pro FAQ

Q How do I generate FLIRT signature from my own libraries ?

A The process is simple if you have installed the FLAIR tools. As an example, we'll use an file called api.lib. First a pattern file should be created. The command

 

plb api(.lib) api(.pat)

creates a pattern file whose format is described in our FLIRT paper. Have a look at this file with a text editor.Then we'll create a signature file with the command

 

sigmake api(.pat) api(.sig)

and copy the resulting api.sig file in the IDA Pro SIG subdirectory.

Q How do I apply my own SIGs to the disassembly ?

A Open the signature window through the View Menu. Press the INS key. Wait a few seconds until the list of available signatures is build. Move the cursor on the line containing your sig file and press the ENTER key.


Q How do I prevent IDA from applying SIGs to the disassembly ?

A Open the IDA.CFG file and modify the ANALYSIS configuration word in order to exclude the AF_FLIRT option.


Q How do I define high level structures ?

 

A See this short tutorial

Q How do I use other processors with IDA Pro ?

 

A You must specify the processor you wish to use on the command line. For example, if you want to disassemble a 8051 binary, IDA Pro should be started as follows (if you are using the Windows version of IDA Pro).

IDAW -p8051


Q How do I use IDC

A see this example


Q How do I load debugging information, MAP or SYM files into IDA ?

 

A The following procedure may be used to load debugging information, MAP and SYM files into a disassembly. This procedure is a temprarory solution, as future versions of IDA will have a built-in support of debugging information.
    Convert debugging information into text using your favorite dumper. (Borland's TDUMP.EXE is a good choice when dealing with Borland and Microsoft debugging information). Load the text into a text editor and convert it into IDC script:
    
                    static main() {
                    MakeName(addr,name);
                    ...........
                    }
                    
    where addr - address should be replaced be the address of the name and name is string constant. Example:
    
                    static main() {
                    MakeName(0x10000,"name1");
                    }
                    
    Launch IDA and execute the script by pressing F2 key. The names from the SYM file will appear in the disassembly.

Q How do I save a fragment of disassembly ?

A Select the area of the disassembly that you want to save and press ALT-F10.


Q How do I view the disassembly in C/VB etc?

A You must create a file called sissy.ini and add the line "LANG_C=Yes". Jokes aside, this is not something IDA can do. The output is ASM.


Q How do I change the search direction ?

A By pressing the TAB key

posted @ 2008-01-25 09:04 tqsheng 阅读(305) | 评论 (0)编辑 收藏

库函数的快速鉴别和识别技术

标 题: 库函数的快速鉴别和识别技术
翻 译: 月中人
时 间: 2006-09-07 15:20
链 接: http://bbs.pediy.com/showthread.php?threadid=31636
详细信息:

 

 

库函数的快速鉴别和识别技术

文档编号:

S002-0017

原作者:

Ilfak Guilfanov [DataRescue.com]

译者:

月中人 [PTG]

审校:

 

发布时间:

2007-01-10

原文标题:

Fast Library Identification and Recognition Technology

关键词:

库函数,鉴别,识别,签名,特征文件

1. 原文版权声明:

(c) Copyright 1997 by Ilfak Guilfanov & DataRescue

Reproduction strictly forbidden

2. 译文字典:

Identification鉴别,即确定一个函数是什么;

Recognition识别,即从程序中分离出属于一个函数的那些代码;

misidentification错误标识

 

 

 

目录

1. 目标

2. 难点

3. 要点

4. 实现

5. 结果

 


1         目标

对于现代高级语言所写的程序,分离库函数所费的时间是反汇编的一个主要障碍。我们会因为没有从中获得新的知识而觉得时间被浪费了:它只是我们深入分析程序和反映其意图的算法之前一个必不可少的步骤。遗憾的是,每次反汇编一个新的程序都不得不重复这一步。

有时,了解一个库函数的类别能相当大地减轻对程序的分析工作。这对于丢弃无用信息可能极有帮助。举例来说,一个处理流数据的C++函数通常与程序的主要算法无关。

请注意一个有意思的现象,每一高级语言程序都使用很多标准库函数,有时甚至达到所有被调用函数的95%是标准函数。使用某个众所周知的编译器编译的“Hello, world!”程序中有:

        库函数        -    58

        函数main()    -    1

当然,这是一个人为的例子,但我们的分析确实显示,真实程序中的函数平均50%是库函数。这就是为什么反汇编器的使用者不得不花费他一半以上的时间来分离出那些库函数。对一个未知程序进行分析类似于解答一个巨大的拼字智力游戏:我们确定的字母越多,就越容易猜测下一个字。反汇编过程中,在一个函数里面注解和有意义名字越多,意味着能够更快地理解它的意图。象OWLMFC以及其他一些标准库的广泛使用,甚至更增加了在目标程序中标准函数所占的成分。

使用现代技术(如,AppExpert或类似的向导)用C++编的一个中等尺寸的Win32程序,会从10002500个库函数中调用任何函数。

我们试图创造一个识别标准库函数的算法来辅助IDA用户。我们希望它是一个实用而且有用的东西,因此必须接受一些限制:

n         我们只考虑用C/C++编写的程序

n         我们不企图达成完美的函数识别:从理论上讲这是不可能的。而且,对某些函数的识别可能导致不良后果。例如,对下面这个函数的识别将会误导以致产生许多错误标识。

                push    bp

                mov     bp, sp

                xor     ax, ax

                pop     bp

                ret

这毫无意义,因为在现代的C++库中你会发现有许多不同名字的函数与它每个字节都完全相同。

n         我们只识别和鉴别位于代码段的函数,而忽略数据段。

n         当一个函数已经被成功鉴别的时候,我们给它指定一个名字和一个最终的注解。我们不打算提供关于函数参数或函数行为的信息。

我们还要求做到以下几点

n         我们努力完全地避免假阳性。我们认为假阳性(一个被错误地鉴别的函数)比假阴性(一个没有鉴别出来的函数)更糟。理想的情况应该是根本不产生假阳性。

n         函数的识别需要你提供最低限度的处理器和存储器资源。

n         由于IDA的多价结构 - 它支持好几十种非常不同的处理器 - 鉴别算法必须是独立于平台的,即它必须适用于为任何处理器编译的程序。

n         应该鉴别main()函数并适当地加以标注,因为我们对库的启动代码不感兴趣。

2         难点

存储器的使用量

识别和鉴别的主要障碍是函数的纯数量及其占用存储空间的大小。如果我们计算所有编译器‘厂商’为不同存储器‘模型’生产的所有库所有‘版本’,所需储存空间动辄成百上千亿字节之巨。

如果我们试图考虑OWLMFCMFC及类似的库,情况更糟。所需要的储存量是极大的。目前,个人电脑用户不可能负担得起划拨成百上千兆字节的磁盘空间给一个简单的反汇编工具程序。因此,我们必须采用某种算法,如此便可以减少识别标准库函数时所需信息的尺寸。当然,由于必须被识别的函数个数多,我们需要一个有效率的识别算法:简单的暴力搜索并非一个可选方案。

 

可变性

另一个的困难在于程序中的‘变数’字节[_variant_ bytes]。有些字节在载入时被修正(被解决),而另一些字节在链接时变成常数,但是大部份变数字节是来自引用的外部名字。在那种情况下,编译器不知道被调用函数的地址,而且保留这些字节的值为零。这种所谓的“修正信息[fixup information]”通常被写到输出文件[output file]的一个表(有时叫做“重定位表”或“重定位信息”)。下例包含有变数字节。

B8 0000s                             mov     ax, seg _OVRGROUP_

9A 00000000se                        call    _farmalloc

26: 3B 1E 0000e                      cmp     bx, word ptr es:__ovrbuffer

 

链接器将会试图解决外部引用,使用所调用的函数的地址替换那些零,但是有些字节仍保留不变:程序中对动态库的引用或者包含绝对地址的那些字节。这些引用只能在载入时由系统装载程序解决。它将会试图解决所有的外部引用而且用绝对地址替换零。当系统装载程序不能够解决某个外部引用时,如同该程序引用一个未知DLL的情况一样,程序就不运行。

某些链接器设置的优化也会让事情变复杂,因为那些固定的字节有时会被改变。例如:

               0000: 9A........        call    far ptr xxx

             被替换成

               0000: 90                nop

               0001: 0E                push    cs

               0002: E8....            call    near ptr xxx

 

程序会照常运行,但是链接器带来的替换确实让我们不可能利用函数模板做逐字节对比。一个程序中变数字节的出现使得不可能利用简单的检验和来识别。如果函数没有包含变数字节,开头N字节的CRC[循环冗余校验码]就足以鉴别和选择在一个散列表中的一组函数。使用这种表会大大减少鉴别所需的信息大小:函数的名字、它的长度以及检验和就够了。

我们已经用例子证明了,识别所有的标准库函数是不可能的,或者甚至是不值得的。还有一个补充证明就是,某些函数是同一的,它们做的事完全相同,只是以不同的方式调用。例如,函数strcmp()fstrcmp()在大规模存储器模型中是同一的。


这里我们遇到一个左右为难的问题:有些函数并非无足轻重,而且将它们标注会对用户有帮助,所以我们不想从识别过程中丢弃这些函数,然而我们无法区别它们。

更具体些:考虑这个

                call    xxx

                ret

        或者

                jmp     xxx

 

乍一看,这些代码片断没有特别之处。问题是它们在标准库中出现,有时数量相当多。Borland C++ 1.5OS/2编译器使用的那些库包含20个这种类型的调用,出现在read()write()等重要的函数中。

对这些函数的无格式比较不会有什么收获。区别那些函数的唯一机会是发现它们调用其他的什么函数。通常,所有的短函数(只由2-3条指令组成)都难以识别,而且错误识别的概率非常高。然而不识别它们是不好的,因为它会导致级联的识别失败:如果我们不识别函数tolower(),我们可能就无法识别引用tolower()strlwr()

 

版权

最后,有一个明显的版权问题:就是一个反汇编器的分发可能不带那些标准库。

 


3         要点

为了解决以上提出的问题,我们创建一个数据库,含有我们想要识别的所有库的所有函数。于是对于反汇编出来的程序每个字节,IDA Pro都检查它能否标志着某个标准库函数的开始。

识别算法所需要的信息保存在一个特征[signature]文件中。每个函数表现为一个模式。模式是一个函数开头的32个字节,其中所有变数字节用符号标记。例如:

558BEC0EFF7604..........59595DC3558BEC0EFF7604..........59595DC3 _registerbgidriver

558BEC1E078A66048A460E8B5E108B4E0AD1E9D1E980E1C0024E0C8A6E0A8A76 _biosdisk

558BEC1EB41AC55604CD211F5DC3.................................... _setdta

558BEC1EB42FCD210653B41A8B5606CD21B44E8B4E088B5604CD219C5993B41A _findfirst

 

其中变数字节显示为“..”,有些函数开始几字节组成的序列相同。因此树结构好象很适合用来储存它们:

558BEC

      0EFF7604..........59595DC3558BEC0EFF7604..........59595DC3 _registerbgidriver

      1E

        078A66048A460E8B5E108B4E0AD1E9D1E980E1C0024E0C8A6E0A8A76 _biosdisk

      B4

        1AC55604CD211F5DC3                                       _setdta

        2FCD210653B41A8B5606CD21B44E8B4E088B5604CD219C5993B41A   _findfirst

 

字节序列保存在树的结点中。在这个例子中,树的根包含序列“558BEC”,三个子树起源于根,分别地以

字节0E1EB4开始。以B4开始的子树生出两个子树。每个子树以叶子结束。关于函数的信息保存在那里(上述例子中只有名字是可见的)。

[注意此处有误:根据之前列出的4个模式来看,直接源于根的只有0E1E两个子树,B4应在1E之后]

树数据结构同时达成两个目标:

n         存储器需求减少了,因为我们在树结点储存几个函数共用字节。当然,节省量与具有相同开头字节的函数数目成比例。

n         它很适合于加快模式速配。将程序中某个特定位置与一个特征文件中所有函数进行匹配所需的比较次数是以函数数目的对数增长。

单独以函数开头的32个字节为基础下结论不是很明智。正如前面所提到的,现代的真实库包含一些以相同字节开始的函数:

558BEC

      56

        1E

          B8....8ED8

                   33C050FF7608FF7606..........83C406

                                                      8BF083FEFF

                    0. _chmod   (20 5F33)

                    1. _access (18 9A62)

 

当两个函数有相同的开头32个字节时,它们被储存在树的同一个叶子中。为了解决这种情况,我们从位置33起开始计算那些字节的CRC16直到遇到第一个变数字节为止。CRC被储存在特征文件中。用于计算该CRC的字节数目也需要保存,因为这个数目因不同函数而不同。在上述例子中,计算_chmod函数(字节3352)的CRC16用了20字节,而_access函数用18字节。

当然,有一种可能性是,第一个变数字节处在33d[十进制数]位置。则用来计算CRC16的字节序列的长度等于零。但实际上,这很少地发生,而且这个算法所引起错误识别的次数非常低。

有时函数有相同的起始32字节模式,而且有相同的CRC16,如下面的例子中

05B8FFFFEB278A4606B4008BD8B8....8EC0

          0. _tolower (03 41CB) (000C:00)

          1. _toupper (03 41CB) (000C:FF)

 

真抱歉:只有3个字节用来计算CRC16,而且两个函数的CRC16是相同的。在这种情况下,我们将会尝试寻找到某个位置,使得同一叶子的所有函数在那里有不同的字节。(在我们的例子中这个位置是32+3+000C[注意数的进制]

但是既使这个方法也不能识别所有的函数。看另外一个例子:

... (这里只显示树的一部分)

                0D8A049850E8....83C402880446803C0075EE8BC7:

                  0. _strupr (04 D19F) (REF 0011: _toupper)

                  1. _strlwr (04 D19F) (REF 0011: _tolower)

 

这些函数在非变数字节处完全相同,而只有它们调用的函数不同。在这个例子中区别两函数的唯一方法是检验偏移11处的那条指令所引用的名字。

最后这个方法有一缺点:函数_strupr()_ strlwr()的正确识别依赖于函数_toupper()_tolower()的识别。这意味着在因为缺少对_toupper()_tolower()的引用而识别失败的情况下,我们必须推迟识别并且稍后在找到_tolower()_toupper()后重做识别。这影响了算法的整体设计:我们需要做第二趟以解决那些被推迟的识别。幸运地,后一趟只应用在程序中少数地方。

最后,你会发现有的函数在非变数字节处完全相同,[而且变数字节]引用相同的名字,但是这些函数被以不同的名字调用。那些函数有相同的实现但有不同的名字。令人惊讶的是,在标准库中,尤其在C++库中,这是常见的情形。

我们称这种情形为‘冲突’,即隶属于同一叶子的一些函数无法通过上述方法相互区别。下面是一个典型的例子:

558BEC1EB441C55606CD211F720433C0EB0450E8....5DCB................

   0. _remove (00 0000)

   1. _unlink (00 0000)

或者

8BDC36834702FAE9....8BDC36834702F6E9............................

   0. @iostream@$vsn            (00 0000)

   1. @iostream_withassign@$vsn (00 0000)

 

人工智能是解决这些冲突的唯一办法。因为当前目标是效率和速度,所以我们决定把人工智能作为该算法的未来发展方向。


4         实现

IDA Pro 3.6版中,该算法的实际实现几乎完全符合以上描述。我们已限制算法只使用于CC++语言编写的程序反汇编,但是未来绝对有可能为其他库编写预处理器[pre-processors]

为每个编译器提供一个单独的特征文件。这样隔离降低了跨编译器的鉴别冲突概率。一个特别的特征文件,名为启动特征文件[startup signature file],被应用于所反汇编程序的入口以确定生成它所用的编译器。一旦得以鉴别,我们就知道应该使用哪个特征文件来反汇编其它部分。我们的算法成功地辨别市场上大多数常用的编译器的启动模块。

既然我们以一个特征文件储存适合于一个编译器的所有函数签名,所以就不必要区别这些编译器的版本和/或库的存储器模型(小的、紧凑的、中等的、大的、极大的)。

对于反汇编文件的每个格式,我们使用专门的启动特征文件。特征文件exe.sig用于MS DOS下运行的程序,而lx.signe.sig用于OS/2下的,等等。

为了降低短函数的错误识别概率,我们必须完全地记录任何一个对外部名字的引用--假如存在这样的引用。这也许会在某种程度上降低函数识别成功的机会,但是我们认为事实证明采用这样的方式是正确的。错误地识别不如不识别。我们创建特征文件时不使用某些短函数(少于4字节),它们不包含对外部名字的引用,而且我们也不企图去识别这类函数。

来自<ctype.h>的函数很短,但它们引用符号类型数组,所以我们决定把对这个数组的引用作为一个例外:我们计算符号类型数组的CRC16,并将它储存特征文件中。

没有人工智能,我们用自然智能解决冲突。特征文件的创作人决定特征文件中函数的取舍。选择函数是很容易的,而且实际上是编辑一个文本文件。

函数的模式不是以它们的最初形式储存在一个特征文件中(即,它们看起来不像例子那样)。我们不使用那些形式的模式,而是储存一些比特数组,这些位用来确定某些字节的改变,即字节的数值被分离储存。因此,除了函数的名字以外,特征文件没有包含来自原始库文件的字节。特征文件的创建包括两个阶段:库的预处理和特征文件的创建。在第一个阶段使用‘parselib’这个程序。它预处理*.obj*.lib文件以产生一个模式文件。模式文件包含函数的模式、它们的名字、它们的CRC16以及创建特征文件所需的所有其他信息。在第二个阶段,‘sigmake’这个程序从模式文件建立特征文件。

分为两个阶段允许sigmake工具程序与输入文件的格式无关。因此未来将有可能为非*.obj*.lib格式的文件编写另外的预处理器。

我们决定压缩(使用InfoZip算法)所创建的特征文件以减少储存它们所需的磁盘空间。

为了方便用户,我们试图尽最大可能识别main()函数。鉴别这个函数的算法因编译器不同而不同、因程序不同而不同。(DOS/OS2/Windows/GUI/Console...)。

该算法作为一个文本字符串被写入一个特征文件中。遗憾的是,我们还不能自动产生该算法。

 


5         结果

事实证明那些特征文件压缩得很好;它们的压缩系数可能大于2。这个压缩率的理由是,一个特征文件大约95%是函数名字。(例子:用于MFC 2.x的特征文件在压缩前是2.5MB,压缩是700Kb。它包含33634个函数名字;平均每个函数用21字节储存。通常,一个库文件尺寸对一个特征文件尺寸的比率从100500不等。

正确识别函数的百分比非常高。我们的算法识别出那个“Hello World”程序中除一个函数之外的所有函数。未被识别的函数只有一条指令:

        jmp     off_1234

 

尤其让我们满意的是没有错误识别。然而这并不意味着错误永远不会发生。请注意,该算法只对函数工作。

数据有时位于代码段,而且因此我们需要把某些名字标记为“数据名”,而非“函数名”。在一个现代的大型库文件中检验所有名字并标记所有的数据名字是不容易的。

我们计划在未来某时实现这些数据名字的标记。


©2000-2007 PEdiy.com All rights reserved.
By PEDIY

posted @ 2008-01-24 12:05 tqsheng 阅读(565) | 评论 (0)编辑 收藏

翻译:向PE中注入代码

     摘要: 标 题: 翻译:向PE中注入代码翻 译: arhat时 间: 2006-04-16 00:53 链 接: http://bbs.pediy.com/showthread.php?threadid=24183 详细信息: Inject your code to a Portable Executable file向PE中注...  阅读全文

posted @ 2008-01-24 12:02 tqsheng 阅读(4371) | 评论 (1)编辑 收藏

从IDA中提取代码做汇编注册机之C/C++篇

标 题: 从IDA中提取代码做汇编注册机之C/C++篇
作 者: laomms
时 间: 2006-12-22 23:22
链 接: http://bbs.pediy.com/showthread.php?threadid=36799
详细信息:

    继上篇那个VB后,我也想做个C/C++类的例子,正好看到FreeSoul写的FScrackme2,所以写了这篇文章。
    FScrackme2应该是在lunix下取了GNU中的 GCC.exe 和 G++.exe来编译的一个C程序! 所以用PEID来看语言类型是MingWin32 GCC 3.x。也就是GCC编译的C程序。

先来分析程序,找到真正的注册码并不难,OD中的算法部分:
00401AE7  |.  8945 9C       MOV [LOCAL.25],EAX                       ; |
00401AEA  |.  837D A0 03    CMP [LOCAL.24],3                         ; |用户名长度必须大于3位
00401AEE  |.  0F8E C2000000 JLE <FScrackm.loc_401BB6>                ; |
00401AF4  |.  837D 9C 1A    CMP [LOCAL.25],1A                        ; |注册码长度必须大于27位
00401AF8  |.  0F8E B8000000 JLE <FScrackm.loc_401BB6>                ; |
...
00401B62  |.  8B45 9C       MOV EAX,[LOCAL.25]
00401B65  |.  894424 0C     MOV DWORD PTR SS:[ESP+C],EAX             ;  注册码长度
00401B69  |.  8B45 A0       MOV EAX,[LOCAL.24]
00401B6C  |.  894424 08     MOV DWORD PTR SS:[ESP+8],EAX             ;  用户名长度
00401B70  |.  8B45 A4       MOV EAX,[LOCAL.23]
00401B73  |.  894424 04     MOV DWORD PTR SS:[ESP+4],EAX             ;  注册码ASCII
00401B77  |.  8B45 EC       MOV EAX,[LOCAL.5]
00401B7A  |.  890424        MOV DWORD PTR SS:[ESP],EAX               ;  用户名ASCII
00401B7D  |.  E8 1C010000   CALL <FScrackm.sub_401C9E>               ;  主要算法
00401B82  |.  85C0          TEST EAX,EAX
00401B84  |.  75 18         JNZ SHORT <FScrackm.loc_401B9E>          ;  判断返回值是否为0


================================================================================================
CALL <FScrackm.sub_401C9E>  主要算法:

00401D33 > > \8B45 0C       MOV EAX,DWORD PTR SS:[EBP+C]            
00401D36   .  8038 4E       CMP BYTE PTR DS:[EAX],4E                 ;  对比第一位注册码是否为“N”
00401D39   .  74 0F         JE SHORT <FScrackm.loc_401D4A>
00401D3B   .  C785 4CFFFFFF>MOV DWORD PTR SS:[EBP-B4],1
00401D45   .  E9 7E070000   JMP <FScrackm.loc_4024C8>
00401D4A > >  8B45 0C       MOV EAX,DWORD PTR SS:[EBP+C]            
00401D4D   .  40            INC EAX
00401D4E   .  8038 61       CMP BYTE PTR DS:[EAX],61                 ;  对比第二位注册码是否为“a”
00401D51   .  74 07         JE SHORT <FScrackm.loc_401D5A>
00401D53   .  C745 94 00000>MOV DWORD PTR SS:[EBP-6C],0
00401D5A > >  8B45 0C       MOV EAX,DWORD PTR SS:[EBP+C]            
00401D5D   .  83C0 02       ADD EAX,2
00401D60   .  8038 52       CMP BYTE PTR DS:[EAX],52                 ;  对比第三位注册码是否为“R”
00401D63   .  74 0F         JE SHORT <FScrackm.loc_401D74>
00401D65   .  C785 4CFFFFFF>MOV DWORD PTR SS:[EBP-B4],1
00401D6F   .  E9 54070000   JMP <FScrackm.loc_4024C8>
00401D74 > >  8B45 0C       MOV EAX,DWORD PTR SS:[EBP+C]           
00401D77   .  83C0 03       ADD EAX,3
00401D7A   .  8038 46       CMP BYTE PTR DS:[EAX],46                 ;  对比第四位注册码是否为“F”
00401D7D   .  74 0F         JE SHORT <FScrackm.loc_401D8E>
00401D7F   .  C785 4CFFFFFF>MOV DWORD PTR SS:[EBP-B4],1
00401D89   .  E9 3A070000   JMP <FScrackm.loc_4024C8>
...
00401E8B   .  83C0 04       ADD EAX,4
00401E8E   .  0FB600        MOVZX EAX,BYTE PTR DS:[EAX]
00401E91   .  3A45 87       CMP AL,BYTE PTR SS:[EBP-79]              ;  对比第5位开始5位注册码
00401E94   .  74 0F         JE SHORT <FScrackm.loc_401EA5>

...

00401EEA   .  83C1 09       ADD ECX,9
00401EF6   .  0FB601        MOVZX EAX,BYTE PTR DS:[ECX]
00401EF9   .  3A02          CMP AL,BYTE PTR DS:[EDX]                 ;  对比第10位注册码
00401EFB   .  74 0F         JE SHORT <FScrackm.loc_401F0C>
...
00401F4A   .  83C2 0A       ADD EDX,0A
00401F4D   .  8B85 70FFFFFF MOV EAX,DWORD PTR SS:[EBP-90]            ;  对比第11位注册码...
00401F8B   .  E8 30420000   CALL <FScrackm.isalpha>                  ; 第12位后6位注册码,必须为字母
..
00401FB5   .  E8 F6410000   CALL <FScrackm.isupper>                  ; 第12位后6位注册码,必须大写
...
0040205F   .  83C0 0A       ADD EAX,0A                               ;  第10位后
00402075   .  0FBEC0        MOVSX EAX,AL
00402078   .  39C1          CMP ECX,EAX                              ;  对比第11位开始3位的注册码
0040207A   .  74 0F         JE SHORT <FScrackm.loc_40208B>
...
004020B1   .  0FBEC0        MOVSX EAX,AL
004020B4   .  39C1          CMP ECX,EAX                              ;  对比第17位开始倒数3位的注册码
004020B6   .  74 44         JE SHORT <FScrackm.loc_4020FC>
...
00402125   .  83C0 11       ADD EAX,11                               ; 第17位后
00402128   .  0FBE00        MOVSX EAX,BYTE PTR DS:[EAX]              
0040212B   .  890424        MOV DWORD PTR SS:[ESP],EAX               
0040212E   .  E8 6D400000   CALL <FScrackm.isdigit>                  ; \第18位开始的6位必须为数字
00402133   .  85C0          TEST EAX,EAX
00402135   .  75 0F         JNZ SHORT <FScrackm.loc_402146>
...
0040220D   .  0FB601        MOVZX EAX,BYTE PTR DS:[ECX]
00402210   .  3A02          CMP AL,BYTE PTR DS:[EDX]                 ;  对比第18位后的6位注册码
00402212   .  74 0F         JE SHORT <FScrackm.loc_402223>
...
00402230   .  83C0 17       ADD EAX,17
00402233   .  8038 2D       CMP BYTE PTR DS:[EAX],2D                 ;  对比第24位注册码,必须为“-”号
00402236   .  74 07         JE SHORT <FScrackm.loc_40223F>
...
004022C7   .  83C1 18       ADD ECX,18
004022CA   .  8B85 58FFFFFF MOV EAX,DWORD PTR SS:[EBP-A8]
004022D0   .  0FBE10        MOVSX EDX,BYTE PTR DS:[EAX]
004022D3   .  0FB601        MOVZX EAX,BYTE PTR DS:[ECX]
004022D6   .  3A842A 78FFFF>CMP AL,BYTE PTR DS:[EDX+EBP-88]          ;  对比第25位注册码
004022DD   .  74 0F         JE SHORT <FScrackm.loc_4022EE>
...
00402474   .  83C2 19       ADD EDX,19
00402477   .  8B85 50FFFFFF MOV EAX,DWORD PTR SS:[EBP-B0]
0040247D   .  3802          CMP BYTE PTR DS:[EDX],AL                 ;  对比第26位注册码
0040247F   .  74 07         JE SHORT <FScrackm.loc_402488>
00402481   .  C745 94 00000>MOV DWORD PTR SS:[EBP-6C],0
00402488 > >  8B45 0C       MOV EAX,DWORD PTR SS:[EBP+C]             
0040248B   .  83C0 1A       ADD EAX,1A
0040248E   .  8038 44       CMP BYTE PTR DS:[EAX],44                 ;  对比第27位注册码是否为“D”
00402491   .  74 07         JE SHORT <FScrackm.loc_40249A>
...
0040249A > > \837D 14 1B    CMP DWORD PTR SS:[EBP+14],1B             ;  对比注册码长度是否为27位
0040249E   .  74 0C         JE SHORT <FScrackm.loc_4024AC>

==================================================================================================


    我们主要是讨论写注册机,所以算法具体不去讨论。我们现在所知道的是,用户名要大于三位,注册码必须为27位,而且我们知道了每个注册码比较的地方。CALL 00401C9E里面是具体的算法,里面还有很多子函数,想提取这样一个含有很多子程序的代码到注册机里在OD中好象没有好的方法,只有手动提,而在IDA中就有办法,这得益于最早由dreaman写的提取函数的脚本,我称它为GetCall.IDC(http://bbs.pediy.com/showthread.php?s=&threadid=23231),在IDA中,只要将光标定位于一个函数的起始代码,运行脚本后,它会截取所有该函数的代码,包括里面所有的子函数代码,并在文件目录保存汇编代码为一个ASM文件。真是太方便了。让我们行动。
IDA反汇编FScrackme2,“G”到401C9E,菜单“文件”-“IDC文件”,运行脚本。FScrackme2目录中已经生成了FScrackme2Part.asm。但是在这之前,对于这个软件,需要在IDA中动动手脚。因为软件是GCC写的,IDA的脚本gcc32rtf.sig自动把GCC的库函数识别出来了:
.text:00401DCF                 call    __Znaj

    这本来是个好事,我也想找GCC的运行库,然后将它转换成MASM的库文件,这样,这些CALL就可以直接在MASM中引用了,写注册机就非常方便了。但是找不到这样的运行库。而GetCall.IDC脚本只要碰到被IDA识别出来的函数,它就不进去拷里面的子函数,所以,解决方法只有一个,将IDA中的SIG文件夹下的gcc32rtf.sig文件删除,再让IDA反汇编FScrackme2,再拷CALL 401C9E里的代码。这样拷出来的的子函数就比较全了。(当然我也把所有的GCC的库函数提取出来做个备份,谁需要的话跟我联系。)
现在就已经把代码全部拷出到FScrackme2Part.asm了,我们不管三七二十一先把这段汇编代码拷到一个汇编注册机模板里再说,就是上次那个模板,当然这样在RADASM中运行,错误会非常多,有错就改嘛,中国人的好作风。
因为是C语言类的,所以不可避免的用到C运行库msvcrt.dll。所以看到汇编代码中很多对msvcrt的调用,比如:
.text:00401F8B                 call    isalpha
.text:00401D14                 call    isalnum

    先解决这个问题。有了上篇的文章,这已经不是问题了,用Dll2inc将msvcrt.dll转换为MASM的msvcrt.lib和msvcrt.inc文件,然后在汇编代码中调用这两个文件。这样就可以直接在汇编中调用msvcrt的库函数了。
然后继续让RADASM找出错误,一步一步来解决,一般的方法是将RADASM中的错误提示地址复制出来,在IDA中G到该地址,看数据是属于哪个段的,如果是.text可执行代码段的子函数,就用GetCall脚本复制出来粘贴到汇编代码区,如果是.bss、.rdata、.data 等数据段,就拷贝到汇编代码的.DATA区,LINUX中还会出现.stab .stabstr .comment .note .shstrtab .symtab .strtab,不过这些都没用。我想在下篇结束篇专门写如何从IDA中拷代码,结合具体实例讲的详细些。在这里,拷好的代码就看附件中的注册机代码。
拷好并修改好后这俨然是用汇编代码仿制FScrackme2,但是文件大小比原来小了N倍,执行速度也快了不少。
做成个仿制FScrackme2还不行,我们是要做它的注册机。
很多crackme都可以通过用户名算出个结果,然后将注册码也通过运算得到这个结果,那就可以通过注册码算法的逆运算推出真实的注册码。
但是这个crackme是个正向的,它就是通过运算得到一个个注册码,然后将输入的注册码也一一对比。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
对于这种crackme写注册机时我提供一种解决思路,就是制造一个“伪注册码”,然后将程序里算出的一个个真实的注册码重新存到一个地方,这个地方的字符串就是真的注册码了。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    首先说伪注册码,只要符合条件的任何字符都行。我这里为了减少弯路,根据软件的要求:第12到17位必须为大写字母,第18位和第24位必须为数字,我提供个名称为TEMP的伪注册码:12345678901ABCDEF123456789D:
.data
Temp            db '12345678901ABCDEF123456789D',0

我将一个个真实的注册码存到SerialBuffer中:
invoke SetDlgItemText,hDlg,IDC_SERIAL,addr SerialBuffer

其次要将所有对比注册码的地方都改为移动真实的注册码到SerialBuffer,并且将那些对比发生错误跳转到退出的地方都“爆掉”,相当于先爆破再复制代码:
cmp  byte ptr [eax],  4Eh                  ;对比第一位注册码是否为“N”
mov      [SerialBuffer+0],4Eh                 ;将注册码第一位“N”写回
jmp  short loc_401D4A                     ;避免退出,原来是je  401D4A    

再次要注意原来程序中循环取输入的注册码进行对比的地方,我们要将它改为循环存储到SerialBuffer中,这就需要个累计参数,可以利用原程序中循环的时候对比是否循环完的那个参数,用那个非常爽:
mov  [ebp+var_8C], 1

cmp  [ebp+var_8C], 6                                                ;是否取完
….
mov  al,  [edx]                              ;对比第18位后的6位注册码
xor      ecxecx
mov      cl,  byte ptr [ebp+var_8C]          ;循环中的对比参数
mov      [SerialBuffer+ecx+10h],al               ;写回第18位后的6位注册码
jmp  short loc_402223                ;避免退出
mov  [ebp+var_B4], 1
jmp  loc_4024C8                      ;跳到退出

lea  eax, [ebp+var_8C]
inc  dword ptr [eax]                 ;累计

最后还有一点,就是用前面已经计算出来的正确注册码来推算后面部分的注册码,这时,不能伪注册码来推算了,因为它本身是错误的,所以推算出来的后面部分也是错的,要替换成真实的注册码,如:
mov  eax, [ebp+arg_4]  ;注意这里,这里是将已算出的注册码第23位拿来运算,所以要改成:
mov eaxoffset SerialBuffer  ;将已经算出的注册码放到EAX,当然也包括已算出的第23位
mov  [esp+0C8h+var_C8], eax
call  sub_4024FE

然后我们来看看原来程序取用户名和注册码的过程,我们可以替换成取用户名和伪注册码:
00401B65  |.  894424 0C     MOV DWORD PTR SS:[ESP+C],EAX             ;  注册码长度
00401B69  |.  8B45 A0       MOV EAX,[LOCAL.24]
00401B6C  |.  894424 08     MOV DWORD PTR SS:[ESP+8],EAX             ;  用户名长度
00401B70  |.  8B45 A4       MOV EAX,[LOCAL.23]
00401B73  |.  894424 04     MOV DWORD PTR SS:[ESP+4],EAX             ;  注册码ASCII
00401B77  |.  8B45 EC       MOV EAX,[LOCAL.5]
00401B7A  |.  890424        MOV DWORD PTR SS:[ESP],EAX               ;  用户名ASCII
00401B7D  |.  E8 1C010000   CALL <FScrackm.sub_401C9E>               ;  主要算法
00401B82  |.  85C0          TEST EAX,EAX
00401B84  |.  75 18         JNZ SHORT <FScrackm.loc_401B9E>          ;  判断返回值是否为0

只要改为:
invoke lstrlenaddr Temp                          ;取伪注册码长度
mov  [esp+98h+var_8C], eax      
invoke lstrlenaddr NameBuffer                    ;取用户名长度
mov  [esp+98h+var_90], eax             
mov  [esp+98h+var_94], offset Temp              ;装入伪注册码
mov  [esp+98h+var_98], offset NameBuffer        ;装入用户名
call  sub_401C9E                                 ;调用算法CALL
test  eaxeax
jnz  loc_401B9E

    我们还可以利用原来注册码错误的对话框的地方来提示对用户名各种限制,比如要求第一位和最后一位必须是字母等。
    这样弄好后,到最后一位注册码算好,所有的真实注册码也就已经全部存到SerialBuffer中了。
    如果还有错误,最好的办法就是在RADASM中用OD调试跟踪错误。
    注册机的源代码我已经放在附件里,最好结合原程序看看。注册机里我已经做了比较详细的注释了。
    注册机中的全部代码都拷自FScrackme2在IDA中反汇编后的代码,也就是几乎全部是算法函数CALL 401C9E里的代码。我尽量保持“原汁原味”,虽然有些代码完全可以删除,但是直接拷贝比判断该删除哪些要好。
    主要就是加入了存储真实注册码的过程,看看每个注册码的存储方法,慢慢体会。

附件:单击下载

【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年12月24日
                                                                                                                       


©2000-2007 PEdiy.com All rights reserved.
By PEDIY

posted @ 2008-01-24 12:00 tqsheng 阅读(1858) | 评论 (0)编辑 收藏

黑客反汇编揭密(中文版).pdf

http://www.shubulo.com/thread-34984-1-1.html

posted @ 2007-12-18 08:46 tqsheng 阅读(1775) | 评论 (6)编辑 收藏

真假盒装的鉴别

我最近买了2个酷睿~才发现的现在寿光的卖电脑 href=http://detail.zol.com.cn/desktop_pc_index/subcate27_list_1.html style=text-decoration:underline target=_blank>电脑的奸商实在是太多了~我没数码相机否则就给大家把照片发上来!现在详细的说一下~希望给大家有所帮助!真盒:封口的标签字迹非常清晰~并且背景是带有金属光泽的就是类似磨沙的会反光的银色!假盒背景只是简单的银色没有金属颗粒感!认证标签左边都有防伪全息图~为一个穿戴类似阿拉伯带风镜的人站着用双手去触摸一个球体并且INTEL的标志是横排列的还有这个标签的背景是类似线路板样步线一样的的线条假的却没有~~假的却非常模糊基本看不出人~~INTEL的标志中的E是下沉的~真盒是这样写的core~tm ~2 Duo Processor E4400.LEGA775 Pkg 假盒没有TM标志~并且字迹也是异常模糊~真盒2.0 GHZ.2MB Cache.800MHZ FSB 假盒直接没有这些字~总之假盒字迹非常模糊~但是一般没有对比难发现!!我自认为精通了都买第2块时才发现其中的奥秘~还有真盒的风扇上的标签INTEL后有R的标志假的就没有~并且真盒风扇比假盒沉一点点~!!真的条形码颜色深~假的颜色非常浅!!真盒标签右上角的钥匙图形浅蓝色的有金属感并且有点反光有凹凸感~而假的就是深蓝色~不发光也没有凹凸感!!!!还有风扇的托架金属是那种带有黑色感觉的~假的就是只发亮的!买CPU是还要看步进~~就是所谓的MO和DO!MO的是好的DO是的老的!!还有外包装盒上所有酷睿的标志都是特殊印刷的非常亮~并且颜色鲜明~都凸出盒面一点!这里所说适用于现在所有的酷睿系列CPU!请大家以后多加注意~因为原装后封和原装原封的价格是差100多元的~而且风扇也没原装原封的好!请大家互相转告!!!别让这个帖子沉下去!我现在所知道的就是这些了~大家有更详细的可以继续添加!!zol

posted @ 2007-12-13 12:45 tqsheng 阅读(134) | 评论 (0)编辑 收藏

仅列出标题
共25页: First 17 18 19 20 21 22 23 24 25