九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

手动脱ASPack壳


手动脱ASPack

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

个人手动脱壳定义,不是完全不用工具,仅仅是指不用脱壳机,并且手动寻找OEP,恢复IAT的时候使用ImportREC,但是手动找到IAT的位置,不用自动搜寻功能,其实找到了位置后,ImportREC还是做了新添加一个段,拷贝2进制数据,修改PE头中的IAT偏移地址这种工作,因为重复性太高,不手动进行了。

这些自然不是最佳,最快脱壳的方式,仅仅是学习。。。。。

需要用到的工具有OllyDbg(用于调试),LordPE(用于DumpLordPEDump似乎比OllyDbgDump插件更稳定,因为好像OlldyDbgDump插件还尝试做了一些其他工作),ImportREC(恢复IAT)

压缩壳总是容易脱的,作为脱壳练手,再来一个著名的壳,ASPack

我用的版本是212,测试对象是用其将windows的计算器程序打包。

获取OEP

ASPack获取OEP就没有UPX那么简单了,当然,其实也还是不难,毕竟是压缩壳嘛。而且我尝试过压缩简单的程序(比如以前用汇编写的那个HelloWorld,几乎不会改变代码段,仅仅加密了IAT),因为calc.exe还算是一个正规的程序,所以体积不算很小,然后可以达到50%以上的压缩率,代码段自然也破坏了。

我首先用的是一种很笨的办法,直接通过段之间的跳转来定位,但是因为ASPack相当的狡猾。。。。它不是一个一个段的解压而是交替进行解压,导致的结果就是我没有办法通过在一段内存上面设一次断点,也没有办法通过先在数据段设断,然后再在代码段设断的方式获取OEP,最后只能用蛮办法,用OllyDbgRUN跟踪,设置跟踪中断条件为EIP执行到代码段,然后跟踪步进。。。。。。即便这样,中途ASPack竟然还特意到代码段Ret2次,呵呵,无耻啊。当然,最后通过这种办法,耗时超久,才找到了OEP.OllyDbg跟踪的指令条数在500W以上。。

01012475   .  6A 70         PUSH    70

01012477   .  68 E0150001   PUSH    复件_cal.010015E0

0101247C   .  E8 47030000   CALL    复件_cal.010127C8

01012481   .  33DB          XOR     EBX, EBX

01012483   .  53            PUSH    EBX                              ; /pModule => NULL

01012484   .  8B3D 20100001 MOV     EDI, DWORD PTR [1001020]         ; |kernel32.GetModuleHandleA

0101248A   .  FFD7          CALL    NEAR EDI                         ; /GetModuleHandleA

 

第一句就是入口

然后再通过堆栈平衡的方法来找OEP,结果不需要一秒钟就能找到,简直崩溃。

方法是在外壳通过PUSHAD保护现场后(很多外壳第一句都这样)

查看堆栈

0006FFA4   7C930208  ntdll.7C930208

0006FFA8   FFFFFFFF

0006FFAC   0006FFF0

0006FFB0   0006FFC4     -- 原有ESP

0006FFB4   7FFDB000

0006FFB8   7C92E4F4  ntdll.KiFastSystemCallRet

0006FFBC   0006FFB0

0006FFC0   00000000

 

然后根据堆栈平衡条件,在6FFA4下访问中断(一般的情况是在这里调用了POPAD了),或者在在6FFC4(原来的ESP-4=6FFC0处(其实也可以通过PUSHAD压入8个寄存器算到,6FFA4+(8-1)*4=6FFC0)下写中断(一般的情况是POPAD后,通过PUSH 地址,然后RET返回原有代码段),这样只要壳没有特意的隐藏OEP,一般一次下断就能获取OEP

 

恢复IAT

根据

010124E3   .  FF15 0C120001 CALL    NEAR DWORD PTR [100120C]         ;  msvcrt.__set_app_type

一句,可以找到100120C属于IAT的一部分。然后用ImportRec恢复就好了,不多说了。

 

非加密壳,还是比较容易脱的,当然,写就没有那么容易了。。。。

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

女朋友用Python实现的猜数字游戏:)

1 from random import randint
 2
 3 def ran():
 4     return randint(1,100)
 5

 6 c=ran()
 7

 8 # Guess
Number

 9 # Writen By
小乖乖

10 count=0
11
while(count<5):
12     
13
    a=input()
14
15
    if a>c:
16         print "too large ,try again"
17
    elif a<c:
18         print "too small, again"
19
    else:    
20
        print "good
,right
"
21         print "count number
times=
",count+1
22         break
23
    count=count+1
24 else:
25     print "answer=",c
26     
27

阅读全文....

C++中字母大小写转换实现的优化

 

 

C++中字母大小写转换实现的优化

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie

讨论新闻组及文件

在本文中全部以转换为小写为例。

从推荐复用代码的角度来看,用库函数是不错的办法:

方案一:

 char gc1[53] = "abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ";

 

void wayOne()

{

    strlwr(gc1);

}

 

优点是使用方便,别人看着也容易理解,但是效率慢的让人吐血。

extern "C" char * __cdecl _strlwr (

        char * string

        )

{

    if (__locale_changed == 0)

    {

        char * cp;

 

        /* validation
section */

        _VALIDATE_RETURN(string != NULL,
EINVAL, NULL);

 

        for (cp=string; *cp; ++cp)

        {

            if
('A' <= *cp
&& *cp <= 'Z')

                *cp
+= 'a' - 'A';

        }

 

        return(string);

    }

    else

    {

        _strlwr_s_l(string, (size_t)(-1),
NULL);

        return string;

    }

}

 

循环中平均2.5次的判断,(*cp一次,ifA<=一次,*cp<=版次)加平均每次0.5次的加法,虽然这样的转换O(n)是必不可少的,但是对于这样多的操作还是慢的可怕。

2

char gc2[53]
= "abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ";

namespace MYTEST

{

    inline char*
strlwr(char
*asz)

    {

       for(char*
lp = gc2;
*lp != 0; ++lp)

       {

           *lp |= 0x20;

       }

 

       return asz;

    }

}

 

 

void wayTwo()

{

    MYTEST::strlwr(gc2);

}

此例中利用了ASCII字母值的特点,一共只有一次判断(*lp=0,一次位或操作。算法上提高了很多:)其实已经达到了1/3的效率提升。。。。。

将原来一大堆的代码,转化成了反汇编只有4句的程序:

00401020 80 08 20         or          byte ptr [eax],20h

00401023 83 C0 01         add         eax,1

00401026 80 38 00         cmp         byte ptr [eax],0

00401029 75 F5            jne         wayTwo+10h (401020h)

但是考虑到char只是1个字节,看到

00401020 80 08 20         or          byte ptr [eax],20h

一句都感觉不爽,白白浪费了eax 这样4个字节的寄存器,于是可以这样优化:

namespace MYTEST2

{

    inline char*
strlwr(char
*asz)

    {

       long* lp
= (long*)gc3;

       for(; *((char*)lp) != 0; ++lp)

       {

           (long)(*lp) |= 0x20202020;

       }

 

       for(char*
lpc = (char*)lp;*lpc!=0; ++lpc)

       {

           *lpc |= 0x20;

       }

 

       return asz;

    }

}

 

说实话,。。。。。。。。。。。没有任何清晰性可言,没有任何可读性可言,但是优化的思想就是充分的利用4个字节的寄存器,并且以DWORD来读取内存,这是很有效率的方式。汇编代码其实比C语言代码更加清晰,原因在于C语言代码还需要处理大量与类型相关的事情,汇编代码不需要。

第一个循环汇编代码如下:

00401040 81 08 20 20 20 20 or          dword ptr [eax],20202020h

00401046 83 C0 04         add         eax,4

00401049 80 38 00         cmp         byte ptr [eax],0

0040104C 75 F2            jne         wayThree+10h (401040h)

将循环次数减少了3/4。。。。所以效率的优化还是很明显的。单指令多数据操作的思想不过就是这种思想的延生罢了。。。呵呵,但是说在前面,如此影响可读性的效率优化,除非在很必要的情况下,不然慎用。。。。。

为了证实效率的优化,起码也得给出一个测试结果给大家看看吧,不然还以为我胡扯了。

 

void wayOne()

// Hit Count          : 1

// Time               : 5553.00

// Time with Children : 5553.00

{

       strlwr(gc1);

}

 

void wayTwo()

// Hit Count          : 1

// Time               : 247.00

// Time with Children : 247.00

{

       MYTEST::strlwr(gc2);

}

 

void wayThree()

// Hit Count          : 1

// Time               : 180.00

// Time with Children : 180.00

{

       MYTEST2::strlwr(gc3);

}

 

int _tmain(int argc, _TCHAR* argv[])

// Hit Count          : 1

// Time               : 6836996435.00

// Time with Children : 6837002415.00

{

       wayThree();

       wayTwo();

       wayOne();

}

 

测试结果为AQtime5测试数据,单位为机器周期,因为结果已经很明显了,所以没有进行多次循环的测试。并且为了排除缓存的影响,将最快的放在了最前面,那么哪怕有缓存的影响,对于wayThree也是最不利的才对。库函数的5000多的结果,说慢的可怕并不为过。在数据量很大的时候,这种优化的差异可不是一点点而已。

 

write by 九天雁翎(JTianLing)
-- www.jtianling.com

 

阅读全文....

调试用Python C API 写的程序问题还真多,关于import搜索路径的,复制过来,以防忘记

其实本地的import调试倒是感觉没有什么问题,但是一旦通过网络的序列化,然后再反序列化出来,PyImpor模块的时候,再报模块不存在,就很让人郁闷了,刚开始根本就不知道是什么问题,本地测好的程序,怎么一到了网络环境就出问题了呢?郁闷啊。找了半天,才发现是自己写的扩展库在本地的路径没有找到,而实际上Python API报的错误根本就不靠谱,唉。。。。。。。。。。。吃一堑长一智啊。。。。

 

原帖位置:

http://groups.google.com/group/python-cn/browse_thread/thread/f4e2b245147182ad

 

1、可以修改python的环境变量PYTHONPATH
2、可以在程序里动态的修改sys.path
import sys
# 获得当前文件的路径并添加到sys.path
sys.path.append(os.getcwd())
import other_python

阅读全文....

偏偏给我碰到Python C API的Bug?Py_Initialze()直接死循环

 今天在一个程序中,在不同的两个函数,先用Py_Initialize()初始化,然后用Py_Finalize()结束,在本机都调试没有问题,然后将DLL发给同事后,老是在Py_Initialize()进入死循环,莫名其妙!怎么调试都没有用,奇怪了,然后将前一次的Py_Finalize()和后一次Py_Initialize()的取消,也就是仅仅经过一次的Py_Initialize(),Py_Finalize()就完全没有问题。

这点也很奇怪,毕竟本机调试是没有任何问题的,在同事那里虽然换成了多线程环境,但是也没有道理总是必在Py_Initialize()死掉吧?看了Python的文档,才发现Py_Finalize()函数后有这么一段

 

Bugs and caveats: The destruction of modules and objects in
modules is done in random order; this may cause destructors (__del__() methods) to fail when they depend on other objects
(even functions) or modules. Dynamically loaded extension modules loaded by
Python are not unloaded. Small amounts of memory allocated by the Python
interpreter may not be freed (if you find a leak, please report it). Memory tied
up in circular references between objects is not freed. Some memory allocated by
extension modules may not be freed. Some extensions may not work properly if
their initialization routine is called more than once;
this can happen if an
application calls Py_Initialize() and Py_Finalize() more than once.

 

难道是这个问题?我这里也不是extensions不工作的问题啊。。。。。。。或者还是多线程的问题?莫名奇妙啊。。。。。。

 

这里顺便记个关于Python在对线程环境下工作的文章,明天去好好研究一下是什么问题。

C++调用PythonAPI线程状态和全局解释器锁

http://blog.csdn.net/marising/archive/2008/10/08/3032278.aspx

 


阅读全文....

Python C API 使用心得

Python C API 使用心得

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

工作的变化简直和人生变化一样不可预知.就在反外挂刚开始的时候,天天看的都是汇编和一大堆只有cracker(或想称为cracker)的人才会看的破解相关书籍,但是才过了几天,准备做被动的反外挂系统后,工作基本上就转到了用Python C API来写扩展模块和用Python来写Check程序了,真是突然。

8月份看完《Programming in LUA》以后,对脚本语言重拾兴趣,后来断断续续也算把《Python核心编程》,这本来是非常纯粹的Just for fun的事情,最后确还是免不了染上工作的性质,呵呵,这样说好像很奇怪,应该是说,凭借着一点兴趣,花了很多业余时间去学习的Python, 想不到竟然能这么快就在工作中有所应用与发挥,实话说,做老板的,肯定希望有这样的员工。。。。。何况,在老板心中我还算是新员工呢。我要是老板,我也希望做一个新东西前,本来说要调研调研的。。。。结果已经有员工用业余时间已经做过了,然后可以直接开工,唉。。。。我不知道碰到这样的情况我为什么会叹 气。。。。。。。

 

其实以前学的主要是Python语言的语法,用Python C API来写扩展模块还真没有写过,要是用LUA的话,虽然特别复杂,但是可能好歹还有个底。最后几乎是纯粹靠文档来解决了一切,(还有老总提供的一些没头没尾的例子)不要问我为什么不上网查,资料太少,而且在公司上网并不方便。

这里总结一下,以防忘记(我并不准备详细记录下每个步骤,仅仅记要点,假如需要详细步骤的,请参看Python document中的例子)

一:用C APIPythonC语言函数,以方便Python中调用,这种方法还是很简单的,和LUA C API的方法基本一样。(参看文档的Extending Python with C or C++部分)

1.  首先实现一个特定原型的函数,用Python C API来实现的话,所有函数必须是这种原型。必须是类似这样的

PyObject *Fun(PyObject *self, PyObject *args)

self应该是在用类的时候才会用到(我没有用到),args就是函数的参数。因为args是一个PyObject*类型(可以代表Python语言中的任何类型)

2.      将参数转换成C 语言表示的内容,用PyArg_ParseTuple函数。

3.      执行完需要的操作后,也必须返回一个PyObject*类型的值。通过Py_BuildValue函数来构建。

这里要说的是,假如希望返回一个Tuple类型的值,可以先用

PyObject *tuple = Py_BuildValue("(iis)", 1, 2, "three");

形式来构建,假如很多的话,可以用下面的方式来构建

PyObject *t;

 

t = PyTuple_New(3);

PyTuple_SetItem(t, 0, PyLong_FromLong(1L));

PyTuple_SetItem(t, 1, PyLong_FromLong(2L));

PyTuple_SetItem(t, 2, PyString_FromString("three"));

这一点在刚开始开工的时候迷惑了很久。

4.      将要输出的所有函数放入一个数组中,数组的结构是:

struct PyMethodDef {

    const char    *ml_name/* The name of the built-in function/method */

    PyCFunction  ml_meth;   /* The C function that implements it */

    int       ml_flags; /* Combination of METH_xxx flags, which mostly

                 describe the args expected by the C func */

    const char    *ml_doc;   /* The __doc__ attribute, or NULL */

};

数组以{NULL, NULL}结束

5.  构造一个Python import时初始化的函数

类似

PyMODINIT_FUNC

initexample(void)

{

    Py_InitModule("example", example_methods);

}

这里有个特别需要注意的是,初始化函数名字有严格要求,init后面必须跟模块名,否则Python找不到确定的函数会报没有初始化函数的错误

 

总结一下,记得第一次学习LUA C API接口的时候,碰到类似的扩展模块写法,还是感觉很奇怪,“怎么搞这么复杂啊”。。。。。当时为了实现一个简单的扩展都是折腾了很久,到了再一次碰到类似的问题,(Python C API写扩展的时候几乎就是和LUA C API一样),基本已经轻车熟路了。这里突然也想说一句,就算不是真的写非常底层的接口,但是用的接口多了,都是能有些感觉的。

 

扩展模块写完后,编译成动态库(Python要求此动态库名字为pyd,实际就是改个后缀而已)。就可以直接在Python脚本中用import的方式加载了,对于使用来说,根本不需要知道此库是用C API扩展写的还是直接用Python语句写的(这点Lua做的也是一样好)

最后,python的源代码中附带了一个叫做example_nt的例子,可以参考一样,完整的扩展代码如下:

#include "Python.h"

 

static PyObject *

ex_foo(PyObject *self, PyObject *args)

{

    printf("Hello, world/n");

    Py_INCREF(Py_None);

    return Py_None;

}

 

static PyMethodDef example_methods[] = {

    {"foo", ex_foo, METH_VARARGS, "foo() doc string"},

    {NULL, NULL}

};

 

PyMODINIT_FUNC

initexample(void)

{

    Py_InitModule("example", example_methods);

}

 

 

二.C语言中调用Python语句

首先,void Py_Initialize()用来初始化,void Py_Finalize()用来结束Python的调用,这是必须要的。

燃火分两种情况,假如仅仅是几条语句的话,那么以PyRun_为前缀的一些函数都很好用,比如

int PyRun_SimpleString(const char *command)

函数就可以直接执行一条char*Python语句。

需要获得返回值得话

PyObject* PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)

也很好用,以上两个函数用来处理Python源代码已经读入内存的情况,在文件中的时候

int PyRun_SimpleFile(FILE *fp, const char *filename)

PyObject* PyRun_File(FILE *fp, const char *filename, int start, PyObject *globals, PyObject *locals)

使用类似。不多讲了。

假如是个模块的话(比如一个函数),希望在C语言中调用的话那么使用起来就稍微复杂了一点。这种情况的需要在于你可以从C语言中向Python函数中传入参数并且执行,然后获取结果。

此处又分为几种情况:

在文件中,在内存中,编译过的,源代码。

在文件中都很好解决,和上面一样。这里主要讲在内存中的情况。(事实上我工作中需要并且耗费了很长时间才找到解决方法的就是这种情况)

未编译时:(也就是源代码)

1.通过

PyObject* Py_CompileString(const char *str, const char *filename, int start)

API首先编译一次。此API的参数我说明一下,str就是内存中的源代码,filename主要是出错时报错误用的,事实测试证明,你随意给个字符串也没有关系,但给NULL参数在运行时必然报错。start我一般用的是Py_file_input,因为的确是从文件中读取过来的,相对的还有Py_single_input用来表示一条语句,Py_eval_input的用法我也不是太清楚。

源代码通过此函数调用后,获得编译后的PyObject*,(其实假如跟进源代码中去看,是一个PyCodeObject结构)假设命名为lpCode

2.此时再调用API

PyObject* PyImport_ExecCodeModule(char *name, PyObject *co)

导入模块。参数也说明一下,name为导入的模块名,co就是前面编译过的代码对象(lpCode)。返回的就是模块对象了,假设命名为lpMod

3.再调用API

PyObject* PyObject_GetAttrString(PyObject *o, const char *attr_name)

获得函数对象。o就是模块对象(lpMod,attr_name就是你想要调用的函数名了,假设叫main的函数,就是”main”,然后返回的就是函数对象,假设命名为lpFun

4.此时可以用API

int PyCallable_Check(PyObject *o)

去检查一下是不是获得了一个函数。假如确定的话,就可以直接用

PyObject_Call开头的一族函数调用lpFun了。这些函数包括很多,一般就是输入参数的不同,但是效果都是一样的,就是调用函数而已。参数一般可以通过前面说过的build函数来获得,返回值也是获得一个PyObject*,可以通过PyArg_那个函数来获取,但是好像不太好,那是分析参数用的。推荐用确定类型(假设为type)的类似Py[type]_As的函数来获取。

比如:

long PyLong_AsLong(PyObject *pylong)获取long

double PyLong_AsDouble(PyObject *pylong)获取double

 

这里想说的是,应该有直接从源代码中获取函数调用对象的方式,但是我本人没有试出来,有人知道请一定赐教!

 

编译过的代码:

对于编译过的代码和上面就是获得编译后的PyCodeObject对象,当然在源代码中表示还是PyObject*的方法不同(上例中的lpCode)。

当然要想以后获得一个编译后的lpCode,自然要先编译一下啦。但是纯粹编译成pyc结尾的文件后,直接读入内存,我没有找到将其转化为PyCodeObject对象的方法(也希望有人知道能告诉我!)

 

我找到的方法是先用

PyObject* PyMarshal_WriteObjectToString(PyObject *value, int version)

void PyMarshal_WriteLongToFile(long value, FILE *file, int version)

两个函数先把PyCodeObject对象(lpCode)序列化到文件或者内存中。

再在需要的时候用函数

PyObject* PyMarshal_ReadObjectFromFile(FILE *file)

PyObject* PyMarshal_ReadObjectFromString(char *string, Py_ssize_t len)

读出来,读出来的PyObject*其实就是想要的PyCodeObject对象了(lpCode)。接下来的步骤与未编译时的步骤一样。

光是这个扭曲的方法我还是参考老总给的半边资料反复研究出来的。而真正直接有效的方法我还是没有找到。

 

我希望能解决的问题我总结一下,有2

1.     直接从源代码中读入,转成模块对象(lpMod),而不需要先通过Py_CompileString的调用。

2.     直接从pyc文件(内存)中读入,转成代码对象(lpCode)或者模块对象(lpMod),扭曲的通过序列化PyCodeObject对象再序列化出来的方式。

 

希望我从文档中摸索出来的东西对于有些同样需要的人有价值。这一些方法我在网上也找到,但是没有找到,最后还是靠自己摸索才找出了一些扭曲的方式来解决问题。

这里想对那些动不动就说你想把文档都看看的,你先把什么都看仔细了的兄弟们说一下,并不是每个老板都允许员工干一件事情前去把一种新的语言全学精了,哪怕他明知道这就是一种你不知道的语言。有的时候,just for work而 已。当然,在有时间的时候,自然我也是想把该学的都好好学了,只是,工作的时候,身不由己啊,何况,该学的东西是很多的,个人也可能有个人的学习计划,并不是工作中碰到一种问题,我的业余时间也得全部搭上去,把这件事情学通,所以请多给一些谅解,碰到有人问对你来说很初级的问题时,你好心的就给个回答,我 很感谢,不想回答的起码也不要说风凉话,我也很感谢了。其实我倒是也没有真发帖去问,因为等到有人回答估计一天的工作时间都耽误了,只是看到有类似的情况,所以随便说说,希望不要见怪。

 

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

SoftIce,IDA pro强强联合!从SOFTICE中打开IDA Pro输出的map信息文件


SoftIce,IDA pro强强联合!从SOFTICE中打开IDA Pro输出的map信息文件

 

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie

 

很搞笑的事情,才知道原来没有关系(指两个公司没有关系)的softice,ida pro两个软件其实也是可以交互的,这点除了在Linux下有这样的思想,其他地方还真少看到,一般都要等某个公司独霸了市场,然后大家慢慢向他靠拢以形成标准。才有可能做到通用。

其实softice支持的信息文件格式为nym格式的(我也不知道是什么格式),IDA Pro作为反汇编工具可以输出的格式还真算比较多,其中有种是 borland公司的map格式,这点倒好,还是没有办法通用,kris kaspersky的黑客调试技术揭秘中提到有个工具可以转换,叫做idasym,但是我一通暴搜,一无所获。最后几近周折(从官网找到了几个业余爱好者 blog)找到了一个插件,但是不能用。然后从ida pro的配置文件注释中发现了i2s插件,去下了一个,发现版本太老,不用用于最近的IDA,但是,就在绝望之中,发现了让人惊奇的事 实。。。。。。。。。。。。。。。。。。。。。。。。VS中有提供从mapnym转换的工具!!!至于为什么微软提供这样的工具我实在是不得而知。。。 但是。。。首次。。。由衷地感谢微软!

顺便说下工具的名字,MapSym.Exe
VS安装目录的Common7/Tools/Bin/下(VS2005),其他版本有没有我不缺确认,可以去找一找。

再顺便说下使用方法,格式如同mapsym -o outfile.nym infile.map

然后用softicesymload打开转换后的nym文件,载入成功后再载入需要调试的文件,这个时候就爽了!!!
可以任意在你分析后命了名的代码下断,呵呵,从此后,没有加壳的代码。。。。。无论是看,还是调试。。。基本已经和一般的源码么有区别了。。。(当然指的是非宏汇编源码)

 

 

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie

阅读全文....

黑客调试技术揭秘(Hacker Debugging Uncovered) 学习(1) 最简单的密码保护破解


黑客调试技术揭秘(Hacker Debugging Uncovered 学习(1 最简单的密码保护破解

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie

 

源代码

#include "stdafx.h"

#include <iostream>

#include <cstring>

using namespace std;

#define LEGAL_PSW "my.good.password"

 

int main(int argc, char* argv[])

{

    char lszCharPsw[100]
= {0};

 

    cout << "Crackme 00h/nenter passwd:";

    cin >>lszCharPsw;

    if(strcmp(LEGAL_PSW, lszCharPsw))

    {

       cout << "wrong password/n";

    }

    else

    {

       cout << "password ok/nhello, legal user!/n";

    }

 

 

 

    return 0;

}

 

我是在VS2005 SP1 Release下编译的,以下的汇编代码都是这个编译器生成的。

对于这么简单的程序,只能是用来熟悉工具了。。。。怎么说以前也是玩过一下的,呵呵。

按以前的传统步骤,先用PEID打开文件看看,哦,VS2005-2008编译的,没有加壳。(纯粹为了说明步骤.....)运行程序输出看结果,wrong password,ok就找这个字符串,用Ollydbg打开,查找参考字符串,就列出了所有的字符串了,连答案都已经出来了,假设此时不知道答案,双击定位到wrong password所在的位置,


00401063     /74 0D         JE     
SHORT JustForH.00401072

00401065  |. |A1 60204000   MOV    
EAX, DWORD PTR [<&MSVCP80.std::c>

0040106A  |. |68 64214000   PUSH   
JustForH.00402164               
;  ASCII "wrong
password",LF

0040106F  |. |50            PUSH    EAX

00401070  |. |EB 0C         JMP    
SHORT JustForH.0040107E

00401072  |> /8B0D 60204000 MOV     ECX, DWORD PTR
[<&MSVCP80.std::c>; 
MSVCP80.std::cout

00401078  |.  68
74214000   PUSH    JustForH.00402174                ;  ASCII "password ok",LF,"hello,
legal user!",LF

 

这个时候,谁都应该知道,这个第一行的JE就是判断完结果的跳转了,判断正确的话就输出了ASCII "password
ok",LF,"hello, legal user!",LF
,不然继续执行,输出ASCII "wrong
password",LF
,该怎么改也很明了了。。。。。

晕,用OllyDbg就达不到目的了。现在是来学别的工具的,按照书中的思路来走吧。

使用IDA Pro,直接载入文件,分析完毕,按书中提示,打开数据段(data),可是这个程序似乎不和书中一样,相关数据存储在(.rdata)中,如下所示,而不是.data中,因为目前对于PE文件格式还不是太熟悉(熟悉PE格式就是我下一步需要做的工作)还不知道出现与书中不一致情况的原因。

.rdata:0040211C
; struct _EXCEPTION_POINTERS ExceptionInfo

.rdata:0040211C
ExceptionInfo   _EXCEPTION_POINTERS
<offset dword_403040, offset dword_403098>

.rdata:0040211C                                         ; DATA
XREF: sub_4013A2+390o

.rdata:00402124
aBadAllocation  db 'bad
allocation',0   ; DATA XREF:
.data:00403018o

.rdata:00402133                 align 4

.rdata:00402134
aCrackme00hEnte db 'Crackme 00h',0Ah    ;
DATA XREF: sub_401000+32o

.rdata:00402134                 db 'enter passwd:',0

.rdata:0040214E                 align 10h

.rdata:00402150
aMy_good_passwo db 'my.good.password',0 ; DATA XREF: sub_401000+55o

.rdata:00402161                 align 4

.rdata:00402164
aWrongPassword  db 'wrong password',0Ah,0
; DATA XREF: sub_401000+6Ao

.rdata:00402174
aPasswordOkHell db 'password ok',0Ah    ;
DATA XREF: sub_401000+78o

 

OK,还是找到了wrong password,然后用IDA Pro的交叉引用,OK一样搞定了,一样可以找到wrong number被引用的地址,看到相关的源代码(其实应该说是反汇编代码):

.text:00401063                 jz      short loc_401072

.text:00401065                 mov     eax,
ds:?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ;
std::basic_ostream<char,std::char_traits<char>> std::cout

.text:0040106A                 push    offset aWrongPassword ; "wrong
password/n"

.text:0040106F                 push    eax

.text:00401070                 jmp     short loc_40107E

.text:00401072
;
哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

.text:00401072

.text:00401072
loc_401072:                             ;
CODE XREF: sub_401000+63j

.text:00401072                 mov     ecx,
ds:?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ;
std::basic_ostream<char,std::char_traits<char>> std::cout

.text:00401078                 push    offset aPasswordOkHell ; "password
ok/nhello, legal user!/n"

.text:0040107D                 push    ecx

 

一样还是傻子都能看出第一行在干什么,问题基本能够解决了。

其实不按书中所做,IDA Pro中有个string window,其中已经有所有的字符串了,这点和OllyDbg的查找有所引用字符串功能一样。相对而言,两个工具都很好用。但是IDA Pro生成的反汇编代码可读性相对还是更强一些,可能毕竟是用于静态分析用途的,所以对于代码有可能可进行的跳转操作,也是很方便:)

但是,想起谁说的来者OllyDbg就像是SoftIce + IDA Pro。。。。。呵呵,OllyDbg的确简单易用,我可以证明,但是是否是和SoftIce+IDA Pro这样强大嘛,还有待我继续学习,以了解真实情况。

 

继续:

Kris
Kasperasky
说上面这些都是工具的功能,和我们自己没有关系。。。。的确是,虽然我不是黑客,但是也继续下去了。。。。

HIEW32。。。。。。。。呵呵,原以为仅仅是一个16进制的编辑器,所以我还想假如不好用我就继续用我的WinHEX(这个可爱的家伙伴随着我很长很长的岁月了,虽然很多人喜欢用UE来做16进制编辑器,但是我在研究一个文件打包格式的时候才真的体会到,UEWinHex的差距,WinHex就是为编辑16进制为生的。。。。。。参看我以前贴的惨烈的工作环境的图,那就是WinHex伴随我工作的场景。

HIEW32是个命令行工具,打开文件后,一堆乱码。。。。书中实在没有详细讲解怎么用,只好自己研究研究啦,Linux下那么多命令行的工具都不在话下,不怕这一个。和许多有简陋图形界面的命令行工具一样,操作命令都写在最下面,如图,


但是你在尝试直接按数字没有用后,自然就是按F系列了,按F1可以看使用帮助,内容还真不少。。。。。。。。仔细研究一下吧。继续说。

先按F4,选择HEX模式,查找wrong number ASCII值,查到的地址,在我的机器中是。这里用F1中看到的有趣功能Atl-P,抓屏:)

.004020A0:  3C 25 00 00-28 25 00 00-12 25 00 00-04 25 00
00  <% 
(%   %   %   

.004020B0:  F8 24 00 00-EC 24 00 00-E4 24 00 00-D6 24 00
00  ? 
?  ?  ?   

.004020C0:  CE 24 00 00-C4 24 00 00-B4 24 00 00-A6 24 00
00  ? 
?  ?  ?   

.004020D0:  90 24 00 00-2A 2A 00 00-40 2A 00 00-00 00 00
00  ? 
**  @*       

.004020E0:  00 00 00 00-B1 13 40 00-00 00 00 00-00 00 00
00      ?@          

.004020F0:  72 15 40 00-9F 17 40 00-00 00 00 00-00 00 00
00  r @ ?@          

.00402100:  00 00 00 00-BD 1B 62 49-00 00 00 00-02 00 00
00      ?bI         

.00402110:  67 00 00 00-E8 21 00 00-E8 11 00 00-40 30 40
00  g  
?  ?  @0@  

.00402120:  98 30 40 00-62 61 64 20-61 6C 6C 6F-63 61 74
69  ?@ bad allocati 

.00402130:  6F 6E 00 00-43 72 61 63-6B 6D 65 20-30 30 68
0A  on 
Crackme 00h  

.00402140:  65 6E 74 65-72 20 70 61-73 73 77 64-3A 00 00
00  enter passwd:    

.00402150:  6D 79 2E 67-6F 6F 64 2E-70 61 73 73-77 6F 72
64  my.good.password 

.00402160:  00 00 00 00-77 72 6F 6E-67 20 70 61-73 73 77
6F      wrong passwo 

.00402170:  72 64 0A 00-70 61 73 73-77 6F 72 64-20 6F 6B
0A  rd 
password ok  

.00402180:  68 65 6C 6C-6F 2C 20 6C-65 67 61 6C-20 75 73
65  hello, legal use 

.00402190:  72 21 0A 00-70 61 75 73-65 00 00 00-00 00 00
00  r! 
pause        

.004021A0:  48 00 00 00-00 00 00 00-00 00 00 00-00 00 00
00  H                

.004021B0:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00
00                   

.004021C0:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00
00                   

.004021D0:  00 00 00 00-00 00 00 00-00 00 00 00-00 30 40
00               0@  

.004021E0:  50 22 40 00-03 00 00 00-52 53 44 53-0D F5 61
EF  P"@     RSDS
?

.004021F0:  22 6B C6 4D-9D 15 B2 31-EB B1 74 CB-02 00 00
00  "k
??t?    

 

记下地址00402160,按F5 0 enter跳转到文件头,再搜寻哪个地方使用到了这个字符串的地址,搜寻的时候因为0x86是小头机,搜寻60 21 40 00,搜到后,按F4 切入Decode模式,这是HIEWWinHex强大的地方了。。。。WinHex不带反汇编功能。。。。不过我奇怪的是。。作者最终还是用到了工具来反汇编,然后理解。。。仅仅因为自己多搜寻了两下地址。。。这就体现了技术了?-_-!

 

.00401047:
50                           push        eax                       

.00401048:
FF1558204000                 call        ??$?5DU?$char_traits@D@std@

.0040104E:
83C410                       add         esp,010 ;" "              

.00401051:
8D7C2408                     lea         edi,[esp][08]             

.00401055:
BE50214000                   mov         esi,000402150 ;'my.good.pas

.0040105A:
B911000000                   mov         ecx,000000011  --- 
(2)   

.0040105F:
33D2                         xor         edx,edx                   

.00401061:
F3A6                         repe        cmpsb                     

.00401063:
740D                         je         .000401072  --- 
(3)       

.00401065:
A160204000                   mov         eax,std::cout ;MSVCP80    

.0040106A: 6864214000                   push        000402164  --- 
(4)       

.0040106F:
50                           push        eax                       

.00401070:
EB0C                         jmps       .00040107E  --- 
(5)       

.00401072:
8B0D60204000                 mov         ecx,std::cout ;MSVCP80    

.00401078:
6874214000                   push        000402174  --- 
(6)       

.0040107D:
51                           push        ecx                       

.0040107E:
E85D010000                   call       .0004011E0  --- 
(7)       

.00401083:
83C408                       add         esp,008 ;" "              

.00401086:
6894214000                   push        000402194 ;'pause'        

.0040108B:
FF15D0204000                 call        system ;MSVCR80           

.00401091:
8B4C2478                     mov         ecx,[esp][78]             

.00401095:
83C404                       add         esp,004 ;" "              

.00401098:
5F                           pop         edi                       

 

粗体显示的就是我们搜寻到的那一行,到了这里,看看前两行,什么都知道了。

这里说下在HIEW中的改法,按F3进入编辑模式,直接将00401063这行改为第二排那样,按F9 Update,再按F10退出,再运行,就发现怎么都是正确的了。

.00401063:
740D                         je         .000401072  --- 
(3)

.00401063:
EB0D                         jmps       .000401072  --- 
(3)  

 

这里的修改有个不方便的地方就是,假如在编辑模式中直接按EnterF2进入Asm输入方式的修改,会破坏整个程序,因为HIEW不会将jmps 000401072改为near的相对偏移跳转方式的汇编代码(即EB0D),而是直接将jmps 000401072翻译为绝对地址的跳转方式,因为代码的数量远远的大于了原来的两个字节(光是地址就需要4个),所以导致程序破坏了。这里要说明的是,OllyDbg就要强大的多,它会进行这样适当的转换,并且还可以自动的在你将长代码改为短代码时为你用NOP填充。其实最简单的办法就是找到原有代码的合适位置,然后修改成代码长度一样的代码,这在插入代码很短的时候(比如一个简单的jmp)时很有用。在书中仅仅描述了怎么在原地修改的办法。其实常用的办法还包括一种简单的代码注入方式,即将某条指令换成跳转语句,跳转到某个空地方,将我们要执行的代码放在那里,以前我甚至试过动态调试的时候,直接输入指令执行:)这样可是很好很强大的啊。

原地修改嘛,起码得给自己的语句腾出位置,但是不伤害其他的原有语句。比如改成下面这样:

0040105F   . /EB 11         JMP    
SHORT JustForH.00401072

这样就绕过了检测了,一样可以达到目的。。。。太晚了,今天就到这里吧,其实好像除了IDA ProHIEW最基本的用法,什么都没有学会。

 

 

 

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie

 

阅读全文....

工作方向又换了,开始研究反外挂技术

以前自己是非常羡慕这样一群人的,每天对着汇编代码,任何软件无论怎么加壳,怎么加密,在他们看来都是源码。
呵呵,后来玩游戏的时候,也慢慢接触了一些东西,从DOS时代修改那些老RPG游戏的PCTools到FPE,到后来用的GE及与NP做斗争等等,往往在根据以往的教程,在新版的游戏中通过了游戏中的碰撞检测(达到无敌),极端的兴奋,虽然那时那个游戏已经是外挂漫天了,但是我往往可以在新版更新,外挂还没有发布之前就享受到外挂(乐趣?)呵呵。
以至于听说我们公司也要做这样的东西后(当然是反外挂了),我是非常的有兴趣。
虽然经过了这么久的工作,一些事情也看开了,而且我有了个很重要的认识,我是程序员,不是黑客。知道破解这东西是无止境的,所以反破解的事情也不会有止境,而且,这些对于你真正设计代码,写代码的帮助往往没有那么大,不说没有帮助吧,但是从消耗的时间到获得的帮助来说,肯定比老老实实多写点有意思的代码少的多吧。但是,趁这个机会,好好的把原来学了又忘忘了又学的asm好好巩固一下,这是有百利而无一害的事情。

而且,兴趣嘛,曾经这样想过,假如公司让我干反外挂的事情,我将把我的业余时间也拿出来工作。(看看我的博客,就知道其实我的业余时间还是干了很多事情的),所以,我业余也会研究反外挂技术了,初期阶段么,自然是以熟悉工具什么的为主了,以前用了OllyDbg,但是最近看了书以后,发现SoftIce才是王道。。。。。毕竟ring3和ring0的可以做的事情还是不一样的。
慢慢来吧,可怜我的数据结构学习又要卡住了,这点才是最郁闷的。

阅读全文....

VMWare虚拟机安装,SoftIce在虚拟机中的安装


VMWare虚拟机安装,SoftIce在虚拟机中的安装



write by 九天雁翎(JTianLing) -- www.jtianling.com

先来个开胃菜,为了安全起见(太多的破解工具都感觉不一定可靠,主要是来源。。。。)
还是用虚拟机比较安全,假如不想重复的开机关机及重装系统的话。
    

VMWare算是比较好的虚拟机了,虚拟机的领军人物。
我 安装的是虚拟先锋的绿色版,解压,点击绿化批处理文件就可以完成安装。然后新建一个虚拟机,配置一下内存,硬盘空间,像普通电脑一样安装系统就完了,
安装完不要忘了克隆一个来备份。安装系统的时候,VMWare有个好用的设置是从镜像(ISO等)来安装,这个只要在光驱的设置中选择就好了,非常的方 便。

要想SoftIceVMWare中正常使用,花费的功夫就需要多一点了。
1.
首先的安装VMWare Tools,这在客户端虚拟机启动的时候,选择VMWare的虚拟机菜单,选择安装VMWare Tools就可以安装了。(碰到过点击没有反应的情况,这个时候你可以选择在Host载入VMWare安装盘下的windows.iso镜像,那么也就可
以在客户端虚拟机安装了,这是windows版的VMWare Tools,不过要说明的是,不要在你虚拟出来的机器上装DaeMon,就我实验,那样会和SoftIce冲突,据说3.9以下版本不会,你可以去试 试)。
2.
安装SoftIce或者Driver Studio,反正都包含有SoftIce,选择显卡的时候,配置不需要改动,只是点击Test试试,只要第一步没有问题,这时候test应该也可以成功,没有第一步的话必然失败。
3.
关闭虚拟机,打开虚拟机的配置文件(你新建的虚拟机所在的目录),VMX后缀的文件,在最后添加
vmmouse.present = "FALSE"
svga.maxFullscreenRefreshTick = "5"
两行,再重新启动,选择开始菜单中SoftIceStart
SoftIce
,然后就可以按CTRL-D看看是否能够正常运行了,应该没有问题。

 

write by 九天雁翎(JTianLing) --
blog.csdn.net/vagrxie

阅读全文....