comment
IRC Chat
play_arrow
Este sitio utiliza cookies propias y de terceros. Si continúa navegando consideramos que acepta el uso de cookies. OK Más Información.

Ing. Inversa De Troyanos By bloodday

  • 6 Respuestas
  • 3387 Vistas

0 Usuarios y 1 Visitante están viendo este tema.

Conectado ANTRAX

  • *
  • Administrator
  • Mensajes: 5409
  • Actividad:
    20%
  • Reputación 33
  • ANTRAX
    • Ver Perfil
    • Underc0de
    • Email
  • Skype: underc0de.org
  • Twitter: @Underc0de
« en: Agosto 14, 2011, 03:19:54 pm »
Ingenieria Inversa de Troyanos Parte I

En nuestras primeras clases... empezaremos con algunas definiciones, veremos lo que hacen las instrucciones de ensamblador, y algunas APIs de Windows, les diría que se leyeran el "Windows API for programmers" pero hay algunas APIs que no esta ahí (por ejemplo busquen DebugActiveProcessStop y no les aparecerá) así que para las APIs que no sepamos que hacen, usaremos el Puto google (con esto también me incluyo por que hay muchas APIs que no se que hacen...)

Empecemos con las definiciones...

Registros: los registros, son algo así como "variables" que usa el procesador, y hay varios de ellos, y cada uno tiene una tarea especifica... nosotros nos concentraremos en los que nos muestra el ollydbg, que son EAX, EBX, ECX, EDX, EBP, ESP, ESI, EDI y EIP. Pero que carajos significa eso?


Pues bien, la “E” significa EXTENDIDO, pero por que extendido? Pues sencillo, antes se trabajaba con 16 bits y los registros eran AX, BX, CX, DX, BP, SP, SI, DI e IP, y para poder trabajar con 32 bits se “inventaron” estos registros extendidos.

Bueno, para dar una explicación sencilla diremos que los registros que terminan en “X” (EAX, EBX, ECX y EDX) son registros de uso general, pero además de ello también tienen una tarea específica que cumplir. Los registros que terminan en “P” (EIP, EBP  y ESP) son registros que llevan un Puntero (Pointer en ingles) es por eso que terminan en “P” y Finalmente los dos registros que terminan en “I” (ESI y EDI) son registros de Índice.

Como ya hemos dicho, cada registro tiene una función específica, y la segunda letra de su nombre nos indica la función asignada a cada registro, de esta manera la “A” de EAX, quiere decir que es el registro Acumulador, por ser aquí donde se guarden los resultados de ciertas operaciones (div y mul por ejemplo) y donde la mayoría de las APIs retornen algún valor.

La “B” de EBX, y EBP nos indica que son registros de Base, en el caso de EBX además de su uso como registro general, suele utilizarse para direccionar el acceso a datos situados en la memoria. Y EBP es utilizado para direccionar el acceso a datos situados dentro del espacio ocupado por la pila, aunque también puede ser de uso general.

La “C” de ECX, nos indica que es el registro Contador (Counter en ingles) y es así, por que es el registro utilizado para hacer bucles, además de ser un registro de uso general.

La “D” de EDX nos indica que es un registro de Datos, y es así por ser utilizado en instrucciones de entrada y salida, pero además de esto, también es utilizado en conjunto con EAX para formar números de mas de 32 bits en operaciones como multiplicar (mul) o dividir (div)

La “S” de ESP viene dada por que como ya se dijo lleva un puntero, y este es el de la Pila (Stack en ingles) es decir que apunta hacia el primer valor de ésta.

La “I” de EIP  significa Instrucción, es decir que este registro nos dice el Puntero o la dirección de la siguiente instrucción a ejecutar.

Y por ultimo los registros de Índice ESI y EDI, pues estos registros son utilizados por ciertas operaciones como origen (ESI) y destino (EDI) por ejemplo para copiar un bloque de bytes.

API: Las APIs (Application Programming Interface) son rutinas "prefabricadas" destinadas a facilitar la labor del Programador y al mismo tiempo a reducir la extensión de los programas, porque no hace falta incluir estas Rutinas en el ejecutable, ya que están incluidas en librerías de Windows.

Flags: son Bits que se ponen a uno o cero según la instrucción que ejecutemos, y hay instrucciones que hacen una cosa u otra según el estado de alguno de ellos (saltos condicionales por ejemplo)

Instrucciones del lenguaje ASM (sacadas de “ASM por Caos Reptante” por que me parece que mejor explicadas no podrían estar)

Vamos a ver la mayoría, pero no todas, he dejado de lado las que me han parecido menos importantes.

And El resultado es 1 si los dos operandos son 1, y 0 en cualquier otro caso.
1 and 1 = 1
1 and 0 = 0
0 and 1 = 0
0 and 0 = 0
Ejemplo: 1011 and 0110 = 0010

Or  El resultado es 1 si uno o los dos operandos es 1, y 0 en cualquier otro caso.
1 or 1 = 1
1 or 0 = 1
0 or 1 = 1
0 or 0 = 0
Ejemplo: 1011 or 0110 = 1111

Xor  El resultado es 1 si uno y sólo uno de los dos operandos es 1, y 0 en cualquier otro caso
1 xor 1 = 0
1 xor 0 = 1
0 xor 1 = 1
0 xor 0 = 0
Ejemplo: 1011 xor 0110 = 1101

Not  Simplemente invierte el valor del único operando de esta función
Not 1 = 0
Not 0 = 1
Ejemplo: not 0110 = 1001

Estos ejemplos los hemos visto con números binarios. Como estas operaciones se hacen bit a bit, si quisiéramos hacer una operación entre números en formato hexadecimal, que es lo más corriente, deberíamos pasarlos antes a binario. O bien utilizar la calculadora de Windows en formato científico.

Nop  (No Operation)

Esta instrucción es de gran utilidad: no hace nada. Su verdadera utilidad reside en rellenar los huecos provocados por la eliminación de instrucciones o su substitución por instrucciones de menor longitud. Su nombre da origen al verbo "nopear" que tan a menudo utilizan los chicos malos ;-) Hay corrientes filosóficas dentro del cracking que sostienen que poner nops es una cochinada.

-y aquí tengo que hacer una pausa para reseñar que la instrucción NOP NO EXISTE pues si vemos su opcode (bytes de la instrucción) es 90, y este opcode pertenece a la instrucción XCHG EAX,EAX pero como es obvio que esto no modifica nada, los debuggers y desensambladotes usan este “convencionalismo”

Push (Push Word or Doubleword Onto the Stack)

Esta instrucción resta del registro ESP, la longitud de su único operando que puede ser de tipo Word o doubleword (4 u 8 bytes), y a continuación lo coloca en la pila. (en pocas palabras pone lo que le indiques de primero en la pila)

Pop (Pop a Value from the Stack)

Es la inversa de la anterior, es decir que incrementa el registro ESP y retira el valor disponible de la pila y lo coloca donde indica el operando.

Pushad (Push All General-Purpose Registers)

Estas instrucciones guardan el contenido de los registros en la pila en un orden determinado. Así pues, pushad equivale a: push EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI.

Popad (Pop All General-Purpose Registers)

Estas instrucciones efectúan la función inversa de las anteriores, es decir, restituyen a los registros los valores recuperados de la pila en el orden inverso al que se guardaron. Así popad equivale a: pop EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX.

Pushfd (Push EFLAGS Register onto the Stack)
Popfd (Pop Stack into EFLAGS Register)

Estas parejas de instrucciones colocan y retiran de la pila el registro de flags.

mov (Move)

Esta instrucción tiene dos operandos. Copia el contenido del operando de origen (representado en segundo lugar) en el de destino (en primer lugar), y según el tipo de estos operandos adopta formatos distintos. He aquí unos ejemplos:
Código: [Seleccionar]
6689C8 mov ax, cx
8BC3 mov eax, ebx
8B5BDC mov ebx, dword ptr [ebx-24]
893438 mov dword ptr [eax+edi], esi
movsx (Move with Sign-Extension)

Copia el contenido del segundo operando, que puede ser un registro o una posición de memoria, en el primero (de doble longitud que el segundo), rellenándose los bits sobrantes por la izquierda con el valor del bit más significativo del segundo operando. Aquí tenemos un par de ejemplos:
Código: [Seleccionar]
33C0 xor eax, eax
BB78563412 mov ebx, 12345678
0FBFC3 movsx eax, bx EAX=00005678
33C0 xor eax, eax
BBCDAB3412 mov ebx, 1234ABCD
0FBFC3 movsx eax, bx EAX=FFFFABCD
Vemos como en el primer ejemplo los espacios vacíos se rellenan con ceros y en el segundo con unos.

movzx (Move with Zero-Extend)

Igual a movsx, pero en este caso, los espacios sobrantes se rellenan siempre con ceros. Veamos como el segundo ejemplo de la instrucción anterior da un resultado distinto:
Código: [Seleccionar]
33C0 xor eax, eax
BBCDAB3412 mov ebx, 1234ABCD
0FB7C3 movzx eax, bx EAX=0000ABCD
lea (Load Effective Address)

Similar a la instrucción mov, pero el primer operando es un registro de uso general y el segundo una dirección de memoria. Esta instrucción es útil sobre todo cuando esta dirección de memoria responde a un cálculo previo.
Código: [Seleccionar]
8D4638 lea eax, dword ptr [esi+38]
xchg (Exchange Register/Memory with Register)

Esta instrucción intercambia los contenidos de los dos operandos.
Código: [Seleccionar]
87CA xchg edx, ecx
870538305100 xchg dword ptr [00513038], eax
8710 xchg dword ptr [eax], edx
En el primer ejemplo, el contenido del registro ECX, se copia en el registro EDX, y el contenido anterior de EDX, se copia en ECX. Se obtendría el mismo resultado con:
Código: [Seleccionar]
51 push ecx
52 push edx
59 pop ecx
5A pop edx
La instrucción pop ecx toma el valor que hay en la pila y lo coloca en el registro ECX, pero como podemos ver por la instrucción anterior push edx, este valor, procedía del registro EDX. Luego se coloca en EDX el valor procedente de ECX.

bswap (Byte Swap)

Esta instrucción es para empleo exclusivo con registros de 32 bits como único parámetro. Intercambia los bits 0 a 7 con los bits 24 a 31, y los bits 16 a 23 con los bit 8 a 15.
Código: [Seleccionar]
B8CD34AB12 mov eax, 12AB34CD EAX=12AB34CD
0FC8 bswap eax EAX=CD34AB12
Inc (Increment by 1) / dec (Decrement by 1)

Estas dos instrucciones incrementan y decrementan respectivamente el valor indicado en su único operando.
Código: [Seleccionar]
FF0524345100 Inc dword ptr [00513424]
FF0D24345100 dec dword ptr [00513424]
40 inc eax
4B dec ebx
En los dos primeros ejemplos, se incrementaría y decrementaría el valor contenido en los cuatro bytes situados a partir de la dirección 513424.

add (Add)

Esta instrucción suma los contenidos de sus dos operandos y coloca el resultado en el operando representado en primer lugar.
Código: [Seleccionar]
02C1 add al, cl AL + CL -> AL
01C2 add edx, eax EDX + EAX -> EDX
8345E408 add dword ptr [ebp-1C], 0000008 dword ptr [EBP-1C]+ 8 > [EBP-1C]
En el tercer ejemplo, como el resultado se coloca siempre en el primer operando, no sería aceptada por el compilador una instrucción como add 00000008, dword ptr [ebp-1C]

adc (Add with Carry)

Esta instrucción es similar a la anterior, con la diferencia de que se suma también el valor del flag de acarreo. Se utiliza para sumar valores mayores de 32 bits. Supongamos que queremos sumar al contenido de los registros EDX:EAX (EDX=00000021h y EAX=87AE43F5), un valor de más de 32 bits (3ED671A23). Veamos como se hace:
Código: [Seleccionar]
Add eax, ED671A23 EAX=75155E18 (87AE43F5+ED671A23) CF=1
adc edx, 0000003 EDX=25h (21h+3+1)
sub (Subtract)

Esta instrucción resta el contenido del segundo operando del primero, colocando el resultado en el primer operando.
Código: [Seleccionar]
83EA16 sub edx, 00000016 EDX - 16 -> EDX
29C8 sub eax, ecx EAX - ECX -> EAX
2B2B sub ebp, dword ptr [ebx] EBP - dword ptr [EBX] -> EBP
sbb (Integer Subtraction with Borrow)

Esta instrucción es una resta en la que se tiene en cuenta el valor del flag de acarreo. Supongamos que del contenido de los registros EDX:EAX después del ejecutado el ejemplo de la instrucción adc (EDX=00000025h y EAX=75155E18), queremos restar el valor 3ED671A23. El resultado es el valor que tenían inicialmente los dos registros en el ejemplo citado:
Código: [Seleccionar]
Sub eax, ED671A23 EAX=87AE43F5 (75155E18-ED671A23) CF=1
sbb edx, 0000003 EDX=21h (25h-3-1)

mul (Unsigned Multiply) / imul (Signed Multiply)

Estas dos instrucciones se utilizan para multiplicar dos valores. La diferencia más importante entre las dos, es que en la primera no se tiene en cuenta el signo de los factores, mientras que en la segunda sí. Como veremos en algunos ejemplos, esta diferencia se refleja en los valores de los flags.

En la instrucción mul, hay un solo operando. Si es un valor de tamaño byte, se multiplica este valor por el contenido de AL y el resultado se guarda en EAX, si el valor es de tamaño word (2 bytes), se multiplica por AX, y el resultado se guarda en EAX y finalmente, si el valor es de tamaño dword (4bytes), se multiplica por EAX y el resultado se guarda en EDX:EAX. O sea, que el espacio destinado al resultado siempre es de tamaño doble al de los operandos.
Código: [Seleccionar]
F7E1 mul ecx EAX*ECX -> EDX:EAX
F72424 mul dword ptr [esp] EAX*[ESP] -> EDX:EAX
En la instrucción imul, hay también una mayor variedad en el origen de sus factores. Además de la utilización de los registros EAX y EDX, así como de sus subdivisiones, pueden especificarse otros orígenes y destinos de datos y puede haber hasta tres operandos. El primero, es el lugar donde se va a guardar el resultado, que debe ser siempre un registro, el segundo y el tercero son los dos valores a multiplicar. En estos ejemplos vemos como estas instrucciones con dos o tres operandos, tienen el mismo
espacio para el resultado que para cada uno de los factores:
Código: [Seleccionar]
F7EB imul ebx EAX x EBX -> EDX:EAX
696E74020080FF imul ebp, dword ptr [esi+74], FF800002 [ESI+74] x FF800002 -> EBP
0FAF55E8 imul edx, dword ptr [ebp-18] EDX x [EBP-18] -> EDX
Veamos finalmente la diferencia entre la multiplicación con signo y sin él:
Código: [Seleccionar]
66B8FFFF mov ax, FFFF AX=FFFF
66BBFFFF mov bx, FFFF
66F7E3 mul bx AX=0001 OF=1 CF=1
66B8FFFF mov ax, FFFF
66F7EB imul bx AX=0001 OF=0 CF=0
En la primera operación, como se consideran los números sin signo, se ha multiplicado 65535d por 65535d y el resultado ha sido 1. Debido a este resultado "anómalo", se han activado los flags OF y CF. En cambio, en la segunda operación, en la que se toma en cuenta el signo, se ha multiplicado -1 por -1 y el resultado ha sido 1. En este caso no se ha activado ningún flag. Otro ejemplo:
Código: [Seleccionar]
B8FFFFFF7F mov eax, 7FFFFFFF
BB02000000 mov ebx, 00000002
F7E3 mul ebx EAX=FFFFFFFE OF=0 CF=0
B8FFFFFF7F mov eax, 7FFFFFFF
F7EB imul ebx EAX=FFFFFFFE OF=1 CF=1
Esta vez, en el primer caso, el resultado ha sido correcto, porque 2147483647d multiplicado por dos ha dado 4294967294d, por tanto, no se ha activado ningún flag. Pero en el segundo caso, teniendo en cuenta el signo, hemos multiplicado 2147483647d por dos y nos ha dado como resultado -2. Ahora si se han activado los flags.

div (Unsigned Divide) / idiv (Signed Divide)

El caso de la división es muy parecido al de la multiplicación. Hay dos instrucciones: div para números en los que no se considere el signo e idiv para números que se consideren con signo. El dividendo está formado por una pareja de registros y el divisor es el único operando. He aquí varios ejemplos de una y otra instrucción:
Código: [Seleccionar]
66F7F3 div bx DX:AX : BX -> AX resto -> DX
F7F3 div ebx EDX:EAX : EBX -> EAX resto -> EDX
F77308 div dword ptr [ebx+08] EDX:EAX : [EBX+8] -> EAX resto -> EDX
F7F9 idiv ecx EDX:EAX : ECX -> EAX resto -> EDX
Ahora, como hemos hecho con la multiplicación, vamos a ver el diferente resultado que se obtiene empleando una u otra instrucción:
Código: [Seleccionar]
33D2 xor edx, edx
66B80100 mov ax, 0001
66BBFFFF mov bx, FFFF
66F7F3 div bx AX=0000 DX=0001
33D2 xor edx, edx
66B80100 mov ax, 0001
66F7FB idiv bx AX=FFFF DX=0000
En el primer caso, al no considerar el signo de los números, se ha dividido 1 por 65535, que ha dado un cociente de 0 y un resto de 1. En el segundo caso, se ha dividido -1 por 1, lo que ha dado un cociente de -1 y un resto de 0. No ha habido overflow ni acarreo en ninguno de los dos casos.

xadd (Exchange and Add)

Intercambia los valores de los dos operandos y los suma, colocando el resultado en el primer operando. El primer operando puede ser un registro o una posición de memoria, pero el segundo sólo puede ser un registro.
Código: [Seleccionar]
C703CDAB3412 mov dword ptr [ebx], 1234ABCD
En la dirección indicada por EBX tendremos el valor CD AB 34 12. Vemos el valor que hemos puesto en la memoria invertido, porque el paso del valor de un registro a la
memoria y viceversa se hace empezando por el último byte y terminando por el primero.
Código: [Seleccionar]
B8CD34AB12 mov eax, 12AB34CD
B934120000 mov ecx, 00001234
0FC1C8 xadd eax, ecx
EAX contiene el valor 12AB4701 (12AB34CD+1234) y ECX el valor 12AB34CD.
Código: [Seleccionar]
B934120000 mov ecx, 00001234
0FC10B xadd dword ptr [ebx], ecx
La dirección indicada por EBX contiene el valor 01 BE 34 12 (1234ABCD+1234) y el registro ECX el valor 1234ABCD.

neg (Two's Complement Negation)

Esta instrucción tiene la finalidad de cambiar de signo el número representado por su único operando, mediante una operación de complemento a dos.
Código: [Seleccionar]
B81C325100 mov eax, 0051321C
F7D8 neg eax EAX=FFAECDE4

neg 0000 0000 0101 0001 0011 0010 0001 1100 = 0051321C
       1111 1111 1010 1110 1100 1101 1110 0011
  + 1
       1111 1111 1010 1110 1100 1101 1110 0100 = FFAECDE4
CMP (Compare Two Operands)

La comparación entre dos valores es en realidad una resta entre ambos. Según cual sea el resultado, podemos saber si los valores son iguales y en caso contrario, cual de ellos es el mayor. Así, se podría utilizar la instrucción sub ecx, ebx para comparar el resultado de estos dos registros, sin embargo el hacerlo así tiene el problema de que el resultado de la resta se colocaría en el registro ECX, cuyo valor anterior desaparecería. Para evitar este problema el programador dispone de la instrucción cmp.

Esta instrucción resta el segundo operando del primero. El resultado no se guarda en ningún sitio, pero según cual sea este resultado, pueden modificarse los valores de los flags CF, OF, SF, ZF, AF y PF. Es en base al estado de estos flags, que se efectúa o no el salto condicional que suele acompañar a esta instrucción. Veremos esta instrucción en los ejemplos de saltos condicionales.

Bueno, eso es todo por esta clase, si tienen preguntas, háganlas por acá o por el Chat, para la próxima veremos algunas APIs, y nos introduciremos en el Olly para aprender a utilizarlo y ver las posibilidades que nos da.

Para los que quieran leer el documento “ASM por Caos Reptante” se los dejo adjunto.

http://www.mediafire.com/?mz9zxnvwztn


Conectado ANTRAX

  • *
  • Administrator
  • Mensajes: 5409
  • Actividad:
    20%
  • Reputación 33
  • ANTRAX
    • Ver Perfil
    • Underc0de
    • Email
  • Skype: underc0de.org
  • Twitter: @Underc0de
« Respuesta #1 en: Agosto 14, 2011, 03:24:10 pm »
Ingenieria Inversa de Troyanos Parte II

Bueno para empezar en la clase anterior quedamos pendientes con los saltos, así que será lo primero que veamos (salten esta parte los que se leyeron “ASM por CaoS ReptantE”), luego veremos algunas APIs de Windows y empezaremos a ver de refilón el debugger.

Instrucciones de salto

El listado de un programa consiste en una sucesión de instrucciones. Sin embargo a la hora de ejecutarlo, la ejecución del mismo no sigue el orden del listado, sino que, de acuerdo con distintas circunstancias y mediante las instrucciones de salto, se interrumpe la ejecución lineal del programa para continuar dicha ejecución en otro lugar del código.
Las instrucciones de salto son básicamente de dos tipos: de salto condicional y de salto incondicional. En el primer tipo, la instrucción de salto suele ponerse después de una comparación y el programa decide si se efectúa o no el salto, según el estado de los flags (excepto en un caso, en el que el salto se efectúa si el valor del registro ECX o CX es cero). En el segundo, el salto se efectúa siempre.

Tipos de salto

Según la distancia a la que se efectúe el salto, estos se dividen en tres tipos: corto, cercano y lejano. También se dividen en absolutos o relativos, según como se exprese en la instrucción la dirección de destino del salto. Como veremos, los saltos cortos y cercanos son relativos, y los largos absolutos. En el salto corto, la dirección de destino se expresa mediante un byte, que va a continuación del código de la instrucción. Este byte contiene un número con signo que, sumado a la dirección de la instrucción siguiente a la del salto, nos da la dirección de destino del mismo. Como este número es con signo, podemos deducir que un salto corto sólo puede efectuarse a una distancia hacia adelante de 127 bytes y hacia atrás de 128. Veamos dos ejemplos de salto corto:

Código: [Seleccionar]
:004011E5 83FB05 cmp ebx, 00000005
:004011E8 7505 jne 004011EF
:004011EA C645002D mov [ebp+00], 2D
...
:004011EF 83FB09 cmp ebx, 00000009
:004011F2 72E2 jb 004011D6
:004011F4 58 pop eax
En el primer salto, la dirección de destino es la suma de la dirección de la instrucción siguiente y el desplazamiento: 4011EA+5=4011EF. En el segundo caso, E2 es un número negativo, por lo que la dirección de destino es la de la instrucción siguiente menos la equivalencia en positivo de E2 (1E): 4011F4-1E=4011D6. Estos cálculos, por supuesto, no los hace el programador, que simplemente dirige el salto a una etiqueta para que luego el compilador coloque los códigos correspondientes, pero me ha parecido que valía la pena explicarlo aquí.

- y aquí interrumpo yo de nuevo, nosotros NO trabajaremos desde un compilador, así que no pondremos saltos a etiquetas si no a direcciones de memoria, pero no se preocupen que tampoco tendremos que hacer “cálculos de salto”

El salto cercano es básicamente lo mismo que el salto corto. La diferencia está en que la distancia a que se efectúa es mayor, y no se puede expresar en un byte, por lo que se dispone de cuatro bytes (en programas de 16 bits) que permiten saltos de 32767 bytes hacia adelante y 32768 hacia atrás o de ocho bytes (en programas de 32 bits) que permiten vertiginosos saltos de 2147483647 bytes hacia adelante y 21474836478 bytes hacia atrás.
Código: [Seleccionar]
:0040194F 0F8E96000000 jle 004019EB
:00401955 8D4C2404 lea ecx, dword ptr [esp+04]
...
:004019CB 0F8566FFFFFF jne 00401937
:004019D1 8D4C240C lea ecx, dword ptr [esp+0C]
En la primera instrucción, la dirección de destino es: 401955+96= 4019EB. En la segunda la dirección es: 4019D1-9A= 401937. Fijaos en que los bytes que indican el desplazamiento están escritos al revés y que 9A es la equivalencia en positivo de FFFFFF66.

Los saltos largos se utilizan cuando la instrucción de destino está en un segmento distinto al de la instrucción de salto.
Código: [Seleccionar]
:0003.0C28 2EFFA72D0C jmp word ptr cs:[bx+0C2D]

Saltos Condicionales

Las instrucciones de salto condicional sólo admiten los formatos de salto corto y salto cercano, por lo que su código está formado por el código de la instrucción más un byte (cb), un Word (cw) o un doubleword (cd) que determinan el desplazamiento del salto.

Aquí tenéis una relación de las instrucciones de salto condicional, desglosadas según el tipo de salto, en la que pueden apreciarse las equivalencias entre instrucciones de nombre distinto pero idéntica función.

Código: [Seleccionar]
JA Si es superior (CF=0 y ZF=0)
JNBE Si no es inferior o igual (CF=0 y ZF=0)

  JAE Si es superior o igual (CF=0)
JNB Si no es inferior (CF=0)
JNC Si no hay acarreo (CF=0)

  JNA Si no es superior (CF=1 o ZF=1)
JBE es inferior o igual (CF=1 o ZF=1)

JNAE Si no es superior o igual (CF=1)
JB Si es inferior (CF=1)
JC Si hay acarreo (CF=1)

JG Si es mayor (ZF=0 y SF=OF)
JNLE Si no es menor o igual (ZF=0 y SF=OF)

JGE Si es mayor o igual (SF=OF)
JNL Si no es menor (SF=OF)

JNG Si no es mayor (ZF=1 o SF<>OF)
JLE Si es menor o igual (ZF=1 o SF<>OF)

JNGE Si no es mayor o igual (SF<>OF)
JL Si es menor (SF<>OF)

JE Si es igual (ZF=1)
JZ Si es cero (ZF=1)

JNE Si no es igual (ZF=0)
JNZ Si no es cero (ZF=0)

JO Si hay desbordamiento (OF=1)

JNO Si no hay desbordamiento (OF=0)

JP Si hay paridad (PF=1)
JPE Si es paridad par (PF=1)

JNP Si no hay paridad (PF=0)
JPO Si es paridad impar (PF=0)

JS Si es signo negativo (SF=1)

JNS Si no es signo negativo (SF=0)

JCXZ Si CX=0 //esta instrucción solo es interpretada
JECXZ Si ECX=0 //por procesadores INTEL
Salto incondicional

Es un salto que no está sujeto a ninguna condición, es decir, que se efectúa siempre. Hay una sola instrucción: jmp. Esta instrucción admite los tres tipos de salto: corto, cercano y lejano. Creo que con unos ejemplos será suficiente:
Código: [Seleccionar]
:004011DA EB30 jmp 0040120C
:00402B35 E9F8000000 jmp 00402C32
:0001.02B4 E94D01 jmp 0404
:0003.0C28 2EFFA72D0C jmp word ptr cs:[bx+0C2D]

Ahora que hemos visto todos los saltos habidos y por haber pasemos a otro tema, las API.

Como se dijo en la clase 1, son instrucciones preprogramadas que le facilitan la vida al programador, pero para poder utilizarlas, se debe indicar en el ejecutable cuales son las que van a utilizar, y en que librería se encuentran (aunque no lo crean, hay apis repetidas en Kernel32.dll y Ntdll.dll), pero además de esto también hay que pasarle unos parámetros o argumentos, para que la función sepa lo que va a hacer.

La forma de pasarle estos argumentos a las APIs es metiéndolos en la pila (push) antes de llamar a la API.
Código: [Seleccionar]
Push 40 //tipo de MessageBox
Push 402000  //puntero del titulo del MessageBox
Push 402010 //puntero del texto del MessageBox
Push 0 //manejador del MessageBox
Call MessageBoxA
Como pueden ver en este ejemplo, se introducen los argumentos a la pila antes de la llamada a la API, los “punteros” son las direcciones que contienen los verdaderos argumentos.

Para cada API los argumentos son diferentes. Y aquí les dejo algunas APIs, lo que hacen y los argumentos que necesitan

MessageBoxA

Crea, despliega, y maneja una caja de mensaje. La caja de mensaje contiene un mensaje definido por el programa y un título, más cualquier combinación de iconos y botones predefinida.
Código: [Seleccionar]
    Int MessageBox(
     HWND  hWnd,                       // el manipulador de ventana padre
     LPCTSTR  lpText,                           // la dirección del texto dentro de la caja de mensaje
     LPCTSTR  lpCaption,               // la dirección del título de la caja de mensaje 
     UINT  uType                       // el estilo de caja de mensaje
    );
Returns
El valor de retorno es cero si no hay bastante memoria para crear la caja de mensaje. Si la función tiene éxito, el valor del retorno es uno de lo siguiente valores devueltos por la caja de diálogo:

IDABORT, IDCANCEL, IDIGNORE, IDNO, IDOK, IDRETRY, IDYES

Si una caja de mensaje tiene un botón de Cancelación, la función devuelve el valor IDCANCEL si la tecla ESC es apretada o si de botón de Cancelación es seleccionado. Si la caja de mensaje no tiene ningún botón de Cancelación, apretar ESC no tiene efecto.
CreateFileA

La función CreateFile crea, abre, o trunca un archivo, pipe (*), fuente de comunicaciones, dispositivo de disco, o consola. Devuelve un manipulador que puede usarse para acceder al objeto. También puede abrir y puede devolver un manipulador para un directorio.
Código: [Seleccionar]

     HANDLE CreateFile(
      LPCTSTR  lpFileName,                       // la dirección de nombre del archivo
      DWORD  dwDesiredAccess,          // el modo de acceso (leer-escribir)
      DWORD  dwShareMode,                       // modo share 
      LPSECURITY_ATTRIBUTES  lpSecurityAttributes,// la dirección de descriptor de seguridad
      DWORD  dwCreationDistribution,          // cómo crear
      DWORD  dwFlagsAndAttributes,          // los atributos del archivo
      HANDLE  hTemplateFile                    // el manipulador de archivo con atributos para copiar 
     );

Returns
Si la función tiene éxito, el valor del retorno es un manipulador abierto del archivo especificado. Si el archivo especificado existe antes de función llamada y dwCreationDistribution es CREATE_ALWAYS u OPEN_ALWAYS, una llamada a GetLastError devuelve ERROR_ALREADY_EXISTS (aunque la función ha tenido éxito). Si el archivo no existe antes de la llamada, GetLastError devuelve cero.

ReadFileA

La función de ReadFile lee datos de un archivo, comienza en la posición indicada por el indicador del archivo. Después de que la operación de lectura se ha completado, el indicador del archivo es ajustado realmente por el número de bytes leídos, a menos que el manipulador de archivos sea creado con el atributo superpuesto. Si el manipulador de archivos es creado para entrada y salida superpuesta (I/O), la aplicación debe ajustar la posición del indicador del archivo después de la operación de lectura.
Código: [Seleccionar]
     BOOL ReadFile(
      HANDLE  hFile,                                  // el manipulador de archivo para leer
      LPVOID  lpBuffer,                     // la dirección de buffer que recibe los datos 
      DWORD  nNumberOfBytesToRead,        // el número de bytes para leer
      LPDWORD  lpNumberOfBytesRead,        // la dirección de número de lectura de los bytes
      LPOVERLAPPED  lpOverlapped        // la dirección de estructura para el dato
     );

Returns
Si la función tiene éxito, el valor del retorno es TRUE. Si el valor de retorno es que TRUE y el número de lectura de los bytes es cero, el indicador del archivo estaba más allá del extremo actual del archivo en el momento de la operación de lectura.

Si la función falla, el valor de retorno es FALSE. Para conseguir información extendida del error, llama a GetLastError.
WriteFile

La función WriteFile escribe datos a un archivo y está diseñado para la operación sincrónica y asíncrona. La función comienza escribiendo datos al archivo en la posición indicado por el indicador del archivo. Después de que la operación de escritura se ha completado, el indicador del archivo es ajustado realmente por el número de bytes escritos, excepto cuando el archivo es abierto con FILE_FLAG_OVERLAPPED. Si el manipulador de archivos se creara para la entrada y salida solapada (I/O), la aplicación debe ajustar la posición del indicador del archivo después de que la operación de escritura es terminada.
Código: [Seleccionar]
    BOOL WriteFile(
      HANDLE  hFile,                                // el manipulador de archivo para escribir
      LPCVOID  lpBuffer,                   // la dirección de datos para escribir al archivo
      DWORD  nNumberOfBytesToWrite,       // el número de bytes a escribir
      LPDWORD  lpNumberOfBytesWritten,   // la dirección del número de bytes escritos
      LPOVERLAPPED  lpOverlapped       // la direc. De estructura necesaria para I/O solapada 
     );
Returns
Si la función tiene éxito, el valor de retorno es TRUE.

Si la función falla, el valor del retorno es FALSE. Para conseguir información extendida del error, llama a GetLastError.

RegCreateKeyExA

La función RegCreateKeyEx crea la clave especificada. Si la clave ya existe en el registro, la función lo abre.
Código: [Seleccionar]
    LONG RegCreateKeyEx (
      HKEY  hKey,                             // el manipulador de una clave abierta
      LPCTSTR  lpszSubKey,                // la dirección del nombre de subclave
      DWORD  dwReserved,                // reservado
      LPTSTR  lpszClass,                // la dirección de clase de string
      DWORD  fdwOptions,                // las opciones especiales de flag
      REGSAM  samDesired,                // acceso de seguridad deseado 
      LPSECURITY_ATTRIBUTES  lpSecurityAttributes, // la dirección de estructura de seguridad de clave
      PHKEY  phkResult,                // la dirección de buffer para el manipulador abierto 
      LPDWORD  lpdwDisposition   // la dirección de valor de disposición del buffer
     );
Returns
Si la función tiene éxito, el valor de retorno es ERROR_SUCCESS.

Si la función falla, el valor de retorno es un valor de error.
(la forma de llamar a estas apis en ASM es hacer push a los parámetros, poniendo los que están de últimos, de primeros –por ejemplo, el parámetro lpdwDispsition de la ultima API explicada, es al que primero se debe hacer push-)

Eso es todo en cuanto a APIs por hoy, ahora pasaremos al debugger que será en lo que nos enfoquemos de aquí en adelante…

Empecemos con las partes del mismo, pero una imagen vale más que mil palabras



Ahora vamos a ver que función tiene cada parte…

En el desensamblado, es donde trabajaremos la mayor parte del tiempo y es donde se nos muestran las instrucciones a ejecutar.

La barra de información, es donde se nos muestra la información acerca de cada instrucción (valores con los que trabaja, desde donde es llamada X subrutina, etc.)

El dump, es donde se nos muestra los valores hexa de los segmentos de la memoria

La pila o Stack, es eso la pila, es donde se guardan datos provisionalmente, y donde se pasan los parámetros a las API.

Los registros nos muestran el valor de los mismos, con posibilidad de modificarlos

Los botones de ventanas, nos muestran las distintas ventanas al presionarlos:

- los conceptos no explicados, serán vistos sobre la marcha en esta u otras clases.

El botón “L” nos muestra el Log, que si lo vemos apenas al cargar cualquier ejecutable, veremos que nos dice que cargo los plugins y el ejecutable de forma satisfactoria, esta ventana nos será de especial atención, pues será aquí donde se nos muestre los parámetros que se le pasan a las API cuando son llamadas, de esta forma sabremos todo lo que hace nuestra victima (esto después de haber hecho ciertas cosillas que veremos en las próximas clases)

El botón “E” nos muestra los módulos que se han cargado en memoria, es decir el ejecutables, las dll, los ocx y cualquier otro modulo que necesite para funcionar (pudiendo llegar a ser otro ejecutable, pues aunque no lo crean, los ejecutables también pueden exportar funciones, siendo así otros ejecutables necesitaran cargar este ejecutable a la memoria para poder utilizar sus funciones exportadas)

El Botón “M” nos muestra la memoria, es decir los módulos cargados, sus secciones, trozos de memoria reservados a ciertos módulos por la llamada a la función VirtualAlloc, etc.

El botón “T” nos muestra los threads o hilos creados, permitiéndonos “matarlos”, suspenderlos, etc.

El botón “W” nos muestra las ventanas creadas por nuestro ejecutable.

El botón “H” nos muestra los handles o manejadores.

El botón “C” nos muestra la “CPU” es decir la ventana que esta en la imagen donde esta la pila, el dump, etc.

El botón “/” nos muestra los parches o cambios hechos a mano, (o debería, pero yo nunca he visto nada en esa ventana)

El botón “K” nos muestra el “Call Stack” es decir las subrutinas en las que hemos entrado, o mejor dicho las subrutinas en las que estamos dentro.

El Botón “B” nos muestra los breackpoints que hemos puesto, y el estado de los mismos.

El botón “R” nos muestra las referencias, pero para que nos muestre algo debemos primero buscarlas, en esta ventana se nos muestran las referencias a la instrucción que especifiquemos, las referencias de texto dentro del ejecutable, las llamadas intermodulares, etc.

El botón “…” nos muestra el “run trace” es decir, aquí quedan logueadas las instrucciones ejecutadas cuando se tracea. Para esto debemos “abrir el run trace” esto se hace en el menú DEBUG > Open or Clear Run Trace.

Y el botón “S” al poner el Mouse encima de el nos dice “Show Source” que en español será algo así como “mostrar fuente”, pero nunca he sabido para que es esta ventana…

Los botones de acción, los explicare según el orden en el que aparezcan en la imagen (por ejemplo “el tercer botón hace esto, esto y lo otro) así que estad atentos con la imagen.

El primer botón, es el que tiene la forma de un botón de play (?) y lo que hace es correr el programa, y su tecla de acceso rápido es F9.

El segundo botón, es el botón de pause (??) y hace lo que es obvio pausa o detiene la ejecución del programa..

El tercer botón, de forma de flecha doblada a la derecha, ( algo parecido a esto ???) es el botón “step into” y lo que hace es que ejecuta instrucción por instrucción, entrando en las calls (se entenderá mejor en la practica) su tecla de acceso rápido es F7.

El cuarto botón, es una flecha que medio se asoma por arriba, (este si no se como expresarlo T_T)  es el botón “step over” y hace lo mismo que el anterior, ejecuta instrucción por instrucción, pero con la diferencia de que con este no entramos a los calls, si no que se ejecutan las subrutinas completas. Su tecla de acceso rápido el F8.

El quinto botón, es una flecha zigzagueante, es el botón trace into. Lo que hace es eso tracear (ejecutar instrucción por instrucción, pero continuamente, es decir sin parar, al “abrir el Run trace” quedara logueado ahí, cada instrucción ejecutada. Cuando nosotros ejecutaos paso a paso con F8 o F7 también se le llama tracear) entrando en las calls. Su combinación de acceso rápido es Ctrl. + F7.

El sexto botón, es el botón de “trace over” que hace lo mismo que el anterior, pero sin entrar en las calls. Su combinación de acceso rápido es Ctrl. +F8.

Y el séptimo botón, es el botón es el botón “till return” y lo que haces es ayudarnos a salir de una subrutina, es decir, que ejecuta las instrucciones hasta encontrar una instrucción RETN y ahí para. Su combinación de acceso rápido es Ctrl. + F9.


Conectado ANTRAX

  • *
  • Administrator
  • Mensajes: 5409
  • Actividad:
    20%
  • Reputación 33
  • ANTRAX
    • Ver Perfil
    • Underc0de
    • Email
  • Skype: underc0de.org
  • Twitter: @Underc0de
« Respuesta #2 en: Agosto 14, 2011, 03:27:48 pm »
Ingenieria Inversa de Troyanos Parte III

Desde ahora empezaremos a practicar… y como su profesor dejo pendiente una explicación de cierta rutina, en cierto stub, aprovechara la clase para explicarla y enseñarles a hacer su propia rutina…

Lo que veremos hoy es conocido como “manual packing” o empaquetamiento manual, en el que lo que haremos será encriptar nuestro ejecutable favorito nosotros mismos directamente con el olly. Pero antes de entrar en el tema explicare la rutina del stub…

Y como seria muy tonto explicar una rutina de un ejecutable sin que los alumnos puedan “sentir que trabajan con el ejecutable” se los adjunto… (Los que entraron al Chat “por obligación” se sentirán algo molestos, pero no se preocupen que casi nadie pasa por este subforo –y probablemente sean recompensados-)

Antes de empezar, veamos un par de APIs que se utilizan aquí y que no se han explicado:

GetModuleHandleA

Obtiene el Handle o manejador de un modulo, siempre y cuando este se encuentre cargado en la memoria.

Código: [Seleccionar]
Push @offset nombre del modulo
Call GetModuleHandleA
Returns
La función devolverá el handle del modulo si tiene éxito, de lo contrario devolverá cero

GetProcAddress

Obtiene la dirección de memoria de una función exportada del modulo especificado, siempre y cuando el modulo esté cargado.
Código: [Seleccionar]
Push @offset nombre o ordinal de la función
Push Handle del modulo.
[Code]
Returns
La función devolverá la dirección de memoria de la función. De fallar devolverá cero.

Bien, empecemos… apenas abrimos el ejecutable con el olly caemos en un Pushad

[url=http://www.subirimagenes.com/imagen-entripoint-1892470.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892470entri-point.png[/img][/url]

Podríamos explicar la rutina así, pero para hacer las cosas más sencillas utilizaremos el plugin del Olly AnalizeThis! (Si no lo tienen lo encontraran adjunto al final)

[url=http://www.subirimagenes.com/imagen-entripoint2-1892514.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892514entri-point2.png[/img][/url]

Vemos que ha cambiado un poco, pero gracias a ese cambio podemos ver una tabla de texto… si miramos bien podemos deducir que esta obteniendo las direcciones de memoria de las APIs que se mencionan en el  cuadro de texto, y guarda esas direcciones en otro lado, y si nos fijamos bien, esos saltos que están abajo apuntan a hacia donde se guardaron esa direcciones de memoria. Es decir que es como una mini-IAT.

[url=http://www.subirimagenes.com/imagen-exicaciondelaruti-1892542.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892542exicacion-de-la-ruti.png[/img][/url]

Ahí esta dividido por partes ese código.

En la parte 1 si, se darán cuenta que lo que hace es obtener el Handle o manejador del modulo “Kernel32.dll”

En la parte 2, mete el handle en el stack, pasa al siguiente elemento del cuadro de texto que se ve claramente en la imagen, y si son muy despistados (:ja:), es el que esta marcado con el numero 4. Compara si ese elemento es ultimo de la tabla (6179 son los bytes invertidos de “ya” en ASCII,) y si no lo es, entonces mete el handle de la dll (kernel32) en el stack y mete también, el elemento de la tabla al que apunta EBP para llamar a GetProcAddress, y hacer la mini-IAT.

Luego de eso, chequea el Bit de IsDebuggerPresent.

[url=http://www.subirimagenes.com/imagen-isdebuggerpresent-1892565.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892565isdebuggerpresent.png[/img][/url]

Y si esta a 1, nos lanzara afuera con un salto condicional que se ve que va directo a ExitProcess. Ahora ustedes dirán: como puede Chequear el Bit de IsDebuggerPresent si no llama a esa API? Pues la respuesta es sencilla, si en el olly oprimen Ctrl. + G y ponen ahí “IsDebuggerPresent” (con sus respectivas mayúsculas, o no funcionara) verán que en el lugar en el que caen, están las mismas primeras tres instrucciones que en la imagen que esta arriba de este párrafo (a excepción del pushad), pero si tenemos suerte, o mejor dicho, algún plugin que modifique ese bit (o lo modifican ustedes mismos), se hará una llamada a GetVersion, a la cual se le machacaran los datos que retorna con un popad y nos iremos en un vertiginoso salto hacia el inicio de la rutina de desencriptado (Al fin!!!)

[url=http://www.subirimagenes.com/imagen-iniciorutina-1892618.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892618inicio-rutina.png[/img][/url]

Ahí vemos otro pushad, y vemos también que empezara a desencriptar desde 401020, que al parecer desencriptara el exe por bloques de 179h bytes y que cada tres bytes los encripta de forma distinta. Al llegar ECX a cero saltara hacia la call que esta abajo del salto incondicional, hacia el inicio del bucle encriptador, veamos que hay dentro de ese call…

[url=http://www.subirimagenes.com/imagen-dentrodelcall-1892629.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892629dentro-del-call.png[/img][/url]

Pues, resumiré diciendo que es una rutina basura, en la cual se llama 100 veces a GetModuleHandleA pidiendo el handle o manejador de una dll Inexistente (lolazo.dll para ser mas exactos) y repite el bucle anidado ahí 300 veces (bucle que hace solo push EAX/pop EAX)

Al retornar vemos que se hace una comparación entre lo que hay en EAX y 4075BD, si no es igual nos manda a seguir desencriptando. Con esto sabemos que va a desencriptar hasta esa dirección, pero luego de eso hay un salto que va hacia abajo… veamos que nos depara la rutina por esos lares…

[url=http://www.subirimagenes.com/imagen-nosmodifica-1892644.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892644nos-modifica.png[/img][/url]

Este trozo de código, lo veremos en dos partes…

[url=http://www.subirimagenes.com/imagen-nosmodifica1-1892680.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892680nos-modifica1.png[/img][/url]

Ahí vemos que incrementa en uno el byte almacenado en 4090F5 y lo compara con 91, básicamente es para verificar si se ejecutara la primera parte del código a analizar o la segunda. Primero se ejecutara esta parte, en la que vemos que mueve ciertos valores hacia la rutina de desencriptado, así que lo mejor será ejecutarlo hasta el pushad.

Una vez llegado aquí nos vamos hacia donde empieza la rutina para ver que cambios hizo…

[url=http://www.subirimagenes.com/imagen-nosmodifica1-1892680.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892680nos-modifica1.png[/img][/url]

Ahí nos fijamos que ha cambiado las operaciones de la rutina, el antiguo ADD ahora es un XOR, el antiguo ROR ahora es un ROL, y el antiguo XOR ahora es un ADD…

Siguiendo donde estábamos, hay un PUSH 5000 y una llamada a “Sleep”, esta API lo que hace es parar la ejecución del programa el tiempo que se le diga, que en este caso son 5000h milisegundos (5000h = 20480d) y se nos manda al inicio de la rutina de desencriptado de nuevo.

Al llegar a desencriptar el byte de la dirección 4075BD vuelve a hacia la rutina que estamos analizando, y el salto decide que se ejecute la segunda parte…

[url=http://www.subirimagenes.com/imagen-nosmodifica2-1892688.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892688nos-modifica2.png[/img][/url]

Ahí vemos que cambia de nuevo la rutina, así que ejecutamos para ver que cambios se hacen…

[url=http://www.subirimagenes.com/imagen-modificado2-1892709.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892709modificado-2.png[/img][/url]

Ahora solo hay dos cambios, el primero es que el antiguo ADD se convirtió en un SUB y el salto que estaba al final de la rutina de desencriptado que nos mandaba al último código analizado, ahora nos manda a 4074ED. Que ciertamente es nuestro entripoint.

Listo… un poco larga la explicación, pero a más de uno le servirá para entender muchas cosas (o eso creo). Ahora pasemos a algo más interesante…

[b]Haciendo Manual-Packing[/b]

Yo explicare como hacerlo con el Stub del Bifrost (versión 1.21 desempacada), ustedes pueden hacerlo con el stub que prefieran… (los enlaces de las herramientas utilizadas están al final)

Para no estar trasteando con ningún editor de recursos lo que vamos a hacer es sacar el stub dándole a builder… no importa la configuración que le den, solo denle al botón build que esta abajo a la derecha y les saldrá un mensaje, lo dejaran en el aire, es decir no lo toquen… abran la carpeta donde tienen el bif y copian el ejecutable que se llama Server.exe y lo ponen en otra carpeta, este será el que utilizaremos… ya pueden darle aceptar al cartel y cerrar el cliente y si quieren borrar el Server.exe que esta al lado del cliente (sobra decir que si utilizas otro stub tendrán que sacarlo con el editor de recursos)

Lo que tenemos que hacer ahora es agregar una nueva sección, para esto yo utilizare el Stud_pe, ustedes utilicen el PE Editor que prefieran.

Los que me siguen con el Stud_pe: arrastramos el Stub que acabamos de sacar y nos vamos a la pestaña sections, hacemos click secundario > New Section, y el cuadro que aparece ponemos RAW SIZE: 200 y VIRTUAL SIZE: 1000 (esto es por que en RAW SIZE solo se pueden Poner Múltiplos de 200 y en VIRTUAL SIZE múltiplos de 1000) ahora seleccionamos “fill section with NULL bytes”

[url=http://www.subirimagenes.com/imagen-studpe-1892740.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892740studpe.png[/img][/url]

Ahora le damos al botón “ADD” y ya tenemos lista la nueva sección, así que abrimos el stub con el olly y nos vamos a donde pusimos la nueva sección.

Para obtener el valor en memoria de la nueva sección lo que tenemos que hacer es sumar el IMAGEBASE y el VIRTUAL OFFSET (normalmente el IMAGEBASE tiene un valor de 400000 en los ejecutables) y la dirección resultante es la que pondremos en el olly. Otra forma de llegar a la dirección virtual de la sección es con el plugin OllyAdvanced en el que se marcara la casilla “enable Advaced Ctrl. + G” y al hacer presionar esa combinación marcamos donde dice offset en el cuadro que nos aparece y ponemos la dirección que nos aparece en RAW OFFSET.

Una vez llegados acá, podremos empezar a escribir código para encriptar, pero deberemos tener en cuenta algo antes de encriptar y eso es la IAT y el IMPORT DESCRIPTOR.

La IAT la hallamos buscando algún call hacia cualquier API, cuando encontremos una miramos la dirección que esta entre corchetes ([]) y nos dirigimos hacia allá en el dump, y nos fijamos donde empieza y donde termina, en este caso empieza en 401000 y termina en 401020 (por eso empezaba a desencriptar desde ahí en la rutina explicada arriba)

Ahora le toca el turno al IMPORT DESCRIPTOR, pero hay dos caminos a recorrer, el primero es que cuando terminemos de encriptar, dumpeemos, con lo cual el plugin OllyDump pondrá un IMPORT DESCRIPTOR en una nueva sección y no habrá que preocuparse por el, pero esto nos aumentara el tamaño (el original es de 23Kb y lo dejara de 41Kb) y el otro camino es hacer “copy to executable” con lo cual mantendremos el tamaño, pero deberemos preocuparnos por el IMPORT DESCRIPTOR así que elijan su camino.

Camino del “Copy to executable”: deberemos hallar el IMPORT DESCRIPTOR y para ello nos ayudara el Stud_pe, en la primera pestaña hay una parte que dice “Import table” y tiene su dirección en RVA (Relative Virtual Address), RAW y el tamaño.

[url=http://www.subirimagenes.com/imagen-importdescriptor-1892800.html][img]http://s1.subirimagenes.com/imagen/previo/thump_1892800import-descriptor.png[/img][/url]

Esas direcciones son las del IMPORT DESCRIPTOR y como dije antes, para obtener su dirección virtual, se suma el RVA al IMAGEBASE. Y el resultado es el que se pone en el olly y para nuestra suerte, no hay nada después del IMPORT DESCRIPTOR, así que hasta ahí encriptaremos.

[b]Aquí los caminos se unen…[/b]

La encriptación que haremos será una súper básica, pero que como vimos en la rutina explicada al principio, puede mejorarse.

Movemos hacia algún registro de uso general la dirección desde la que empezamos a encriptar…
[code]
MOV EAX,401020
Encriptamos el byte de la dirección a la que apunta el registro de uso general con cualquier operación matemática o lógica…
Código: [Seleccionar]
MOV EAX,401020
SUB BYTE PTR DS:[EAX],51
Incrementamos el valor del registro en uno.
Código: [Seleccionar]
MOV EAX,401020
SUB BYTE PTR DS:[EAX],51
INC EAX
Comparamos si se ha llegado hasta donde queremos encriptar (4075f4 si vamos a hacer “copy to executable”, hasta el final de la sección si vamos a dumpear)
Código: [Seleccionar]
MOV EAX,401020
SUB BYTE PTR DS:[EAX],51
INC EAX
CMP EAX, XXXXX // ustedes ponen el valor dependiendo de lo que van a hacer
Hacemos que si no es igual, siga encriptando.
Código: [Seleccionar]
MOV EAX,401020
SUB BYTE PTR DS:[EAX],51
INC EAX
CMP EAX, XXXXX // ustedes ponen el valor dependiendo de lo que van a hacer
JNE @dirección donde esta el SUB
Y por ultimo hacemos que salte al entri point cuando termine de encriptar.
Código: [Seleccionar]
MOV EAX,401020
SUB BYTE PTR DS:[EAX],51
INC EAX
CMP EAX, XXXXX // ustedes ponen el valor dependiendo de lo que van a hacer
JNE @dirección donde esta el SUB
JMP 4074ED
Ahora para que de verdad quede encriptado, vamos a hacer lo siguiente, nos paramos donde esta la instrucción MOV hacemos click Secundario y hacemos click en “New origin here” o lo que es lo mismo Ctrl. + *, Ponemos un breakPoint en el salto hacia el entri point y corremos con F9 al parar deberemos tomar la decisión, dumpeamos o no…

Si dumpean, hagan click secundario > “dump debugged process” y solo pongan como entripoint la dirección del MOV (Sin el IMAGEBASE) y guarden con el nombre que prefieran y listo.

Si no dumpean, seleccionen el código recién escrito, hagan click secundario > Copy to executable > selection, para volver al CPU (a ventana donde estábamos trabajando) hacemos click en el botón “C” de la barra de botones, luego vallan a 401000, hagan click normal en la primera instrucción para que se marque y vallan hacia el inicio del IMPORT DESCRIPTOR (4075f4) y presionando shift hacen click ahí donde aparecieron. Ahora de nuevo click secundario > Copy to executable > selection y ahí damos de nuevo click secundario > save file, Guardemos y con el Stud_pe cambiamos el entri point la dirección del MOV (sin el IMAGEBASE) y listo.

ahora despues de guardado lo abrimos de nuevo para que desencripte, es decir lo que tenemos que hacer es cambiar el sub por un add y hacer copy to executable, fin

plugin analizethis
plugin ollyDump
Stud_pe
stub de la explicacion


Conectado ANTRAX

  • *
  • Administrator
  • Mensajes: 5409
  • Actividad:
    20%
  • Reputación 33
  • ANTRAX
    • Ver Perfil
    • Underc0de
    • Email
  • Skype: underc0de.org
  • Twitter: @Underc0de
« Respuesta #3 en: Agosto 14, 2011, 03:31:52 pm »
Ingenieria Inversa de Troyanos Parte IV

Aprender como funcionan los sockets estudiándolos desde el debugger para así aplicarlos luego a cualquier programa, ya sea injertando otros ejecutables o programando desde cero. (Al saber solo como son llamadas las apis en ASM y averiguando que parámetros y que tipos de datos deben tener estos parámetros podrás aplicar estos conocimientos en cualquier otro lenguaje de programación)

Quise hacerlo con algún troyano que no se haya tocado mucho en el foro y por eso decidí hacerlo con el “bifrost v2.0” de “la vozr”  pero entonces me encontré con un problema:

Un sistema de empaquetado muy sencillo, pero que no lograba quitar. Por esta razón es que esta cuarta clase ira dirigida hacia el desempacado del stub de este troyano.

Pero primero algunas cosas que se dejaron en el aire en la clase pasada:

BreakPoint o punto de ruptura: es un punto que especificamos con el debugger para que el programa pare ahí, así podemos analizar mejor una rutina y los estados de los registros y la pila en dicha rutina.

Import Descriptor: es la parte del ejecutable donde se indica en formato de texto las apis a utilizar y las Dll en las que se encuentran dichas apis (el verdadero nombre de esto es Image Import Descriptor, También abreviado IID)

ahora a la acción…

desmarcamos la opción “autoPack” aunque igual nos dará el Server comprimido con FSG v2.0 (eso es lo que dice el RDGPacker detector) no nos meterá la configuración. (pero igual se la pueden borrar con el Stud_pe, es la “Extradita”)

Este packer es súper fácil de quitar. No necesitamos ningún truco. Solo bajemos desde el EP (Entri Point) en busca de un salto raro, y cuando lo encontremos ponemos un Bp (Break Point) ahí.



Este es el EP, fíjense que su dirección es: 00400154



Y ahí el salto que buscamos, esta en 004001D1 (es decir que no hay que bajar mucho)

Ponemos el BP ahí y damos F9. al instante se detiene ahí, damos F7 o F8 y caemos en lo que parece ser el OEP (Original Entri Point)



Dumpeamos y al archivo dumpeado le pasamos el RDG Packer Detector, y nos dice que es un FCrypt 2 (creían que iba a ser tan fácil?)

OJO, NO CIERREN ESE OLLY, TRABAJEN CON EL DESEMPACADO EN MEMORIA, NO CON EL DUMPEADO

De una les digo que esas call acompañadas de otras call que van a ExitProcess son rutinas antidebuging, y una es para comprobar si no le hemos quitado el FSG. Así que hagan que salten ya sea cambiando el Flag Z o convirtiendolos en jmp’s (ojo que son solo los que están acompañados de la call a ExitProcess).

Traceando entre ese código solo veremos rutinas basura que están ahí para retrasarnos al tracear (dio resultado conmigo) y llamadas a GetProcAddress y GetModuleHandleA para conseguir otras apis, de las cuales nos interesan CreateProcessA, WriteProcessMemory.

Así que pasadas esas evidentes rutinas antidebug, ponemos un BP en CreateProcessA y corremos con F9. Paramos en dicha api, vemos en la pila que el nuevo proceso es el mismo Server y además lo crea suspendido. Ejecutamos hasta el RETN y caemos acá.



Esta reescribiendo toda la seccion de código… entonces solo queda esperar a que arranque ese proceso y dumpearlo para terminar el trabajo.

Seguimos traceando, y llegamos a una llamada a VirtualProtectEx y da todos los permisos a la sección de código del proceso “nuevo”.

Pasamos por otra Api WriteProcessMemory. Y al final llegamos a SetThreadContext

Pausa

Esta api es igual a GetThreadContext pero hace todo lo contrario, da un nuevo context al Thread indicado. La diferencia en los atributos es el atributo Buffer que es donde esta el Context a darle al thread.

Nos quedaría así:

MASM

Invoke SetThreadContext, hThread, lpContext.

Forma tradicional.

Push lpContext
Push hThread
Call SetThreadContext

Seguimos

Eso quiere decir que esta a punto de arrancar el proceso, pues al darle un nuevo context al Thread, le esta diciendo desde donde va a empezar (el OEP) para poder entender la estructura que pasara a ser el context del thread utilizaremos el plugin StollyStruct que lo que hace es leer la zona que le indicamos como la estructura que le indiquemos (en este caso el argumento Buffer vale 0012FD1C y le indicaremos como estructura “CONTEXT”)

Para utilizarlo, lo que debemos hacer es copiar StollyStruct.dll en el directorio de los plugin y copiar StollyStructs.ini en el directorio del olly.

Para poder utilizarlo, como lo acabamos de copiar debemos cerrar el olly y abrirlo de nuevo. No hay problema, cuando inicien el Stub escriban en la línea de comandos “BP SetThreadContext” y corren con F9 (caeran dentro del call pero lo que nos interesa de ahí son los argumentos y aun están visibles)

Vamos a la dirección del argumento en el dump (Clic secundario en el argumento pContext > Follow in dump) y en el primer byte que aparece damos Clic secundario > Struct. Y ponemos context.



Damos clic en show y vemos que donde dice regEip vale cero y dos regXXX mas arriba (en regEax) hay un valor que es “00408E7A” debe ser algún error en como esta definida la estructura pero el valor de Eip es el que ahí aparece como Eax. Y lo que debemos hacer es agarrar PUPE (o cualquier otro Parcheador de Memoria) y ver que hay en esa dirección, anotarlo y poner EBFE para hacer un bucle infinito y así no se nos escape el Thread. ejecutamos la llamada a SetThreadContext y la próxima es hacia ResumeThread.

Pausa

Esta API lo que hace es “Resumir” o “Reactivar” un thread suspendido. Solo necesita el manejador del Thread como argumento y nos quedaría así:

MASM

ResumeThread, hThread

Forma tradicional:

Push hThread
Call ResumeThread

Seguimos

Con pupe lo Parcheamos en esa zona y pasamos la API ResumeThread. Ahora ya podemos cerrar este proceso y pasar al otro.

En el olly le damos File > Attach. Seleccionamos el proceso (el que aparece en rojo es el que estamos debugeando, por lo tanto el otro es el que nos interesa)

Y acá es donde yo tenia el problema… puesto que el administrador de tareas y el PUPE eran los únicos programas que me mostraban el proceso, en las otras herramientas no aparecía, para verlo debemos utilizar en el olly el plugin ollyAdvanced y marcar la siguiente opción.



Ahora lo attacheamos, y caemos en un retn debajo de una instrucción int3. Hacemos clic en el botón “T” de la Barra de botones para ver los threads, vemos que hay dos y el primero es el nuestro (el segundo es creado por el debugger con CreateRemoteThread en la API DebugBreak -que lo único que tiene es un Int3 y el Retn- para poder detener el proceso al attachearlo) hacemos doble clic en el y caemos en el salto que salta a el mismo (es el Bucle Infinito que pusimos con el PUPE recuerdan, vean que el OpCode el EBFE) y lo remplazamos por los bytes que había antes de parchear (558B), vemos que no tenga nada raro (buscamos las llamadas –search names in current module- a ExitProcess y otros que pudieran fastidiar) y dumpeamos. (Con el Pupe por que por lo menos a mi me da un Error el Plugin OllyAdvanced al tratar de dumpear y me cierra el olly)

Para dumpear con el PUPE, debemos hacer clic secundario (con el Proceso que queremos dumpear seleccionado) > caja de herramientas. Ahí seleccionamos lo que queremos dumpear dando click secundario en Server.exe (o como lo tengan ustedes) y seleccionan alinear, ahora dumpean y le ponen el nombre que prefieran.



Con eso lo tendran desempacado :D

Lo ejecutamos y…

No funciona. Por que?

Sencillo, no le hemos metido la configuración!!!!

Copiamos este Server en la carpeta del donde tenemos el bif. Abrimos el bif, damos a Build y (con la opción Autopack desmarcada) creamos un Server (debes tener la configuración que vas a usar, pero si te equivocas ya sabes, stud_pe)

Antes de darle OK al cartel que nos sale, borramos el Server creado, y al nuestro le ponemos como nombre “Server.exe” y ahora aceptamos. Ya lo tenemos funcional.

Esto es todo, para la próxima clase lo de los Sockets.

Saludos!

PD: se que me tarde mucho, este tuto lo tenia casi listo desde hace mas o menos mes y medio, pero por causa del Conficker me daban error los dumpeados y yo creía que era que el tuto estaba mal. Ahora se que no :ja:

editado:

se me olvido poner las herramientas xD

PUPE
Plugin StollyStruct

editado 2:

el bif usado aca lo pueden descargar de aca
o de mi google site si por algun motivo los links del post no sirvan.



Desconectado linkgl

  • *
  • Underc0der
  • Mensajes: 45
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
« Respuesta #4 en: Agosto 14, 2011, 04:28:53 pm »
Jeje estan buenos los tutos que hizo bloodday en su tiempo x), en el 3 si necesitas obtener la dirección virtual de alguna API y la librería o dll no está cargada en memoria puedes usar LoadLibrary en vez de GetModuleHandle ya que si no esta última no va a funcionar :P, en general muy bien el manual  :)

Desconectado REC

  • *
  • Underc0der
  • Mensajes: 122
  • Actividad:
    0%
  • Reputación 0
  • o.O!
    • Ver Perfil
    • Email
« Respuesta #5 en: Abril 02, 2012, 09:20:24 am »
Muy buen tema :) !!  saludos...! 

Desconectado dracko.rx

  • *
  • Underc0der
  • Mensajes: 247
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
    • http://rax0rnet.blogspot.com/
    • Email
« Respuesta #6 en: Agosto 23, 2012, 01:34:40 pm »
Muy buen aporte bro, muy interesante.

Saludos
Venta de diseños - Contactar por MP

http://rax0rnet.blogspot.com/

 

¿Te gustó el post? COMPARTILO!



Detectando Y Eliminando Keyloggers (Aplicable a Troyanos)

Iniciado por Jhonjhon_123

Respuestas: 4
Vistas: 3735
Último mensaje Febrero 17, 2010, 10:42:24 pm
por OSX
¿Qué son y cómo funcionan los troyanos bancarios?

Iniciado por DeuSer

Respuestas: 5
Vistas: 2583
Último mensaje Marzo 28, 2010, 01:31:04 pm
por RevoluHack
Desinfeccion de Troyanos by ANTRAX

Iniciado por ANTRAX

Respuestas: 0
Vistas: 1736
Último mensaje Febrero 23, 2010, 08:17:42 am
por ANTRAX
Lista de nombres de Troyanos..

Iniciado por Cygog

Respuestas: 8
Vistas: 6136
Último mensaje Abril 05, 2012, 04:08:39 pm
por REC
Guia Completa de Troyanos

Iniciado por ANTRAX

Respuestas: 0
Vistas: 1778
Último mensaje Febrero 23, 2010, 08:20:21 am
por ANTRAX